This is dirsend.c in view mode; [Download] [Up]
/*
* Copyright (c) 1989, 1990, 1991 by the University of Washington
*
* For copying and distribution information, please see the file
* <copyright.h>.
*
* xarchie v2.0 - (gf) - Sync with archie-1.4.1
* v1.2.3 - 11/04/91 (bcn) - removed host comparison and replaced with check
for connection id (undoes effect of v1.2.2.).
* v1.2.2 - 11/02/91 (gf) - removed extra inet_ntoa() calls and stuff for
multi-interface nets (lmjm@doc.imperial.ac.uk)
* v1.2.1 - 10/20/91 (gf) - asynch implementation
* v1.2.0 - 09/17/91 (bpk) - added BULL & USG stuff, thanks to Jim Sillas
* v1.1.2 - 08/30/91 (bpk) - added VMS support
* v1.1.1 - 08/29/91 (bcn) - changed backoff handling
* v1.1.0 - 08/13/91 (gf) - added XArchie status calls
*
* gf: 20 Oct 1991:
* Broken into pieces so that under X dirsend() doesn't block in select()
* but rather uses Xt calls to allow continued event processing. If
* XARCHIE is not defined, can still be used since processEvent() will
* use select() in this case.
*
* bpk: For archie client v1.3.2:
* If you're going to hack on this, I'd suggest using unifdef with -UCUTCP
* and possibly -UVMS, for your working copy. When you've got your changes
* done, come back and add them into this main file. It's getting pretty
* nasty down there.
*/
#include <copyright.h>
#include <stdio.h>
#include <errno.h>
/* Status stuff */
#include "query.h"
static StatusMsgPtr msg;
/*
* Complaints or suggestions regarding the portability or lack thereof
* of these includes and defines should be directed to Brendan Kehoe,
* brendan@cs.widener.edu.
*/
#ifdef VMS
# ifdef WOLLONGONG
# include "twg$tcp:[netdist.include]netdb.h"
# else /* not Wollongong */
# ifdef UCX
# include <netdb.h>
# else /* Multinet */
# include "multinet_root:[multinet.include]netdb.h"
# endif
# endif
# include <vms.h>
#else /* not VMS */
# include <sys/types.h> /* this may/will define FD_SET etc */
# ifdef u3b2
# include <sys/inet.h> /* THIS does FD_SET etc on AT&T 3b2s. */
# endif /* u3b2 */
# ifdef PCNFS
# include <tklib.h>
# include <tk_errno.h>
# include <sys/nfs_time.h>
# endif
# include <pmachine.h>
# if defined(NEED_TIME_H) && !defined(AUX)
# include <time.h>
# else
# include <sys/time.h>
# endif
# ifdef WANT_BOTH_TIME
# include <sys/time.h>
# endif
# ifdef NEED_STRING_H
# include <string.h>
# else
# include <strings.h>
# endif
# ifdef CUTCP
# include <msdos/cutcp.h>
# include <msdos/netevent.h>
# include <msdos/hostform.h>
# else /* not CUTCP */
# include <netdb.h>
# include <sys/socket.h>
# endif
# ifdef NEED_SELECT_H
# include <sys/select.h>
# endif /* NEED_SELECT_H */
# ifndef IN_H
# include <netinet/in.h>
# define IN_H
# endif
# if !defined(hpux) && !defined(PCNFS)
# include <arpa/inet.h>
# endif
#endif /* !VMS */
/* Interactive UNIX keeps some of the socket definitions in funny places. */
#ifdef ISC
# include <net/errno.h>
#endif /* ISC */
/* PC-NFS Toolkit 4.0 keeps important forward definitions here. */
#ifdef PCNFS
# include <in_addr.h>
#endif
#include <pfs.h>
#include <pprot.h>
#include <pcompat.h>
#include <perrno.h>
/* gf: Removed SUN_GNU_FIX stuff since inet_ntoa.c is now included. */
static int notprived = 0;
#ifndef MSDOS
extern int errno;
#endif
extern int perrno;
#ifdef NeXT
extern int pfs_debug;
/* The fd for the read end of the pipe that NeXTArchie uses to signal
that the user has aborted the select() system call. It is set by the
QueryThreadProc() by calling SetThreadAbortFD(). */
extern int threadAbortFD;
#endif
extern int pfs_disable_flag;
char *nlsindex();
#define max(X, Y) ((X) > (Y) ? (X) : (Y))
static int dir_udp_port = 0; /* Remote UDP port number */
#ifdef CUTCP
# define NS_TIMEOUT 15
#endif
static unsigned short next_conn_id = 0;
/* Always needed externally */
int client_dirsrv_timeout = CLIENT_DIRSRV_TIMEOUT;
int client_dirsrv_retry = CLIENT_DIRSRV_RETRY;
int rdgram_priority = 0; /* gf: was in rdgram.h */
/* These were parameters to dirsend() */
static PTEXT pkt;
static char *hostname;
static struct sockaddr_in *hostaddr;
/* These were locals in dirsend(). Note that the initializations here
* are really meaningless since we have to redo them for each call to
* dirsend() since they were formerly automatically initialized.
*/
static PTEXT first = NULL; /* First returned packet */
static PTEXT next; /* The one we are waiting for */
static PTEXT vtmp; /* For reorganizing linked list */
static PTEXT comp_thru; /* We have all packets though */
static int lp = -1; /* Opened UDP port */
static int maxFD; /* The maximum fd in readfds */
static int hdr_len; /* Header Length */
static int nd_pkts; /* Number of packets we want */
static int no_pkts; /* Number of packets we have */
static int pkt_cid; /* Packet connection identifier */
static unsigned short this_conn_id; /* Connection ID we are using */
static unsigned short recvd_thru; /* Received through */
static short priority; /* Priority for request */
static short one = 0; /* Pointer to value 1 */
static short zero = 0; /* Pointer to value 0 */
static char *seqtxt; /* Pointer to text w/ sequence # */
static struct sockaddr_in us; /* Our address */
static struct sockaddr_in to; /* Address to send query */
static struct sockaddr_in from; /* Reply received from */
static int from_sz; /* Size of from structure */
static struct hostent *host; /* Host info from gethostbyname */
static long newhostaddr; /* New host address from *host */
static int req_udp_port=0; /* Requested port (optional) */
static char *openparen; /* Delimits port in name */
static char hostnoport[500];/* Host name without port */
static int ns; /* Number of bytes actually sent */
static int nr; /* Number of bytes received */
static SELECTARG readfds; /* Used for select */
static int tmp;
static char *ctlptr; /* Pointer to control field */
static short stmp; /* Temp short for conversions */
static int backoff; /* Server requested backoff */
static unsigned char rdflag11; /* First byte of flags (bit vect)*/
static unsigned char rdflag12; /* Second byte of flags (int) */
static int scpflag = 0; /* Set if any sequencd cont pkts */
static int ackpend = 0; /* Acknowledgement pending */
static int gaps = 0; /* Gaps present in recvd pkts */
static struct timeval timeout; /* Time to wait for response */
static struct timeval ackwait; /* Time to wait before acking */
static struct timeval gapwait; /* Time to wait b4 filling gaps */
static struct timeval *selwait; /* Time to wait for select */
static int retries; /* was = client_dirsrv_retry */
char to_hostname[512]; /* lmjm: saves inet_ntoa() str */
/* These are added so dirsend() "blocks" properly */
static PTEXT dirsendReturn;
static int dirsendDone;
/* And here are the values for dirsendDone */
#define DSRET_DONE 1
#define DSRET_SEND_ERROR -1
#define DSRET_RECV_ERROR -2
#define DSRET_SELECT_ERROR -3
#define DSRET_TIMEOUT -4
#define DSRET_ABORTED -5
/* New procedures to break up dirsend() */
static int initDirsend();
static void retryDirsend(), keepWaitingDirsend();
static void timeoutProc();
static void readProc();
/* Wrappers around X calls to allow non-X usage */
static void addInputSource(), removeInputSource();
static void addTimeOut(), removeTimeOut();
static void processEvent();
#ifndef NeXT
/* External status procedures */
extern void status0(),status1(),status2();
#endif
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
static int packetCounter;
#endif
/* Extra stuff for the asynchronous X version of dirsend() */
#ifdef XARCHIE
#include <X11/Intrinsic.h>
extern XtAppContext appContext;
#else /*!XARCHIE*/
#include "xtypes.h"
#endif
static XtInputId inputId;
static XtIntervalId timerId = (XtIntervalId)0;
/*
* dirsend - send packet and receive response
*
* DIRSEND takes a pointer to a structure of type PTEXT, a hostname,
* and a pointer to a host address. It then sends the supplied
* packet off to the directory server on the specified host. If
* hostaddr points to a valid address, that address is used. Otherwise,
* the hostname is looked up to obtain the address. If hostaddr is a
* non-null pointer to a 0 address, then the address will be replaced
* with that found in the hostname lookup.
*
* DIRSEND will wait for a response and retry an appropriate
* number of times as defined by timeout and retries (both static
* variables). It will collect however many packets form the reply, and
* return them in a structure (or structures) of type PTEXT.
*
* DIRSEND will free the packet that it is presented as an argument.
* The packet is freed even if dirsend fails.
*/
PTEXT
dirsend(pkt_p,hostname_p,hostaddr_p, msgArg)
PTEXT pkt_p;
char *hostname_p;
struct sockaddr_in *hostaddr_p;
StatusMsgPtr msgArg;
{
/* copy parameters to globals since other routines use them */
pkt = pkt_p;
hostname = hostname_p;
hostaddr = hostaddr_p;
/* Do the initializations of formerly auto variables */
first = NULL;
lp = -1;
one = 0;
zero = 0;
req_udp_port=0;
scpflag = 0;
ackpend = 0;
gaps = 0;
retries = client_dirsrv_retry;
msg = msgArg;
if (initDirsend() < 0)
return(NULL);
addInputSource();
/* set the first timeout */
retryDirsend();
dirsendReturn = NULL;
dirsendDone = 0;
/* Until one of the callbacks says to return, keep processing events */
while (!dirsendDone)
processEvent();
/* Clean up event generators */
removeTimeOut();
removeInputSource();
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
/* Set status if needed (has to be outside of loop or X will crash) */
switch (dirsendDone) {
case DSRET_SEND_ERROR: status0("Send error"); break;
case DSRET_RECV_ERROR: status0("Recv error"); break;
case DSRET_TIMEOUT:
status1("Connection to %s timed out",to_hostname);
break;
case DSRET_ABORTED: status0("Aborted"); break;
}
#endif
/* Return whatever we're supposed to */
return(dirsendReturn);
}
/* - - - - - - - - */
/* This function does all the initialization that used to be done at the
* start of dirsend(), including opening the socket descriptor "lp". It
* returns the descriptor if successful, otherwise -1 to indicate that
* dirsend() should return NULL immediately.
*/
static int
initDirsend()
{
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
status0("Initializing");
#endif
if(one == 0) one = htons((short) 1);
priority = htons(rdgram_priority);
timeout.tv_sec = client_dirsrv_timeout;
timeout.tv_usec = 0;
ackwait.tv_sec = 0;
ackwait.tv_usec = 500000;
gapwait.tv_sec = (client_dirsrv_timeout < 5 ? client_dirsrv_timeout : 5);
gapwait.tv_usec = 0;
comp_thru = NULL;
perrno = 0;
nd_pkts = 0;
no_pkts = 0;
pkt_cid = 0;
/* Find first connection ID */
if(next_conn_id == 0) {
srand(getpid()+time(0)); /* XXX: arg ok, but not right type. */
next_conn_id = rand();
}
/* If necessary, find out what udp port to send to */
if (dir_udp_port == 0) {
register struct servent *sp;
tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
#ifdef USE_ASSIGNED_PORT
/* UCX needs 0 & -1 */
sp = getservbyname("prospero","udp");
if (sp == (struct servent *)0 || sp == (struct servent *)-1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: udp/prospero unknown service - using %d\n",
PROSPERO_PORT);
#endif
dir_udp_port = htons((u_short) PROSPERO_PORT);
}
#else
/* UCX needs 0 & -1 */
sp = getservbyname("dirsrv","udp");
if (sp == (struct servent *)0 || sp == (struct servent *)-1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: udp/dirsrv unknown service - using %d\n",
DIRSRV_PORT);
#endif
dir_udp_port = htons((u_short) DIRSRV_PORT);
}
#endif
else dir_udp_port = sp->s_port;
pfs_enable = tmp;
#ifdef DEBUG
if (pfs_debug > 3)
fprintf(stderr,"dir_udp_port is %d\n", ntohs(dir_udp_port));
#endif
}
/* If we were given the host address, then use it. Otherwise */
/* lookup the hostname. If we were passed a host address of */
/* 0, we must lookup the host name, then replace the old value */
if(!hostaddr || (hostaddr->sin_addr.s_addr == 0)) {
/* I we have a null host name, return an error */
if((hostname == NULL) || (*hostname == '\0')) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: Null hostname specified\n");
#endif
perrno = DIRSEND_BAD_HOSTNAME;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
/* If a port is included, save it away */
if(openparen = index(hostname,'(')) {
sscanf(openparen+1,"%d",&req_udp_port);
strncpy(hostnoport,hostname,400);
if((openparen - hostname) < 400) {
*(hostnoport + (openparen - hostname)) = '\0';
hostname = hostnoport;
}
}
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
status1("Getting address for host \"%s\"",hostname);
#endif
tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
if((host = gethostbyname(hostname)) == NULL) {
pfs_enable = tmp;
/* Check if a numeric address */
newhostaddr = inet_addr(hostname);
if(newhostaddr == -1) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr, "dirsrv: Can't resolve host %s\n",hostname);
#endif
perrno = DIRSEND_BAD_HOSTNAME;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
bzero((char *)&to, S_AD_SZ);
to.sin_family = AF_INET;
bcopy((char *) &newhostaddr, (char *)&to.sin_addr, 4);
if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
}
else {
pfs_enable = tmp;
bzero((char *)&to, S_AD_SZ);
to.sin_family = host->h_addrtype;
#ifdef CUTCP
bcopy((char *) &host->h_addr, (char *)&to.sin_addr, host->h_length);
#else
bcopy(host->h_addr, (char *)&to.sin_addr, host->h_length);
#endif
if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
}
}
else bcopy(hostaddr,&to, S_AD_SZ);
/* lmjm: Save away the hostname */
strncpy(to_hostname,inet_ntoa(to.sin_addr),sizeof(to_hostname)-1);
if(req_udp_port) to.sin_port = htons(req_udp_port);
else to.sin_port = dir_udp_port;
/* If a port was specified in hostaddr, use it, otherwise fill it in */
if(hostaddr) {
if(hostaddr->sin_port) to.sin_port = hostaddr->sin_port;
else hostaddr->sin_port = to.sin_port;
}
#ifndef CUTCP
/* Must open a new port each time. we do not want to see old */
/* responses to messages we are done with */
if ((lp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
#ifdef DEBUG
if (pfs_debug)
fprintf(stderr,"dirsrv: Can't open socket\n");
#endif
perrno = DIRSEND_UDP_CANT;
ptlfree(pkt);
/* return(NULL); */
return(-1);
}
maxFD = lp;
#endif /* not CUTCP */
/* Try to bind it to a privileged port - loop through candidate */
/* ports trying to bind. If failed, that's OK, we will let the */
/* system assign a non-privileged port later */
#ifndef CUTCP
if(!notprived) {
for(tmp = PROS_FIRST_PRIVP; tmp < PROS_FIRST_PRIVP+PROS_NUM_PRIVP;
tmp++) {
#endif
bzero((char *)&us, sizeof(us));
us.sin_family = AF_INET;
#ifndef CUTCP
us.sin_port = htons((u_short) tmp);
if (bind(lp, (struct sockaddr *)&us, sizeof(us))) {
if(errno != EADDRINUSE) {
notprived++;
break;
}
}
else break;
}
}
#else
us.sin_port = htons(PROS_FIRST_PRIVP);
netulisten(PROS_FIRST_PRIVP);
#endif
#ifndef USE_V3_PROT
/* Add header */
if(rdgram_priority) {
pkt->start -= 15;
pkt->length += 15;
*(pkt->start) = (char) 15;
bzero(pkt->start+9,4);
*(pkt->start+11) = 0x02;
bcopy(&priority,pkt->start+13,2);
}
else {
pkt->start -= 9;
pkt->length += 9;
*(pkt->start) = (char) 9;
}
this_conn_id = htons(next_conn_id++);
if(next_conn_id == 0) next_conn_id++;
bcopy(&this_conn_id,pkt->start+1,2);
bcopy(&one,pkt->start+3,2);
bcopy(&one,pkt->start+5,2);
bzero(pkt->start+7,2);
#endif
#ifdef DEBUG
if (pfs_debug > 2) {
#ifndef USE_V3_PROT
if (to.sin_family == AF_INET) {
if(req_udp_port)
fprintf(stderr,"Sending message to %s+%d(%d)...",
to_hostname, req_udp_port, ntohs(this_conn_id));
else fprintf(stderr,"Sending message to %s(%d)...",
to_hostname, ntohs(this_conn_id));
}
#else
if (to.sin_family == AF_INET)
fprintf(stderr,"Sending message to %s...", to_hostname);
#endif /* USE_V3_PROT */
else
fprintf(stderr,"Sending message...");
(void) fflush(stderr);
}
#endif /* DEBUG */
first = ptalloc();
next = first;
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
status2("Connecting to %s (%s)", to_hostname, hostname);
packetCounter = 0;
#endif
#ifndef CUTCP
return(lp);
#else
return(1);
#endif /* CUTCP */
}
/* - - - - - - - - */
/*
* This used to be a label to goto to retry the last packet. Now we resend
* the packet and call keepWaitingDirsend() to wait for a reply. (We
* call keepWaitingDirsend() because formerly the code dropped through
* the keep_waiting label.)
*/
static void
retryDirsend()
{
#ifdef CUTCP
int lretry = 3;
#endif
gaps = ackpend = 0;
#ifndef CUTCP
ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
#else
while(--lretry) {
ns = netusend(&to.sin_addr,ntohs(to.sin_port),ntohs(us.sin_port),
(char *) pkt->start, pkt->length);
if(!ns)
break;
Stask();
Stask();
Stask();
}
#endif /* CUTCP */
#ifndef CUTCP
if(ns != pkt->length) {
#else
if(ns != 0) {
#endif
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
perror("");
}
#endif
close(lp);
perrno = DIRSEND_NOT_ALL_SENT;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_SEND_ERROR;
}
#ifdef DEBUG
if (pfs_debug > 2) fprintf(stderr,"Sent.\n");
#endif
keepWaitingDirsend();
}
/* - - - - - - - - */
/*
* This used to be a label to goto to set the appropriate timeout value
* and blocked in select(). Now we set selwait and the SELECTARGs to the
* appropriate values, and in X register a new timeout, then return to
* allow event processing.
*/
static void
keepWaitingDirsend()
{
/* We come back to this point (by a goto) if the packet */
/* received is only part of the response, or if the */
/* response came from the wrong host */
#ifdef DEBUG
if (pfs_debug > 2) fprintf(stderr,"Waiting for reply...");
#endif
#ifndef CUTCP
FD_ZERO(&readfds);
FD_SET(lp, &readfds);
# ifdef NeXT
# ifndef MAX
# define MAX(a, b) ((a) >= (b) ? (a) : (b))
# endif
/* Add the threadAbortFD to the set of fds we want to select() when
the read status changes */
FD_SET(threadAbortFD, &readfds);
maxFD = MAX(lp, threadAbortFD);
# endif
#endif
if(ackpend) selwait = &ackwait;
else if(gaps) selwait = &gapwait;
else selwait = &timeout;
addTimeOut();
}
/* - - - - - - - - */
/*
* This routine is called when a timeout occurs. It includes the code that
* was formerly used when select() returned 0 (indicating a timeout).
*/
/*ARGSUSED*/
static void
timeoutProc(client_data,id)
XtPointer client_data;
XtIntervalId *id;
{
if (gaps || ackpend) { /* Send acknowledgment */
/* Acks are piggybacked on retries - If we have received */
/* an ack from the server, then the packet sent is only */
/* an ack and the rest of the message will be empty */
#ifdef DEBUG
if (pfs_debug > 2) {
fprintf(stderr,"Acknowledging (%s).\n",
(ackpend ? "requested" : "gaps"));
}
#endif
retryDirsend();
return;
}
if (retries-- > 0) {
timeout.tv_sec = CLIENT_DIRSRV_BACKOFF(timeout.tv_sec);
#ifdef DEBUG
if (pfs_debug > 2) {
fprintf(stderr,"Timed out. Setting timeout to %d seconds.\n",
timeout.tv_sec);
}
#endif
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
status1("Timed out -- retrying (%d seconds)",timeout.tv_sec);
#endif
retryDirsend();
return;
}
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr, "select failed(timeoutProc): readfds=%x ",
readfds);
perror("");
}
#endif
#ifndef CUTCP
close(lp);
#endif
perrno = DIRSEND_SELECT_FAILED;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_TIMEOUT;
}
/* - - - - - - - - */
/*
* This function is called whenever there's something to read on the
* connection. It includes the code that was run when select() returned
* greater than 0 (indicating read ready).
*/
/*ARGSUSED*/
static void
readProc(client_data,source,id)
XtPointer client_data;
int *source;
XtInputId *id;
{
#ifdef CUTCP
int lretry = 3;
#endif
/* We got something to read, so clear the timer */
removeTimeOut();
from_sz = sizeof(from);
next->start = next->dat;
#ifndef CUTCP
if ((nr = recvfrom(lp, next->start, sizeof(next->dat), 0, (struct sockaddr *)&from, &from_sz)) < 0) {
#else
nr = neturead(next->start);
if (nr < 1) {
#endif
#ifdef DEBUG
if (pfs_debug) perror("recvfrom");
#endif
#ifndef CUTCP
close(lp);
#endif
perrno = DIRSEND_BAD_RECV;
ptlfree(first);
ptlfree(pkt);
/* return(NULL) */
dirsendReturn = NULL;
dirsendDone = DSRET_RECV_ERROR;
return;
}
next->length = nr;
next->start[next->length] = 0;
#ifdef DEBUG
if (pfs_debug > 2)
fprintf(stderr,"Received packet from %s\n",inet_ntoa(from.sin_addr));
#endif
#if defined(XARCHIE) || defined(VARCHIE) || defined(NeXT)
if (packetCounter == 0)
{
status2("Connected to %s (%s)", to_hostname, hostname);
}
else
{
status1("Receiving...%d",packetCounter);
}
packetCounter += 1;
#endif
/* For the current format, if the first byte is less than */
/* 20, then the first two bits are a version number and the next six */
/* are the header length (including the first byte). */
if((hdr_len = (unsigned char) *(next->start)) < 20) {
ctlptr = next->start + 1;
next->seq = 0;
if(hdr_len >= 3) { /* Connection ID */
bcopy(ctlptr,&stmp,2);
if(stmp) pkt_cid = ntohs(stmp);
ctlptr += 2;
}
if(pkt_cid && this_conn_id && (pkt_cid != ntohs(this_conn_id))) {
/* The packet is not for us */
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
if(hdr_len >= 5) { /* Packet number */
bcopy(ctlptr,&stmp,2);
next->seq = ntohs(stmp);
ctlptr += 2;
}
else { /* No packet number specified, so this is the only one */
next->seq = 1;
nd_pkts = 1;
}
if(hdr_len >= 7) { /* Total number of packets */
bcopy(ctlptr,&stmp,2); /* 0 means don't know */
if(stmp) nd_pkts = ntohs(stmp);
ctlptr += 2;
}
if(hdr_len >= 9) { /* Receievd through */
bcopy(ctlptr,&stmp,2); /* 1 means received request */
#ifndef USE_V3_PROT
if((stmp) && (ntohs(stmp) == 1)) {
/* Future retries will be acks only */
pkt->length = 9;
bcopy(&zero,pkt->start+3,2);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Server acked request - retries will be acks only\n");
#endif
}
#endif
ctlptr += 2;
}
if(hdr_len >= 11) { /* Backoff */
bcopy(ctlptr,&stmp,2);
if(stmp) {
backoff = (short)ntohs(stmp);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Backing off to %d seconds\n", backoff);
#endif
timeout.tv_sec = backoff;
if ((backoff > 60) && (first == next) && (no_pkts == 0)) {
/* Probably a long queue on the server - don't give up */
retries = client_dirsrv_retry;
}
}
ctlptr += 2;
}
if(hdr_len >= 12) { /* Flags (1st byte) */
bcopy(ctlptr,&rdflag11,1);
if(rdflag11 & 0x80) {
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Ack requested\n");
#endif
ackpend++;
}
if(rdflag11 & 0x40) {
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Sequenced control packet\n");
#endif
next->length = -1;
scpflag++;
}
ctlptr += 1;
}
if(hdr_len >= 13) { /* Flags (2nd byte) */
/* Reserved for future use */
bcopy(ctlptr,&rdflag12,1);
ctlptr += 1;
}
if(next->seq == 0) {
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
if(next->length >= 0) next->length -= hdr_len;
next->start += hdr_len;
goto done_old;
}
pkt_cid = 0;
/* if intermediate format (between old and new), then process */
/* and go to done_old */
ctlptr = next->start + max(0,next->length-20);
while(*ctlptr) ctlptr++;
/* Control fields start after the terminating null */
ctlptr++;
/* Until old version are gone, must be 4 extra bytes minimum */
/* When no version 3 servers, can remove the -4 */
if(ctlptr < (next->start + next->length - 4)) {
/* Connection ID */
bcopy(ctlptr,&stmp,2);
if(stmp) pkt_cid = ntohs(stmp);
ctlptr += 2;
if(pkt_cid && this_conn_id && (pkt_cid != ntohs(this_conn_id))) {
/* The packet is not for us */
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
/* Packet number */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
next->seq = ntohs(stmp);
ctlptr += 2;
}
/* Total number of packets */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
if(stmp) nd_pkts = ntohs(stmp);
ctlptr += 2;
}
/* Receievd through */
if(ctlptr < (next->start + next->length)) {
/* Not supported by clients */
ctlptr += 2;
}
/* Backoff */
if(ctlptr < (next->start + next->length)) {
bcopy(ctlptr,&stmp,2);
backoff = ntohs(stmp);
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Backing off to %d seconds\n", backoff);
#endif
if(backoff) timeout.tv_sec = backoff;
ctlptr += 2;
}
if(next->seq == 0) {
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
goto done_old;
}
/* Notes that we have to start searching 11 bytes before the */
/* expected start of the MULTI-PACKET line because the message */
/* might include up to 10 bytes of data after the trailing null */
/* The order of those bytes is two bytes each for Connection ID */
/* Packet-no, of, Received-through, Backoff */
seqtxt = nlsindex(next->start + max(0,next->length - 40),"MULTI-PACKET");
if(seqtxt) seqtxt+= 13;
if((nd_pkts == 0) && (no_pkts == 0) && (seqtxt == NULL)) goto all_done;
tmp = sscanf(seqtxt,"%d OF %d", &(next->seq), &nd_pkts);
#ifdef DEBUG
if (pfs_debug && (tmp == 0))
fprintf(stderr,"Cant read packet sequence number: %s", seqtxt);
#endif
done_old:
#ifdef DEBUG
if(pfs_debug > 2) fprintf(stderr,"Packet %d of %d\n",next->seq,nd_pkts);
#endif
if ((first == next) && (no_pkts == 0)) {
if(first->seq == 1) {
comp_thru = first;
/* If only one packet, then return it */
if(nd_pkts == 1) goto all_done;
}
else gaps++;
no_pkts = 1;
next = ptalloc();
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
if(comp_thru && (next->seq <= comp_thru->seq))
ptfree(next);
else if (next->seq < first->seq) {
vtmp = first;
first = next;
first->next = vtmp;
first->previous = NULL;
vtmp->previous = first;
if(first->seq == 1) comp_thru = first;
no_pkts++;
}
else {
vtmp = (comp_thru ? comp_thru : first);
while (vtmp->seq < next->seq) {
if(vtmp->next == NULL) {
vtmp->next = next;
next->previous = vtmp;
next->next = NULL;
no_pkts++;
goto ins_done;
}
vtmp = vtmp->next;
}
if(vtmp->seq == next->seq)
ptfree(next);
else {
vtmp->previous->next = next;
next->previous = vtmp->previous;
next->next = vtmp;
vtmp->previous = next;
no_pkts++;
}
}
ins_done:
while(comp_thru && comp_thru->next &&
(comp_thru->next->seq == (comp_thru->seq + 1))) {
comp_thru = comp_thru->next;
#ifndef USE_V3_PROT
recvd_thru = htons(comp_thru->seq);
bcopy(&recvd_thru,pkt->start+7,2); /* Let server know we got it */
#endif
/* We've made progress, so reset retry count */
retries = client_dirsrv_retry;
/* Also, next retry will be only an acknowledgement */
/* but for now, we can't fill in the ack field */
#ifdef DEBUG
if(pfs_debug > 2)
fprintf(stderr,"Packets now received through %d\n",comp_thru->seq);
#endif
}
/* See if there are any gaps */
if(!comp_thru || comp_thru->next) gaps++;
else gaps = 0;
if ((nd_pkts == 0) || (no_pkts < nd_pkts)) {
next = ptalloc();
/* goto keep_waiting; */
keepWaitingDirsend();
return;
}
all_done:
if(ackpend) { /* Send acknowledgement if requested */
#ifdef DEBUG
if (pfs_debug > 2) {
if (to.sin_family == AF_INET)
fprintf(stderr,"Acknowledging final packet to %s(%d)\n",
to_hostname, ntohs(this_conn_id));
else
fprintf(stderr,"Acknowledging final packet\n");
(void) fflush(stderr);
}
#endif
#ifndef CUTCP
ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
#else
while(--lretry) {
ns = netusend(&to.sin_addr, ntohs(to.sin_port), ntohs(us.sin_port),(char *) pkt->start, pkt->length);
if(!ns)
break;
Stask();
Stask();
}
#endif
#ifndef CUTCP
if(ns != pkt->length) {
#else
if(ns != 0) {
#endif
#ifdef DEBUG
if (pfs_debug) {
fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
perror("");
}
#endif
}
}
#ifndef CUTCP
close(lp);
#endif
ptlfree(pkt);
/* Get rid of any sequenced control packets */
if(scpflag) {
while(first && (first->length < 0)) {
vtmp = first;
first = first->next;
if(first) first->previous = NULL;
ptfree(vtmp);
}
vtmp = first;
while(vtmp && vtmp->next) {
if(vtmp->next->length < 0) {
if(vtmp->next->next) {
vtmp->next = vtmp->next->next;
ptfree(vtmp->next->previous);
vtmp->next->previous = vtmp;
}
else {
ptfree(vtmp->next);
vtmp->next = NULL;
}
}
vtmp = vtmp->next;
}
}
/* return(first); */
dirsendReturn = first;
dirsendDone = DSRET_DONE;
}
/* - - - - - - - - */
/* These routines allow dirsend() to run with or without X by providing
* wrappers around the calls that handle the asynchronous communication.
* All parameters are passed using globals.
* Under X: The input sources and timeouts are set using Xt calls, and
* processEvent() just calls XtAppProcessEvent().
* Non-X: None of the input sources and timeouts are used, and
* processEvent() calls select() to handle both timeouts and the
* socket file descriptor. The return value of select() is used
* to determine which callback routine to call.
*/
static void
addInputSource()
{
#ifdef XARCHIE
inputId = XtAppAddInput(appContext,lp,(XtPointer)XtInputReadMask,
readProc,NULL);
#endif
}
static void
removeInputSource()
{
#ifdef XARCHIE
XtRemoveInput(inputId);
#endif
}
static void
addTimeOut()
{
#ifdef XARCHIE
unsigned long timeoutLen = selwait->tv_sec*1000 + selwait->tv_usec/1000;
/* old timeout can still be there if we are being called after the
* file descriptor was read, so we remove it just to be sure. */
removeTimeOut();
timerId = XtAppAddTimeOut(appContext,timeoutLen,timeoutProc,NULL);
#endif
}
static void
removeTimeOut()
{
#ifdef XARCHIE
if (timerId != (XtIntervalId)0) {
XtRemoveTimeOut(timerId);
timerId = (XtIntervalId)0;
}
#endif
}
/*
* In X, this just calls the X routine that blocks waiting for an event,
* timer, or input source and dispatches it.
*
* Otherwise, for Unix we call select() with the appropriate arguments,
* and act on its return calue as follows:
* == 0 : The timer expired, call timeoutProc() then return to, presumably,
* the loop that calling processEvent() until dirsendDone.
* < 0 : If we were interrupted (errno == EINTR) then don't do anything.
* Presumably the signal handler set flags if needed, and we'll
* come back to select() again on the next pass of the outer loop
* if we're supposed to. Otherwise, puke on the error.
* > 0 : The socket is ready for reading, so call readProc().
*
* Otherwise, if we're in MSDOS (CUTCP defined) then I (gf) have no idea
* what's going on. Ask Brendan.
*/
static void processEvent()
{
#ifdef XARCHIE
XtAppProcessEvent(appContext,XtIMAll);
#else
/* select - either recv is ready, or timeout */
/* see if timeout or error or wrong descriptor */
tmp = select(maxFD+1, &readfds, (SELECTARG *)0, (SELECTARG *)0, selwait);
if (tmp == 0)
{
timeoutProc(NULL,&timerId);
}
else if (tmp < 0 && errno == EINTR)
{ /* gf: new for ^C in varchie */
fprintf(stderr,"select interrupted\n"); /* do nothing, we'll be back */
#ifdef NeXT
dirsendDone = DSRET_ABORTED;
}
else if( tmp > 0 && FD_ISSET(threadAbortFD, &readfds) )
{ /* The user has aborted the query thread */
dirsendDone = DSRET_ABORTED;
#endif
}
else if ((tmp < 0) || !FD_ISSET(lp,&readfds))
{
#ifdef DEBUG
if (pfs_debug)
{
fprintf(stderr, "select failed(processEvent): readfds=%x ",
readfds);
perror("");
}
#endif
close(lp);
perrno = DIRSEND_SELECT_FAILED;
ptlfree(first);
ptlfree(pkt);
/* return(NULL); */
dirsendReturn = NULL;
dirsendDone = DSRET_SELECT_ERROR;
}
else
{
readProc(NULL,&lp,&inputId);
}
#endif /* XARCHIE */
}
void
abortDirsend()
{
if (!dirsendDone) {
#ifndef CUTCP
close(lp);
#endif
ptlfree(first);
ptlfree(pkt);
dirsendReturn = NULL;
dirsendDone = DSRET_ABORTED;
}
return;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.