home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / UNIX / ARCHIE / CLIENTS / C_ARCHI1.TAR / archie / dirsend.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-19  |  28.7 KB  |  1,056 lines

  1. /*
  2.  * Copyright (c) 1989, 1990, 1991 by the University of Washington
  3.  *
  4.  * For copying and distribution information, please see the file
  5.  * <copyright.h>.
  6.  *
  7.  * v1.2.4 - 11/19/91 (mmt) - added MSDOS & OS2 stuff
  8.  * v1.2.3 - 11/04/91 (bcn) - removed host comparison and replaced with check
  9.  *                 for connection id (undoes effect of v1.2.2.).
  10.  * v1.2.2 - 11/02/91 (gf)  - removed extra inet_ntoa() calls and stuff for
  11.  *                 multi-interface nets (lmjm@doc.imperial.ac.uk)
  12.  * v1.2.1 - 10/20/91 (gf)  - asynch implementation
  13.  * v1.2.0 - 09/17/91 (bpk) - added BULL & USG stuff, thanks to Jim Sillas
  14.  * v1.1.2 - 08/30/91 (bpk) - added VMS support
  15.  * v1.1.1 - 08/29/91 (bcn) - changed backoff handling
  16.  * v1.1.0 - 08/13/91 (gf)  - added XArchie status calls
  17.  *
  18.  * gf: 20 Oct 1991:
  19.  *  Broken into pieces so that under X dirsend() doesn't block in select()
  20.  *  but rather uses Xt calls to allow continued event processing. If
  21.  *  XARCHIE is not defined, can still be used since processEvent() will
  22.  *  use select() in this case.
  23.  */
  24.  
  25. #include <copyright.h>
  26. #include <stdio.h>
  27. #include <errno.h>
  28.  
  29. #ifdef VMS
  30. # ifdef WALLONGONG
  31. #  include "twg$tcp:[netdist.include]netdb.h"
  32. # else /* Multinet */
  33. #  include "multinet_root:[multinet.include]netdb.h"
  34. # endif
  35. # include <vms.h>
  36. #else /* not VMS */
  37. # include <sys/types.h> /* this may/will define FD_SET etc */
  38. # include <pmachine.h>
  39. # ifdef NEED_TIME_H
  40. #  include <time.h>
  41. # else
  42. #  include <sys/time.h>
  43. # endif
  44. # ifdef NEED_STRING_H
  45. #  include <string.h>
  46. # else
  47. #  include <strings.h>
  48. # endif
  49. # include <netdb.h>
  50. # include <sys/socket.h>
  51. # ifdef NEED_SELECT_H
  52. #  include <sys/select.h>
  53. # endif /* NEED_SELECT_H */
  54. # ifndef IN_H
  55. #  include <netinet/in.h>
  56. #  define IN_H
  57. # endif
  58. # ifndef hpux
  59. #  include <arpa/inet.h>
  60. # endif
  61. #endif /* !VMS */
  62.  
  63. #include <pfs.h>
  64. #include <pprot.h>
  65. #include <pcompat.h>
  66. #include <perrno.h>
  67.  
  68. /* Gnu C currently fails to pass structures on Sparcs properly.  This directly
  69.    effects the calling of inet_ntoa().  To get around it, we use this hack;
  70.    take the address of what's being called to inet_ntoa, so it gets it
  71.    properly.  This won't be necessary with gcc 2.0.  */
  72. #if defined(sun) && defined(__GNUC__) && !defined(__GNU_LIBRARY__)
  73. # define SUN_GNU_FIX &
  74. #else
  75. # define SUN_GNU_FIX
  76. #endif
  77.  
  78. static int notprived = 0;
  79. #ifndef MSDOS
  80. extern int errno;
  81. #endif
  82. extern int perrno;
  83. extern int rdgram_priority;
  84. #ifdef DEBUG
  85. extern int pfs_debug;
  86. #endif
  87. extern int pfs_disable_flag;
  88.  
  89. char    *nlsindex();
  90.  
  91. #define max(X, Y)  ((X) > (Y) ? (X) : (Y))
  92.  
  93. static int        dir_udp_port = 0;    /* Remote UDP port number */
  94.  
  95. static unsigned short    next_conn_id = 0;
  96.  
  97. #ifdef XARCHIE
  98. int client_dirsrv_timeout = CLIENT_DIRSRV_TIMEOUT;
  99. int client_dirsrv_retry = CLIENT_DIRSRV_RETRY; 
  100. #else /* !XARCHIE */
  101. static int client_dirsrv_timeout = CLIENT_DIRSRV_TIMEOUT;
  102. static int client_dirsrv_retry = CLIENT_DIRSRV_RETRY; 
  103. #endif /* XARCHIE */
  104.  
  105. /* These were parameters to dirsend() */
  106. static PTEXT pkt;
  107. static char *hostname;
  108. static struct sockaddr_in *hostaddr;
  109.  
  110. /* These were locals in dirsend(). Note that the initializations here
  111.  * are really meaningless since we have to redo them for each call to
  112.  * dirsend() since they were formerly automatically initialized.
  113.  */
  114. static PTEXT        first = NULL;    /* First returned packet     */
  115. static PTEXT        next;        /* The one we are waiting for      */
  116. static PTEXT        vtmp;           /* For reorganizing linked list  */
  117. static PTEXT        comp_thru;    /* We have all packets though    */
  118. static int        lp = -1;    /* Opened UDP port             */
  119. static int        hdr_len;    /* Header Length                 */
  120. static int        nd_pkts;    /* Number of packets we want     */
  121. static int        no_pkts;    /* Number of packets we have     */
  122. static int        pkt_cid;        /* Packet connection identifier  */
  123. static unsigned short    this_conn_id;    /* Connection ID we are using    */
  124. static unsigned short    recvd_thru;    /* Received through              */
  125. static short        priority;    /* Priority for request          */
  126. static short        one = 0;    /* Pointer to value 1            */
  127. static short        zero = 0;    /* Pointer to value 0         */
  128. static char        *seqtxt;    /* Pointer to text w/ sequence # */
  129. static struct sockaddr_in  us;        /* Our address                   */
  130. static struct sockaddr_in  to;        /* Address to send query     */
  131. static struct sockaddr_in  from;    /* Reply received from         */
  132. static int        from_sz;    /* Size of from structure     */
  133. static struct hostent    *host;        /* Host info from gethostbyname  */
  134. static long        newhostaddr;    /* New host address from *host   */
  135. static int        req_udp_port=0; /* Requested port (optional)     */
  136. static char        *openparen;    /* Delimits port in name         */
  137. static char        hostnoport[500];/* Host name without port        */
  138. static int        ns;        /* Number of bytes actually sent */
  139. static int        nr;        /* Number of bytes received      */
  140. static fd_set        readfds;    /* Used for select         */
  141. static int        tmp;
  142. static char        *ctlptr;    /* Pointer to control field      */
  143. static short        stmp;        /* Temp short for conversions    */
  144. static int        backoff;    /* Server requested backoff      */
  145. static unsigned char    rdflag11;    /* First byte of flags (bit vect)*/
  146. static unsigned char    rdflag12;    /* Second byte of flags (int)    */
  147. static int        scpflag = 0;    /* Set if any sequencd cont pkts */
  148. static int        ackpend = 0;    /* Acknowledgement pending      */
  149. static int        gaps = 0;    /* Gaps present in recvd pkts   */
  150. static struct timeval    timeout;    /* Time to wait for response    */
  151. static struct timeval    ackwait;    /* Time to wait before acking   */
  152. static struct timeval    gapwait;    /* Time to wait b4 filling gaps */
  153. static struct timeval    *selwait;    /* Time to wait for select      */
  154. static int        retries;    /* was = client_dirsrv_retry    */
  155. char   to_hostname[512];        /* lmjm: saves inet_ntoa() str  */
  156.  
  157. /* These are added so dirsend() "blocks" properly */
  158. static PTEXT dirsendReturn;
  159. static int dirsendDone;
  160.  
  161. /* And here are the values for dirsendDone */
  162. #define DSRET_DONE        1
  163. #define DSRET_SEND_ERROR    -1
  164. #define DSRET_RECV_ERROR    -2
  165. #define DSRET_SELECT_ERROR    -3
  166. #define DSRET_TIMEOUT        -4
  167. #define DSRET_ABORTED        -5
  168.  
  169. /* New procedures to break up dirsend() */
  170. static int initDirsend();
  171. static void retryDirsend(), keepWaitingDirsend();
  172. static void timeoutProc();
  173. static void readProc();
  174.  
  175. /* Wrappers around X calls to allow non-X usage */
  176. static void addInputSource(), removeInputSource();
  177. static void addTimeOut(), removeTimeOut();
  178. static void processEvent();
  179.  
  180. /* Extra stuff for the asynchronous X version of dirsend() */
  181. #ifdef XARCHIE
  182. #include <X11/Intrinsic.h>
  183. extern XtAppContext appContext;
  184. #else
  185. typedef char *XtPointer;
  186. typedef char *XtInputId;
  187. typedef char *XtIntervalId;
  188. #endif
  189. static XtInputId inputId;
  190. static XtIntervalId timerId = (XtIntervalId)0;
  191.  
  192. /*
  193.  * dirsend - send packet and receive response
  194.  *
  195.  *   DIRSEND takes a pointer to a structure of type PTEXT, a hostname,
  196.  *   and a pointer to a host address.  It then sends the supplied
  197.  *   packet off to the directory server on the specified host.  If
  198.  *   hostaddr points to a valid address, that address is used.  Otherwise,
  199.  *   the hostname is looked up to obtain the address.  If hostaddr is a
  200.  *   non-null pointer to a 0 address, then the address will be replaced
  201.  *   with that found in the hostname lookup.
  202.  *
  203.  *   DIRSEND will wait for a response and retry an appropriate
  204.  *   number of times as defined by timeout and retries (both static
  205.  *   variables).  It will collect however many packets form the reply, and
  206.  *   return them in a structure (or structures) of type PTEXT.
  207.  *
  208.  *   DIRSEND will free the packet that it is presented as an argument.
  209.  *   The packet is freed even if dirsend fails.
  210.  */
  211. PTEXT
  212. dirsend(pkt_p,hostname_p,hostaddr_p)
  213.     PTEXT pkt_p;
  214.     char *hostname_p;
  215.     struct sockaddr_in    *hostaddr_p;
  216. {
  217.     /* copy parameters to globals since other routines use them */
  218.     pkt = pkt_p;
  219.     hostname = hostname_p;
  220.     hostaddr = hostaddr_p;
  221.     /* Do the initializations of formerly auto variables */
  222.     first = NULL;
  223.     lp = -1;
  224.     one = 0;
  225.     zero = 0;
  226.     req_udp_port=0;
  227.     scpflag = 0;
  228.     ackpend = 0;
  229.     gaps = 0;
  230.     retries = client_dirsrv_retry;
  231.  
  232.     if (initDirsend() < 0)
  233.     return(NULL);
  234.     addInputSource();
  235.     /* set the first timeout */
  236.     retryDirsend();
  237.  
  238.     dirsendReturn = NULL;
  239.     dirsendDone = 0;
  240.     /* Until one of the callbacks says to return, keep processing events */
  241.     while (!dirsendDone)
  242.     processEvent();
  243.     /* Clean up event generators */
  244.     removeTimeOut();
  245.     removeInputSource();
  246. #ifdef XARCHIE
  247.     /* Set status if needed (has to be outside of loop or X will crash) */
  248.     switch (dirsendDone) {
  249.     case DSRET_SEND_ERROR: status0("Send error"); break;
  250.     case DSRET_RECV_ERROR: status0("Recv error"); break;
  251.         case DSRET_TIMEOUT:
  252.         status1("Connection to %s timed out",to_hostname);
  253.         break;
  254.         case DSRET_ABORTED: status0("Aborted"); break;
  255.     }
  256. #endif
  257.     /* Return whatever we're supposed to */
  258.     return(dirsendReturn);
  259. }
  260.  
  261.  
  262. /*    -    -    -    -    -    -    -    -    */
  263. /* This function does all the initialization that used to be done at the
  264.  * start of dirsend(), including opening the socket descriptor "lp". It
  265.  * returns the descriptor if successful, otherwise -1 to indicate that
  266.  * dirsend() should return NULL immediately.
  267.  */
  268. static int
  269. initDirsend()
  270. {
  271. #ifdef XARCHIE
  272.     status0("Initializing");
  273. #endif
  274.  
  275.     if(one == 0) one = htons((short) 1);
  276.  
  277.     priority = htons(rdgram_priority);
  278.  
  279.     timeout.tv_sec = client_dirsrv_timeout;
  280.     timeout.tv_usec = 0;
  281.  
  282.     ackwait.tv_sec = 0;
  283.     ackwait.tv_usec = 500000;
  284.  
  285.     gapwait.tv_sec = (client_dirsrv_timeout < 5 ? client_dirsrv_timeout : 5);
  286.     gapwait.tv_usec = 0;
  287.  
  288.     comp_thru = NULL;
  289.     perrno = 0;
  290.     nd_pkts = 0;
  291.     no_pkts = 0;
  292.     pkt_cid = 0;
  293.  
  294.     /* Find first connection ID */
  295.     if(next_conn_id == 0) {
  296.     srand(getpid()+time(0));
  297.     next_conn_id = rand();
  298.     }
  299.  
  300.  
  301.     /* If necessary, find out what udp port to send to */
  302.     if (dir_udp_port == 0) {
  303.         register struct servent *sp;
  304.     tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
  305. #ifdef USE_ASSIGNED_PORT
  306.         if ((sp = getservbyname("prospero","udp")) == 0) {
  307. #ifdef DEBUG
  308.         if (pfs_debug)
  309.         fprintf(stderr, "dirsrv: udp/prospero unknown service - using %d\n", 
  310.             PROSPERO_PORT);
  311. #endif
  312.         dir_udp_port = htons((u_short) PROSPERO_PORT);
  313.         }
  314. #else
  315.         if ((sp = getservbyname("dirsrv","udp")) == 0) {
  316. #ifdef DEBUG
  317.         if (pfs_debug)
  318.         fprintf(stderr, "dirsrv: udp/dirsrv unknown service - using %d\n", 
  319.             DIRSRV_PORT);
  320. #endif
  321.         dir_udp_port = htons((u_short) DIRSRV_PORT);
  322.         }
  323. #endif
  324.     else dir_udp_port = sp->s_port;
  325.     pfs_enable = tmp;
  326. #ifdef DEBUG
  327.         if (pfs_debug > 3)
  328.             fprintf(stderr,"dir_udp_port is %d\n", ntohs(dir_udp_port));
  329. #endif
  330.     }
  331.  
  332.     /* If we were given the host address, then use it.  Otherwise  */
  333.     /* lookup the hostname.  If we were passed a host address of   */
  334.     /* 0, we must lookup the host name, then replace the old value */
  335.     if(!hostaddr || (hostaddr->sin_addr.s_addr == 0)) {
  336.     /* I we have a null host name, return an error */
  337.     if((hostname == NULL) || (*hostname == '\0')) {
  338. #ifdef DEBUG
  339.             if (pfs_debug)
  340.                 fprintf(stderr, "dirsrv: Null hostname specified\n");
  341. #endif
  342.         perrno = DIRSEND_BAD_HOSTNAME;
  343.         ptlfree(pkt);
  344.             /* return(NULL); */
  345.         return(-1);
  346.     }
  347.     /* If a port is included, save it away */
  348.     if(openparen = index(hostname,'(')) {
  349.         sscanf(openparen+1,"%d",&req_udp_port);
  350.         strncpy(hostnoport,hostname,400);
  351.         if((openparen - hostname) < 400) {
  352.         *(hostnoport + (openparen - hostname)) = '\0';
  353.         hostname = hostnoport;
  354.         }
  355.     }
  356.     tmp = pfs_enable; pfs_enable = PMAP_DISABLE;
  357.         if((host = gethostbyname(hostname)) == NULL) {
  358.         pfs_enable = tmp;
  359.         /* Check if a numeric address */
  360.         newhostaddr = inet_addr(hostname);
  361.         if(newhostaddr == -1) {
  362. #ifdef DEBUG
  363.         if (pfs_debug)
  364.           fprintf(stderr, "dirsrv: Can't resolve host %s\n",hostname);
  365. #endif
  366.         perrno = DIRSEND_BAD_HOSTNAME;
  367.         ptlfree(pkt);
  368.         /* return(NULL); */
  369.         return(-1);
  370.         }
  371.         bzero((char *)&to, S_AD_SZ);
  372.         to.sin_family = AF_INET;
  373.         bcopy(&newhostaddr, (char *)&to.sin_addr, 4);
  374.         if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
  375.     }
  376.     else {
  377.         pfs_enable = tmp;
  378.         bzero((char *)&to, S_AD_SZ);
  379.         to.sin_family = host->h_addrtype;
  380.         bcopy(host->h_addr, (char *)&to.sin_addr, host->h_length);
  381.         if(hostaddr) bcopy(&to,hostaddr, S_AD_SZ);
  382.     }
  383.     }
  384.     else bcopy(hostaddr,&to, S_AD_SZ);
  385.     /* lmjm: Save away the hostname */
  386.     strncpy(to_hostname,
  387.         inet_ntoa(SUN_GNU_FIX *(struct in_addr *)&to.sin_addr),
  388.         sizeof(to_hostname)-1);
  389.  
  390.     if(req_udp_port) to.sin_port = htons(req_udp_port);
  391.     else to.sin_port = dir_udp_port;
  392.  
  393.     /* If a port was specified in hostaddr, use it, otherwise fill it in */
  394.     if(hostaddr) {
  395.     if(hostaddr->sin_port) to.sin_port = hostaddr->sin_port;
  396.     else hostaddr->sin_port = to.sin_port;
  397.     }
  398.  
  399.     /* Must open a new port each time. we do not want to see old */
  400.     /* responses to messages we are done with                    */
  401.     if ((lp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  402. #ifdef DEBUG
  403.         if (pfs_debug)
  404.             fprintf(stderr,"dirsrv: Can't open socket\n");
  405. #endif
  406.     perrno = DIRSEND_UDP_CANT;
  407.     ptlfree(pkt);
  408.         /* return(NULL); */
  409.     return(-1);
  410.     }
  411.  
  412.     /* Try to bind it to a privileged port - loop through candidate */
  413.     /* ports trying to bind.  If failed, that's OK, we will let the */
  414.     /* system assign a non-privileged port later                    */
  415.     if(!notprived) {
  416.     for(tmp = PROS_FIRST_PRIVP; tmp < PROS_FIRST_PRIVP+PROS_NUM_PRIVP; 
  417.         tmp++) {
  418.         bzero((char *)&us, sizeof(us));
  419.         us.sin_family = AF_INET;
  420.         us.sin_port = htons((u_short) tmp);
  421.         if (bind(lp, (struct sockaddr *)&us, sizeof(us))) {
  422.         if(errno != EADDRINUSE) {
  423.             notprived++;
  424.             break;
  425.         }
  426.         }
  427.         else break;
  428.     }
  429.     }
  430.  
  431. #ifndef USE_V3_PROT
  432.     /* Add header */
  433.     if(rdgram_priority) {
  434.     pkt->start -= 15;
  435.     pkt->length += 15;
  436.     *(pkt->start) = (char) 15;
  437.     bzero(pkt->start+9,4);
  438.     *(pkt->start+11) = 0x02;
  439.     bcopy(&priority,pkt->start+13,2);
  440.     }
  441.     else {
  442.     pkt->start -= 9;
  443.     pkt->length += 9;
  444.     *(pkt->start) = (char) 9;
  445.     }
  446.     this_conn_id = htons(next_conn_id++);
  447.     if(next_conn_id == 0) next_conn_id++;
  448.     bcopy(&this_conn_id,pkt->start+1,2);
  449.     bcopy(&one,pkt->start+3,2);
  450.     bcopy(&one,pkt->start+5,2);
  451.     bzero(pkt->start+7,2);
  452. #endif
  453.  
  454. #ifdef DEBUG
  455.     if (pfs_debug > 2) {
  456. #ifndef USE_V3_PROT
  457.         if (to.sin_family == AF_INET) {
  458.         if(req_udp_port) 
  459.         fprintf(stderr,"Sending message to %s+%d(%d)...",
  460.             to_hostname, req_udp_port, ntohs(this_conn_id));
  461.         else fprintf(stderr,"Sending message to %s(%d)...",
  462.              to_hostname, ntohs(this_conn_id));
  463.     }
  464. #else
  465.         if (to.sin_family == AF_INET) 
  466.         fprintf(stderr,"Sending message to %s...", to_hostname);
  467. #endif
  468.         else
  469.             fprintf(stderr,"Sending message...");
  470.         (void) fflush(stderr);
  471.     }
  472. #endif
  473.  
  474.     first = ptalloc();
  475.     next = first;
  476.  
  477. #ifdef XARCHIE
  478.     status1("Connecting to %s",to_hostname);
  479. #endif
  480.     return(lp);
  481. }
  482.  
  483. /*    -    -    -    -    -    -    -    -    */
  484. /*
  485.  * This used to be a label to goto to retry the last packet. Now we resend
  486.  * the packet and call keepWaitingDirsend() to wait for a reply. (We
  487.  * call keepWaitingDirsend() because formerly the code dropped through
  488.  * the keep_waiting label.)
  489.  */
  490. static void
  491. retryDirsend()
  492. {
  493.     gaps = ackpend = 0;
  494.  
  495.     ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
  496.     if(ns != pkt->length) {
  497. #ifdef DEBUG
  498.     if (pfs_debug) {
  499.     fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
  500.         perror("");
  501.     }
  502. #endif
  503.     close(lp);
  504.     perrno = DIRSEND_NOT_ALL_SENT;
  505.     ptlfree(first);
  506.     ptlfree(pkt);
  507.     /* return(NULL); */
  508.     dirsendReturn = NULL;
  509.     dirsendDone = DSRET_SEND_ERROR;
  510.     }
  511. #ifdef DEBUG
  512.     if (pfs_debug > 2) fprintf(stderr,"Sent.\n");
  513. #endif
  514.     keepWaitingDirsend();
  515. }
  516.  
  517. /*    -    -    -    -    -    -    -    -    */
  518. /*
  519.  * This used to be a label to goto to set the appropriate timeout value
  520.  * and blocked in select(). Now we set selwait and the fd_sets to the
  521.  * appropriate values, and in X register a new timeout, then return to
  522.  * allow event processing.
  523.  */
  524. static void
  525. keepWaitingDirsend()
  526. {
  527.     /* We come back to this point (by a goto) if the packet */
  528.     /* received is only part of the response, or if the     */
  529.     /* response came from the wrong host            */
  530.  
  531. #ifdef DEBUG
  532.     if (pfs_debug > 2) fprintf(stderr,"Waiting for reply...");
  533. #endif
  534.     FD_ZERO(&readfds);
  535.     FD_SET(lp, &readfds);
  536.  
  537.     if(ackpend) selwait = &ackwait;
  538.     else if(gaps) selwait = &gapwait;
  539.     else selwait = &timeout;
  540.  
  541.     addTimeOut();
  542. }
  543.  
  544. /*    -    -    -    -    -    -    -    -    */
  545. /*
  546.  * This routine is called when a timeout occurs. It includes the code that
  547.  * was formerly used when select() returned 0 (indicating a timeout).
  548.  */
  549. /*ARGSUSED*/
  550. static void
  551. timeoutProc(client_data,id)
  552. XtPointer client_data;
  553. XtIntervalId *id;
  554. {
  555.     if (gaps || ackpend) { /* Send acknowledgment */
  556.     /* Acks are piggybacked on retries - If we have received */
  557.     /* an ack from the server, then the packet sent is only  */
  558.     /* an ack and the rest of the message will be empty      */
  559. #ifdef DEBUG
  560.     if (pfs_debug > 2) {
  561.             fprintf(stderr,"Acknowledging (%s).\n",
  562.             (ackpend ? "requested" : "gaps"));
  563.     }    
  564. #endif
  565.     retryDirsend();
  566.     return;
  567.     }
  568.  
  569.     if (retries-- > 0) {
  570.     timeout.tv_sec = CLIENT_DIRSRV_BACKOFF(timeout.tv_sec);
  571. #ifdef DEBUG
  572.     if (pfs_debug > 2) {
  573.             fprintf(stderr,"Timed out.  Setting timeout to %d seconds.\n",
  574.             timeout.tv_sec);
  575.     }
  576. #endif
  577. #ifdef XARCHIE
  578.         status1("Timed out -- retrying (%d seconds)",timeout.tv_sec);
  579. #endif
  580.     retryDirsend();
  581.     return;
  582.     }
  583.  
  584. #ifdef DEBUG
  585.     if (pfs_debug) {
  586.     fprintf(stderr, "select failed(timeoutProc): readfds=%x ",
  587.         readfds);
  588.     perror("");
  589.     }
  590. #endif
  591.     close(lp);
  592.     perrno = DIRSEND_SELECT_FAILED;
  593.     ptlfree(first);
  594.     ptlfree(pkt);
  595.     /* return(NULL); */
  596.     dirsendReturn = NULL;
  597.     dirsendDone = DSRET_TIMEOUT;
  598. }
  599.  
  600. /*    -    -    -    -    -    -    -    -    */
  601. /*
  602.  * This function is called whenever there's something to read on the
  603.  * connection. It includes the code that was run when select() returned
  604.  * greater than 0 (indicating read ready).
  605.  */
  606. /*ARGSUSED*/
  607. static void
  608. readProc(client_data,source,id)
  609. XtPointer client_data;
  610. int *source;
  611. XtInputId *id;
  612. {
  613.     /* We got something to read, so clear the timer */
  614.     removeTimeOut();
  615.  
  616.     from_sz = sizeof(from);
  617.     next->start = next->dat;
  618.     if ((nr = recvfrom(lp, next->start, sizeof(next->dat), 0, (struct sockaddr *)&from, &from_sz)) < 0) {
  619. #ifdef DEBUG
  620.         if (pfs_debug) perror("recvfrom");
  621. #endif
  622.     close(lp);
  623.     perrno = DIRSEND_BAD_RECV;
  624.     ptlfree(first);
  625.     ptlfree(pkt);
  626.     /* return(NULL) */
  627.     dirsendReturn = NULL;
  628.     dirsendDone = DSRET_RECV_ERROR;
  629.         return;
  630.     }
  631.  
  632.     next->length = nr;
  633.     
  634.     *(next->start + next->length) = NULL;
  635.  
  636. #ifdef DEBUG
  637.     if (pfs_debug > 2)
  638.         fprintf(stderr,"Received packet from %s\n",
  639.         inet_ntoa(SUN_GNU_FIX *(struct in_addr *)&from.sin_addr));
  640. #endif
  641.  
  642. #ifdef XARCHIE
  643.     {
  644.         static int num = 2;
  645.     if (num == 2)
  646.         status1("Connected to %s",to_hostname);
  647.     else
  648.         status1("Receiving...%c",(num?'+':'*'));
  649.     num = !num;
  650.     }
  651. #endif
  652.  
  653.     /* For the current format, if the first byte is less than             */
  654.     /* 20, then the first two bits are a version number and the next six  */
  655.     /* are the header length (including the first byte).                  */
  656.     if((hdr_len = (unsigned char) *(next->start)) < 20) {
  657.     ctlptr = next->start + 1;
  658.     next->seq = 0;
  659.     if(hdr_len >= 3) {     /* Connection ID */
  660.         bcopy(ctlptr,&stmp,2);
  661.         if(stmp) pkt_cid = ntohs(stmp);
  662.         ctlptr += 2;
  663.     }
  664.     if(pkt_cid && this_conn_id && (pkt_cid != this_conn_id)) {
  665.         /* The packet is not for us */
  666.         /* goto keep_waiting; */
  667.         keepWaitingDirsend();
  668.         return;
  669.     }
  670.     if(hdr_len >= 5) {    /* Packet number */
  671.         bcopy(ctlptr,&stmp,2);
  672.         next->seq = ntohs(stmp);
  673.         ctlptr += 2;
  674.     }
  675.     else { /* No packet number specified, so this is the only one */
  676.         next->seq = 1;
  677.         nd_pkts = 1;
  678.     }
  679.     if(hdr_len >= 7) {        /* Total number of packets */
  680.         bcopy(ctlptr,&stmp,2);  /* 0 means don't know      */
  681.         if(stmp) nd_pkts = ntohs(stmp);
  682.         ctlptr += 2;
  683.     }
  684.     if(hdr_len >= 9) {    /* Receievd through */
  685.         bcopy(ctlptr,&stmp,2);  /* 1 means received request */
  686. #ifndef USE_V3_PROT
  687.         if((stmp) && (ntohs(stmp) == 1)) {
  688.         /* Future retries will be acks only */
  689.         pkt->length = 9;
  690.         bcopy(&zero,pkt->start+3,2);
  691. #ifdef DEBUG
  692.         if(pfs_debug > 2) 
  693.             fprintf(stderr,"Server acked request - retries will be acks only\n");
  694. #endif
  695.         }
  696. #endif
  697.         ctlptr += 2;
  698.     }
  699.     if(hdr_len >= 11) {    /* Backoff */
  700.         bcopy(ctlptr,&stmp,2);
  701.         if(stmp) {
  702.         backoff = ntohs(stmp);
  703. #ifdef DEBUG
  704.         if(pfs_debug > 2) 
  705.             fprintf(stderr,"Backing off to %d seconds\n", backoff);
  706. #endif
  707.         timeout.tv_sec = backoff;
  708.         if ((backoff > 60) && (first == next) && (no_pkts == 0)) {
  709.             /* Probably a long queue on the server - don't give up */
  710.             retries = client_dirsrv_retry;
  711.         }
  712.         }
  713.         ctlptr += 2;
  714.     }
  715.     if(hdr_len >= 12) {    /* Flags (1st byte) */
  716.         bcopy(ctlptr,&rdflag11,1);
  717.         if(rdflag11 & 0x80) {
  718. #ifdef DEBUG
  719.         if(pfs_debug > 2) 
  720.             fprintf(stderr,"Ack requested\n");
  721. #endif
  722.         ackpend++;
  723.         }
  724.         if(rdflag11 & 0x40) {
  725. #ifdef DEBUG
  726.         if(pfs_debug > 2) 
  727.             fprintf(stderr,"Sequenced control packet\n");
  728. #endif
  729.         next->length = -1;
  730.         scpflag++;
  731.         }
  732.         ctlptr += 1;
  733.     }
  734.     if(hdr_len >= 13) {    /* Flags (2nd byte) */
  735.         /* Reserved for future use */
  736.         bcopy(ctlptr,&rdflag12,1);
  737.         ctlptr += 1;
  738.     }
  739.     if(next->seq == 0) {
  740.         /* goto keep_waiting; */
  741.         keepWaitingDirsend();
  742.         return;
  743.     }
  744.     if(next->length >= 0) next->length -= hdr_len;
  745.     next->start += hdr_len;
  746.     goto done_old;
  747.     }
  748.  
  749.     pkt_cid = 0;
  750.  
  751.     /* if intermediate format (between old and new), then process */
  752.     /* and go to done_old                                         */
  753.     ctlptr = next->start + max(0,next->length-20);
  754.     while(*ctlptr) ctlptr++;
  755.     /* Control fields start after the terminating null */
  756.     ctlptr++;
  757.     /* Until old version are gone, must be 4 extra bytes minimum */
  758.     /* When no version 3 servers, can remove the -4              */
  759.     if(ctlptr < (next->start + next->length - 4)) {
  760.     /* Connection ID */
  761.     bcopy(ctlptr,&stmp,2);
  762.     if(stmp) pkt_cid = ntohs(stmp);
  763.     ctlptr += 2;
  764.     if(pkt_cid && this_conn_id && (pkt_cid != this_conn_id)) {
  765.         /* The packet is not for us */
  766.         /* goto keep_waiting; */
  767.         keepWaitingDirsend();
  768.         return;
  769.     }
  770.     /* Packet number */
  771.     if(ctlptr < (next->start + next->length)) {
  772.         bcopy(ctlptr,&stmp,2);
  773.         next->seq = ntohs(stmp);
  774.         ctlptr += 2;
  775.     }
  776.     /* Total number of packets */
  777.     if(ctlptr < (next->start + next->length)) {
  778.         bcopy(ctlptr,&stmp,2);
  779.         if(stmp) nd_pkts = ntohs(stmp);
  780.         ctlptr += 2;
  781.     }
  782.     /* Receievd through */
  783.     if(ctlptr < (next->start + next->length)) {
  784.         /* Not supported by clients */
  785.         ctlptr += 2;
  786.     }
  787.     /* Backoff */
  788.     if(ctlptr < (next->start + next->length)) {
  789.         bcopy(ctlptr,&stmp,2);
  790.         backoff = ntohs(stmp);
  791. #ifdef DEBUG
  792.         if(pfs_debug > 2) 
  793.         fprintf(stderr,"Backing off to %d seconds\n", backoff);
  794. #endif
  795.         if(backoff) timeout.tv_sec = backoff;
  796.         ctlptr += 2;
  797.     }
  798.     if(next->seq == 0) {
  799.         /* goto keep_waiting; */
  800.         keepWaitingDirsend();
  801.         return;
  802.     }
  803.     goto done_old;
  804.  
  805.     }
  806.  
  807.     /* Notes that we have to start searching 11 bytes before the    */
  808.     /* expected start of the MULTI-PACKET line because the message  */
  809.     /* might include up to 10 bytes of data after the trailing null */
  810.     /* The order of those bytes is two bytes each for Connection ID */
  811.     /* Packet-no, of, Received-through, Backoff                     */
  812.     seqtxt = nlsindex(next->start + max(0,next->length - 40),"MULTI-PACKET"); 
  813.     if(seqtxt) seqtxt+= 13;
  814.  
  815.     if((nd_pkts == 0) && (no_pkts == 0) && (seqtxt == NULL)) goto all_done;
  816.  
  817.     tmp = sscanf(seqtxt,"%d OF %d", &(next->seq), &nd_pkts);
  818. #ifdef DEBUG    
  819.     if (pfs_debug && (tmp == 0)) 
  820.     fprintf(stderr,"Cant read packet sequence number: %s", seqtxt);    
  821. #endif
  822.  done_old:
  823. #ifdef DEBUG
  824.     if(pfs_debug > 2) fprintf(stderr,"Packet %d of %d\n",next->seq,nd_pkts);
  825. #endif
  826.     if ((first == next) && (no_pkts == 0)) {
  827.     if(first->seq == 1) {
  828.         comp_thru = first;
  829.         /* If only one packet, then return it */
  830.         if(nd_pkts == 1) goto all_done;
  831.     }
  832.     else gaps++;
  833.     no_pkts = 1;
  834.     next = ptalloc();
  835.     /* goto keep_waiting; */
  836.     keepWaitingDirsend();
  837.     return;
  838.     }
  839.     
  840.     if(comp_thru && (next->seq <= comp_thru->seq))
  841.     ptfree(next);
  842.     else if (next->seq < first->seq) {
  843.     vtmp = first;
  844.     first = next;
  845.     first->next = vtmp;
  846.     first->previous = NULL;
  847.     vtmp->previous = first;
  848.     if(first->seq == 1) comp_thru = first;
  849.     no_pkts++;
  850.     }
  851.     else {
  852.     vtmp = (comp_thru ? comp_thru : first);
  853.     while (vtmp->seq < next->seq) {
  854.         if(vtmp->next == NULL) {
  855.         vtmp->next = next;
  856.         next->previous = vtmp;
  857.         next->next = NULL;
  858.         no_pkts++;
  859.         goto ins_done;
  860.         }
  861.         vtmp = vtmp->next;
  862.     }
  863.     if(vtmp->seq == next->seq)
  864.         ptfree(next);
  865.     else {
  866.         vtmp->previous->next = next;
  867.         next->previous = vtmp->previous;
  868.         next->next = vtmp;
  869.         vtmp->previous = next;
  870.         no_pkts++;
  871.     }
  872.     }   
  873.  
  874. ins_done:
  875.     
  876.     while(comp_thru && comp_thru->next && 
  877.       (comp_thru->next->seq == (comp_thru->seq + 1))) {
  878.     comp_thru = comp_thru->next;
  879. #ifndef USE_V3_PROT
  880.     recvd_thru = htons(comp_thru->seq);
  881.     bcopy(&recvd_thru,pkt->start+7,2); /* Let server know we got it */
  882. #endif
  883.     /* We've made progress, so reset retry count */
  884.     retries = client_dirsrv_retry;
  885.     /* Also, next retry will be only an acknowledgement */
  886.     /* but for now, we can't fill in the ack field      */
  887. #ifdef DEBUG
  888.     if(pfs_debug > 2) 
  889.         fprintf(stderr,"Packets now received through %d\n",comp_thru->seq);
  890. #endif
  891.     }
  892.  
  893.     /* See if there are any gaps */
  894.     if(!comp_thru || comp_thru->next) gaps++;
  895.     else gaps = 0;
  896.  
  897.     if ((nd_pkts == 0) || (no_pkts < nd_pkts)) {
  898.     next = ptalloc();
  899.     /* goto keep_waiting; */
  900.     keepWaitingDirsend();
  901.     return;
  902.     }
  903.  
  904.  all_done:
  905.     if(ackpend) { /* Send acknowledgement if requested */
  906. #ifdef DEBUG
  907.     if (pfs_debug > 2) {
  908.         if (to.sin_family == AF_INET)
  909.         fprintf(stderr,"Acknowledging final packet to %s(%d)\n",
  910.             to_hostname, ntohs(this_conn_id));
  911.             else
  912.                 fprintf(stderr,"Acknowledging final packet\n");
  913.         (void) fflush(stderr);
  914.     }
  915. #endif
  916.     ns = sendto(lp,(char *)(pkt->start), pkt->length, 0, (struct sockaddr *)&to, S_AD_SZ);
  917.     if(ns != pkt->length) {
  918. #ifdef DEBUG
  919.         if (pfs_debug) {
  920.         fprintf(stderr,"\nsent only %d/%d: ",ns, pkt->length);
  921.         perror("");
  922.         }
  923. #endif
  924.     }
  925.  
  926.     }
  927.     close(lp);
  928.     ptlfree(pkt);
  929.  
  930.     /* Get rid of any sequenced control packets */
  931.     if(scpflag) {
  932.     while(first && (first->length < 0)) {
  933.         vtmp = first;
  934.         first = first->next;
  935.         if(first) first->previous = NULL;
  936.         ptfree(vtmp);
  937.     }
  938.     vtmp = first;
  939.     while(vtmp && vtmp->next) {
  940.         if(vtmp->next->length < 0) {
  941.         if(vtmp->next->next) {
  942.             vtmp->next = vtmp->next->next;
  943.             ptfree(vtmp->next->previous);
  944.             vtmp->next->previous = vtmp;
  945.         }
  946.         else {
  947.             ptfree(vtmp->next);
  948.             vtmp->next = NULL;
  949.         }
  950.         }
  951.         vtmp = vtmp->next;
  952.     }
  953.     }
  954.  
  955.     /* return(first); */
  956.     dirsendReturn = first;
  957.     dirsendDone = DSRET_DONE;
  958.  
  959. }
  960. /*    -    -    -    -    -    -    -    -    */
  961. /* These routines allow dirsend() to run with or without X by providing
  962.  * wrappers around the calls that handle the asynchronous communication.
  963.  * All parameters are passed using globals.
  964.  * Under X: The input sources and timeouts are set using Xt calls, and
  965.  *        processEvent() just calls XtAppProcessEvent().
  966.  * Non-X: None of the input sources and timeouts are used, and
  967.  *      processEvent() calls select() to handle both timeouts and the
  968.  *      socket file descriptor. The return value of select() is used
  969.  *      to determine which callback routine to call.
  970.  */
  971.  
  972. static void
  973. addInputSource()
  974. {
  975. #ifdef XARCHIE
  976.     inputId = XtAppAddInput(appContext,lp,XtInputReadMask,readProc,NULL);
  977. #endif
  978. }
  979.  
  980. static void
  981. removeInputSource()
  982. {
  983. #ifdef XARCHIE
  984.     XtRemoveInput(inputId);
  985. #endif
  986. }
  987.  
  988. static void
  989. addTimeOut()
  990. {
  991. #ifdef XARCHIE
  992.     unsigned long timeoutLen = selwait->tv_sec*1000 + selwait->tv_usec/1000;
  993.  
  994.     /* old timeout can still be there if we are being called after the
  995.      * file descriptor was read, so we remove it just to be sure. */
  996.     removeTimeOut();
  997.     timerId = XtAppAddTimeOut(appContext,timeoutLen,timeoutProc,NULL);
  998. #endif
  999. }
  1000.  
  1001. static void
  1002. removeTimeOut()
  1003. {
  1004. #ifdef XARCHIE
  1005.     if (timerId != (XtIntervalId)0) {
  1006.     XtRemoveTimeOut(timerId);
  1007.     timerId = (XtIntervalId)0;
  1008.     }
  1009. #endif
  1010. }
  1011.  
  1012. static void
  1013. processEvent()
  1014. {
  1015. #ifdef XARCHIE
  1016.     XtAppProcessEvent(appContext,XtIMAll);
  1017. #else
  1018.     /* select - either recv is ready, or timeout */
  1019.     /* see if timeout or error or wrong descriptor */
  1020.     tmp = select(lp + 1, &readfds, (fd_set *)0, (fd_set *)0, selwait);
  1021.     if (tmp == 0) {
  1022.     timeoutProc(NULL,&timerId);
  1023.     } else if ((tmp < 0) || !FD_ISSET(lp,&readfds)) {
  1024. #ifdef DEBUG
  1025.     if (pfs_debug) {
  1026.         fprintf(stderr, "select failed(processEvent): readfds=%x ",
  1027.             readfds);
  1028.         perror("");
  1029.     }
  1030. #endif
  1031.     close(lp);
  1032.     perrno = DIRSEND_SELECT_FAILED;
  1033.     ptlfree(first);
  1034.     ptlfree(pkt);
  1035.     /* return(NULL); */
  1036.     dirsendReturn = NULL;
  1037.     dirsendDone = DSRET_SELECT_ERROR;
  1038.     } else {
  1039.     readProc(NULL,&lp,&inputId);
  1040.     }
  1041. #endif /* XARCHIE */
  1042. }
  1043.  
  1044. void
  1045. abortDirsend()
  1046. {
  1047.     if (!dirsendDone) {
  1048.     close(lp);
  1049.     ptlfree(first);
  1050.     ptlfree(pkt);
  1051.     dirsendReturn = NULL;
  1052.     dirsendDone = DSRET_ABORTED;
  1053.     }
  1054.     return;
  1055. }
  1056.