tcp_unix.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:26k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: UNIX TCP/IP routines
  3.  *
  4.  * Author: Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 1 August 1988
  13.  * Last Edited: 29 October 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #undef write /* don't use redefined write() */
  36.  
  37. static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
  38. static long ttmo_open = 0; /* TCP timeouts, in seconds */
  39. static long ttmo_read = 0;
  40. static long ttmo_write = 0;
  41. static long rshtimeout = 15; /* rsh timeout */
  42. static char *rshcommand = NIL; /* rsh command */
  43. static char *rshpath = NIL; /* rsh path */
  44. static long sshtimeout = 15; /* ssh timeout */
  45. static char *sshcommand = NIL; /* ssh command */
  46. static char *sshpath = NIL; /* ssh path */
  47. static long allowreversedns = /* allow reverse DNS lookup */
  48. #ifdef DISABLE_REVERSE_DNS_LOOKUP
  49.   NIL /* Not recommended, especially if using Kerberos authentication */
  50. #else
  51.   T
  52. #endif
  53.   ;
  54. /* Local function prototypes */
  55. int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
  56.      unsigned long port);
  57. long tcp_abort (TCPSTREAM *stream);
  58. char *tcp_name (struct sockaddr_in *sin,long flag);
  59. /* TCP/IP manipulate parameters
  60.  * Accepts: function code
  61.  *     function-dependent value
  62.  * Returns: function-dependent return value
  63.  */
  64. void *tcp_parameters (long function,void *value)
  65. {
  66.   switch ((int) function) {
  67.   case SET_TIMEOUT:
  68.     tmoh = (tcptimeout_t) value;
  69.     break;
  70.   case GET_TIMEOUT:
  71.     value = (void *) tmoh;
  72.     break;
  73.   case SET_OPENTIMEOUT:
  74.     ttmo_open = (long) value;
  75.     break;
  76.   case GET_OPENTIMEOUT:
  77.     value = (void *) ttmo_open;
  78.     break;
  79.   case SET_READTIMEOUT:
  80.     ttmo_read = (long) value;
  81.     break;
  82.   case GET_READTIMEOUT:
  83.     value = (void *) ttmo_read;
  84.     break;
  85.   case SET_WRITETIMEOUT:
  86.     ttmo_write = (long) value;
  87.     break;
  88.   case GET_WRITETIMEOUT:
  89.     value = (void *) ttmo_write;
  90.     break;
  91.   case SET_ALLOWREVERSEDNS:
  92.     allowreversedns = (long) value;
  93.     break;
  94.   case GET_ALLOWREVERSEDNS:
  95.     value = (void *) allowreversedns;
  96.     break;
  97.   case SET_RSHTIMEOUT:
  98.     rshtimeout = (long) value;
  99.     break;
  100.   case GET_RSHTIMEOUT:
  101.     value = (void *) rshtimeout;
  102.     break;
  103.   case SET_RSHCOMMAND:
  104.     if (rshcommand) fs_give ((void **) &rshcommand);
  105.     rshcommand = cpystr ((char *) value);
  106.     break;
  107.   case GET_RSHCOMMAND:
  108.     value = (void *) rshcommand;
  109.     break;
  110.   case SET_RSHPATH:
  111.     if (rshpath) fs_give ((void **) &rshpath);
  112.     rshpath = cpystr ((char *) value);
  113.     break;
  114.   case GET_RSHPATH:
  115.     value = (void *) rshpath;
  116.     break;
  117.   case SET_SSHTIMEOUT:
  118.     sshtimeout = (long) value;
  119.     break;
  120.   case GET_SSHTIMEOUT:
  121.     value = (void *) sshtimeout;
  122.     break;
  123.   case SET_SSHCOMMAND:
  124.     if (sshcommand) fs_give ((void **) &sshcommand);
  125.     sshcommand = cpystr ((char *) value);
  126.     break;
  127.   case GET_SSHCOMMAND:
  128.     value = (void *) sshcommand;
  129.     break;
  130.   case SET_SSHPATH:
  131.     if (sshpath) fs_give ((void **) &sshpath);
  132.     sshpath = cpystr ((char *) value);
  133.     break;
  134.   case GET_SSHPATH:
  135.     value = (void *) sshpath;
  136.     break;
  137.   default:
  138.     value = NIL; /* error case */
  139.     break;
  140.   }
  141.   return value;
  142. }
  143. /* TCP/IP open
  144.  * Accepts: host name
  145.  *     contact service name
  146.  *     contact port number and optional silent flag
  147.  * Returns: TCP/IP stream if success else NIL
  148.  */
  149. TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
  150. {
  151.   TCPSTREAM *stream = NIL;
  152.   int i,sock;
  153.   int ctr = 0;
  154.   int silent = (port & 0x80000000) ? T : NIL;
  155.   int *ctrp = &ctr;
  156.   char *s;
  157.   struct sockaddr_in sin;
  158.   struct hostent *he;
  159.   char hostname[MAILTMPLEN];
  160.   char tmp[MAILTMPLEN];
  161.   struct servent *sv = NIL;
  162.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  163.   void *data;
  164.   port &= 0x7fffffff; /* erase silent flag */
  165.   if (service) { /* service specified? */
  166.     if (*service == '*') { /* yes, special alt driver kludge? */
  167.       ctrp = NIL; /* yes, don't do open timeout */
  168.       sv = getservbyname (service + 1,"tcp");
  169.     }
  170.     else sv = getservbyname (service,"tcp");
  171.   }
  172. /* user service name port */
  173.   if (sv) port = ntohs (sin.sin_port = sv->s_port);
  174.   /* copy port number in network format */
  175.   else sin.sin_port = htons (port);
  176.   /* The domain literal form is used (rather than simply the dotted decimal
  177.      as with other Unix programs) because it has to be a valid "host name"
  178.      in mailsystem terminology. */
  179. /* look like domain literal? */
  180.   if (host[0] == '[' && host[(strlen (host))-1] == ']') {
  181.     strcpy (hostname,host+1); /* yes, copy number part */
  182.     hostname[(strlen (hostname))-1] = '';
  183.     if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
  184.       sin.sin_family = AF_INET; /* family is always Internet */
  185.       strcpy (hostname,host); /* hostname is user's argument */
  186. /* get an open socket for this system */
  187.       sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
  188.     }
  189.     else {
  190.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  191.       mm_log (tmp,ERROR);
  192.       return NIL;
  193.     }
  194.   }
  195.   else { /* lookup host name */
  196.     (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
  197.     data = (*bn) (BLOCK_SENSITIVE,NIL);
  198.     if (he = gethostbyname (lcase (strcpy (hostname,host)))) {
  199.       (*bn) (BLOCK_NONSENSITIVE,data);
  200.       (*bn) (BLOCK_NONE,NIL);
  201. /* copy address type */
  202.       sin.sin_family = he->h_addrtype;
  203. /* copy host name */
  204.       strcpy (hostname,he->h_name);
  205. #ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */
  206.       for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) {
  207. if (i && !silent) mm_log (tmp,WARN);
  208. memcpy (&sin.sin_addr,s,he->h_length);
  209. (*bn) (BLOCK_TCPOPEN,NIL);
  210. sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
  211. (*bn) (BLOCK_NONE,NIL);
  212.       }
  213. #else /* the one true address then */
  214.       memcpy (&sin.sin_addr,he->h_addr,he->h_length);
  215.       (*bn) (BLOCK_DNSLOOKUP,NIL);
  216.       sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
  217.       (*bn) (BLOCK_NONE,NIL);
  218. #endif
  219.     }
  220.     else {
  221.       (*bn) (BLOCK_NONSENSITIVE,data);
  222.       (*bn) (BLOCK_NONE,NIL);
  223.       sprintf (tmp,"No such host as %.80s",host);
  224.       mm_log (tmp,ERROR);
  225.       return NIL;
  226.     }
  227.   }
  228.   if (sock >= 0)  { /* won */
  229.     stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
  230.    sizeof (TCPSTREAM));
  231.     stream->port = port; /* port number */
  232. /* init sockets */
  233.     stream->tcpsi = stream->tcpso = sock;
  234. /* stash in the snuck-in byte */
  235.     if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
  236. /* copy official host name */
  237.     stream->host = cpystr (hostname);
  238.   }
  239.   else if (!silent) mm_log (tmp,ERROR);
  240.   return stream; /* return success */
  241. }
  242. /* Open a TCP socket
  243.  * Accepts: Internet socket address block
  244.  *     scratch buffer
  245.  *     pointer to "first byte read in" storage or NIL
  246.  *     host name for error message
  247.  *     port number for error message
  248.  * Returns: socket if success, else -1 with error string in scratch buffer
  249.  */
  250. int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
  251.      unsigned long port)
  252. {
  253.   int i,ti,sock,flgs;
  254.   time_t now;
  255.   struct protoent *pt = getprotobyname ("ip");
  256.   fd_set fds,efds;
  257.   struct timeval tmo;
  258.   sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
  259.   mm_log (tmp,NIL);
  260. /* make a socket */
  261.   if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
  262.     sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
  263.     return -1;
  264.   }
  265.   if (!ctr) { /* no open timeout wanted */
  266. /* open connection */
  267.     while ((i = connect (sock,(struct sockaddr *) sin,
  268.  sizeof (struct sockaddr_in))) < 0 && errno == EINTR);
  269.     if (i < 0) { /* failed? */
  270.       sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
  271.       close (sock); /* flush socket */
  272.       return -1;
  273.     }
  274.   }
  275.   else { /*  want open timeout */
  276. /* get current socket flags */
  277.     flgs = fcntl (sock,F_GETFL,0);
  278. /* set non-blocking */
  279.     fcntl (sock,F_SETFL,flgs | FNDELAY);
  280. /* open connection */
  281.     while ((i = connect (sock,(struct sockaddr *) sin,
  282.        sizeof (struct sockaddr_in))) < 0 && errno == EINTR);
  283.     if (i < 0) switch (errno) { /* failed? */
  284.     case EAGAIN: /* DG brain damage */
  285.     case EINPROGRESS: /* what we expect to happen */
  286.     case EISCONN: /* restart after interrupt? */
  287.     case EADDRINUSE: /* restart after interrupt? */
  288.       break; /* well, not really, it was interrupted */
  289.     default:
  290.       sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
  291.       close (sock); /* flush socket */
  292.       return -1;
  293.     }
  294.     now = time (0); /* open timeout */
  295.     ti = ttmo_open ? now + ttmo_open : 0;
  296.     tmo.tv_usec = 0;
  297.     FD_ZERO (&fds); /* initialize selection vector */
  298.     FD_ZERO (&efds); /* handle errors too */
  299.     FD_SET (sock,&fds); /* block for error or writeable */
  300.     FD_SET (sock,&efds);
  301.     do { /* block under timeout */
  302.       tmo.tv_sec = ti ? ti - now : 0;
  303.       i = select (sock+1,0,&fds,&efds,ttmo_open ? &tmo : 0);
  304.       now = time (0);
  305.     } while (((i < 0) && (errno == EINTR)) || (ti && !i && (ti > now)));
  306.     if (i > 0) { /* success, make sure really connected */
  307.       fcntl (sock,F_SETFL,flgs);/* restore blocking status */
  308.       /* This used to be a zero-byte read(), but that crashes Solaris */
  309. /* get socket status */
  310.       while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
  311.     }
  312.     if (i <= 0) { /* timeout or error? */
  313.       i = i ? errno : ETIMEDOUT;/* determine error code */
  314.       close (sock); /* flush socket */
  315.       errno = i; /* return error code */
  316.       sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port,
  317.        strerror (errno));
  318.       return -1;
  319.     }
  320.   }
  321.   return sock; /* return the socket */
  322. }
  323.   
  324. /* TCP/IP authenticated open
  325.  * Accepts: host name
  326.  *     service name
  327.  *     returned user name buffer
  328.  * Returns: TCP/IP stream if success else NIL
  329.  */
  330. #define MAXARGV 20
  331. TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
  332. {
  333.   TCPSTREAM *stream = NIL;
  334.   struct hostent *he;
  335.   char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1];
  336.   int i,ti,pipei[2],pipeo[2];
  337.   time_t now;
  338.   struct timeval tmo;
  339.   fd_set fds,efds;
  340.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  341.   void *data;
  342.   if (*service == '*') { /* want ssh? */
  343. /* return immediately if ssh disabled */
  344.     if (!(sshpath && (ti = sshtimeout))) return NIL;
  345. /* ssh command prototype defined yet? */
  346.     if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
  347.   }
  348.   else if (ti = rshtimeout) { /* set rsh timeout */
  349. /* rsh path/command prototypes defined yet? */
  350.     if (!rshpath) rshpath = cpystr (RSHPATH);
  351.     if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
  352.   }
  353.   else return NIL; /* rsh disabled */
  354. /* look like domain literal? */
  355.   if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') {
  356.     strcpy (host,mb->host+1); /* yes, copy without brackets */
  357.     host[i-1] = '';
  358.     if (inet_addr (host) == -1) {
  359.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  360.       mm_log (tmp,ERROR);
  361.       return NIL;
  362.     }
  363.   }
  364.   else { /* note that Unix requires lowercase! */
  365.     (*bn) (BLOCK_DNSLOOKUP,NIL);
  366.     data = (*bn) (BLOCK_SENSITIVE,NIL);
  367.     if (he = gethostbyname (lcase (strcpy (host,mb->host))))
  368.       strcpy (host,he->h_name);
  369.     (*bn) (BLOCK_NONSENSITIVE,data);
  370.     (*bn) (BLOCK_NONE,NIL);
  371.   }
  372.   if (*service == '*') /* build ssh command */
  373.     sprintf (tmp,sshcommand,sshpath,host,
  374.      mb->user[0] ? mb->user : myusername (),service + 1);
  375.   else sprintf (tmp,rshcommand,rshpath,host,
  376. mb->user[0] ? mb->user : myusername (),service);
  377.   for (i = 1,path = argv[0] = strtok (tmp," ");
  378.        (i < MAXARGV) && (argv[i] = strtok (NIL," ")); i++);
  379.   argv[i] = NIL; /* make sure argv tied off */
  380. /* make command pipes */
  381.   if (pipe (pipei) < 0) return NIL;
  382.   if (pipe (pipeo) < 0) {
  383.     close (pipei[0]); close (pipei[1]);
  384.     return NIL;
  385.   }
  386.   (*bn) (BLOCK_TCPOPEN,NIL); /* quell alarm up here for NeXT */
  387.   if ((i = fork ()) < 0) { /* make inferior process */
  388.     close (pipei[0]); close (pipei[1]);
  389.     close (pipeo[0]); close (pipeo[1]);
  390.     return NIL;
  391.   }
  392.   if (!i) { /* if child */
  393.     alarm (0); /* never have alarms in children */
  394.     if (!fork ()) { /* make grandchild so it's inherited by init */
  395.       int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
  396.       dup2 (pipei[1],1); /* parent's input is my output */
  397.       dup2 (pipei[1],2); /* parent's input is my error output too */
  398.       dup2 (pipeo[0],0); /* parent's output is my input */
  399. /* close all unnecessary descriptors */
  400.       for (i = 3; i <= maxfd; i++) close (i);
  401.       setpgrp (0,getpid ()); /* be our own process group */
  402.       execv (path,argv); /* now run it */
  403.     }
  404.     _exit (1); /* child is done */
  405.   }
  406.   grim_pid_reap (i,NIL); /* reap child; grandchild now owned by init */
  407.   close (pipei[1]); /* close child's side of the pipes */
  408.   close (pipeo[0]);
  409. /* create TCP/IP stream */
  410.   stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
  411.  sizeof (TCPSTREAM));
  412. /* copy remote host name from argument */
  413.   stream->remotehost = cpystr (stream->host = cpystr (host));
  414.   stream->tcpsi = pipei[0]; /* init sockets */
  415.   stream->tcpso = pipeo[1];
  416.   stream->ictr = 0; /* init input counter */
  417.   stream->port = 0xffffffff; /* no port number */
  418.   now = time (0); /* open timeout */
  419.   if (ti) ti += now;
  420.   tmo.tv_usec = 0; /* initialize usec timeout */
  421.   FD_ZERO (&fds); /* initialize selection vector */
  422.   FD_ZERO (&efds); /* handle errors too */
  423.   FD_SET (stream->tcpsi,&fds); /* set bit in selection vector */
  424.   FD_SET (stream->tcpsi,&efds); /* set bit in error selection vector */
  425.   FD_SET (stream->tcpso,&efds); /* set bit in error selection vector */
  426.   do { /* block under timeout */
  427.     tmo.tv_sec = ti ? ti - now : 0;
  428.     i = select (max (stream->tcpsi,stream->tcpso)+1,&fds,0,&efds,&tmo);
  429.     now = time (0);
  430.   } while (((i < 0) && (errno == EINTR)) || (ti && !i && (ti > now)));
  431.   if (i <= 0) { /* timeout or error? */
  432.     sprintf (tmp,i ? "error in %s to IMAP server" :
  433.      "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh");
  434.     mm_log (tmp,WARN);
  435.     tcp_close (stream); /* punt stream */
  436.     stream = NIL;
  437.   }
  438.   (*bn) (BLOCK_NONE,NIL);
  439. /* return user name */
  440.   strcpy (usrbuf,mb->user[0] ? mb->user : myusername ());
  441.   return stream; /* return success */
  442. }
  443. /* TCP/IP receive line
  444.  * Accepts: TCP/IP stream
  445.  * Returns: text line string or NIL if failure
  446.  */
  447. char *tcp_getline (TCPSTREAM *stream)
  448. {
  449.   int n,m;
  450.   char *st,*ret,*stp;
  451.   char c = '';
  452.   char d;
  453. /* make sure have data */
  454.   if (!tcp_getdata (stream)) return NIL;
  455.   st = stream->iptr; /* save start of string */
  456.   n = 0; /* init string count */
  457.   while (stream->ictr--) { /* look for end of line */
  458.     d = *stream->iptr++; /* slurp another character */
  459.     if ((c == '15') && (d == '12')) {
  460.       ret = (char *) fs_get (n--);
  461.       memcpy (ret,st,n); /* copy into a free storage string */
  462.       ret[n] = ''; /* tie off string with null */
  463.       return ret;
  464.     }
  465.     n++; /* count another character searched */
  466.     c = d; /* remember previous character */
  467.   }
  468. /* copy partial string from buffer */
  469.   memcpy ((ret = stp = (char *) fs_get (n)),st,n);
  470. /* get more data from the net */
  471.   if (!tcp_getdata (stream)) fs_give ((void **) &ret);
  472. /* special case of newline broken by buffer */
  473.   else if ((c == '15') && (*stream->iptr == '12')) {
  474.     stream->iptr++; /* eat the line feed */
  475.     stream->ictr--;
  476.     ret[n - 1] = ''; /* tie off string with null */
  477.   }
  478. /* else recurse to get remainder */
  479.   else if (st = tcp_getline (stream)) {
  480.     ret = (char *) fs_get (n + 1 + (m = strlen (st)));
  481.     memcpy (ret,stp,n); /* copy first part */
  482.     memcpy (ret + n,st,m); /* and second part */
  483.     fs_give ((void **) &stp); /* flush first part */
  484.     fs_give ((void **) &st); /* flush second part */
  485.     ret[n + m] = ''; /* tie off string with null */
  486.   }
  487.   return ret;
  488. }
  489. /* TCP/IP receive buffer
  490.  * Accepts: TCP/IP stream
  491.  *     size in bytes
  492.  *     buffer to read into
  493.  * Returns: T if success, NIL otherwise
  494.  */
  495. long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
  496. {
  497.   unsigned long n;
  498.   char *bufptr = buffer;
  499.   while (size > 0) { /* until request satisfied */
  500.     if (!tcp_getdata (stream)) return NIL;
  501.     n = min (size,stream->ictr);/* number of bytes to transfer */
  502. /* do the copy */
  503.     memcpy (bufptr,stream->iptr,n);
  504.     bufptr += n; /* update pointer */
  505.     stream->iptr +=n;
  506.     size -= n; /* update # of bytes to do */
  507.     stream->ictr -=n;
  508.   }
  509.   bufptr[0] = ''; /* tie off string */
  510.   return T;
  511. }
  512. /* TCP/IP receive data
  513.  * Accepts: TCP/IP stream
  514.  * Returns: T if success, NIL otherwise
  515.  */
  516. long tcp_getdata (TCPSTREAM *stream)
  517. {
  518.   int i;
  519.   fd_set fds,efds;
  520.   struct timeval tmo;
  521.   time_t t = time (0);
  522.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  523.   if (stream->tcpsi < 0) return NIL;
  524.   (*bn) (BLOCK_TCPREAD,NIL);
  525.   while (stream->ictr < 1) { /* if nothing in the buffer */
  526.     time_t tl = time (0); /* start of request */
  527.     time_t now = tl;
  528.     int ti = ttmo_read ? now + ttmo_read : 0;
  529.     tmo.tv_usec = 0;
  530.     FD_ZERO (&fds); /* initialize selection vector */
  531.     FD_ZERO (&efds); /* handle errors too */
  532.     FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
  533.     FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
  534.     errno = NIL; /* block and read */
  535.     do { /* block under timeout */
  536.       tmo.tv_sec = ti ? ti - now : 0;
  537.       i = select (stream->tcpsi+1,&fds,0,&efds,ttmo_read ? &tmo : 0);
  538.       now = time (0);
  539.     } while (((i < 0) && (errno == EINTR)) || (ti && !i && (ti > now)));
  540.     if (!i) { /* timeout? */
  541.       time_t tc = time (0);
  542.       if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
  543.       else return tcp_abort (stream);
  544.     }
  545.     else if (i < 0) return tcp_abort (stream);
  546.     while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
  547.    (errno == EINTR));
  548.     if (i < 1) return tcp_abort (stream);
  549.     stream->iptr = stream->ibuf;/* point at TCP buffer */
  550.     stream->ictr = i; /* set new byte count */
  551.   }
  552.   (*bn) (BLOCK_NONE,NIL);
  553.   return T;
  554. }
  555. /* TCP/IP send string as record
  556.  * Accepts: TCP/IP stream
  557.  *     string pointer
  558.  * Returns: T if success else NIL
  559.  */
  560. long tcp_soutr (TCPSTREAM *stream,char *string)
  561. {
  562.   return tcp_sout (stream,string,(unsigned long) strlen (string));
  563. }
  564. /* TCP/IP send string
  565.  * Accepts: TCP/IP stream
  566.  *     string pointer
  567.  *     byte count
  568.  * Returns: T if success else NIL
  569.  */
  570. long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
  571. {
  572.   int i;
  573.   fd_set fds,efds;
  574.   struct timeval tmo;
  575.   time_t t = time (0);
  576.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  577.   if (stream->tcpso < 0) return NIL;
  578.   (*bn) (BLOCK_TCPWRITE,NIL);
  579.   while (size > 0) { /* until request satisfied */
  580.     time_t tl = time (0); /* start of request */
  581.     time_t now = tl;
  582.     int ti = ttmo_write ? now + ttmo_write : 0;
  583.     tmo.tv_usec = 0;
  584.     FD_ZERO (&fds); /* initialize selection vector */
  585.     FD_ZERO (&efds); /* handle errors too */
  586.     FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
  587.     FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
  588.     errno = NIL; /* block and write */
  589.     do { /* block under timeout */
  590.       tmo.tv_sec = ti ? ti - now : 0;
  591.       i = select (stream->tcpso+1,0,&fds,&efds,ttmo_write ? &tmo : 0);
  592.       now = time (0);
  593.     } while (((i < 0) && (errno == EINTR)) || (ti && !i && (ti > now)));
  594.     if (!i) { /* timeout? */
  595.       time_t tc = time (0);
  596.       if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
  597.       else return tcp_abort (stream);
  598.     }
  599.     else if (i < 0) return tcp_abort (stream);
  600.     while (((i = write (stream->tcpso,string,size)) < 0) && (errno == EINTR));
  601.     if (i < 0) return tcp_abort (stream);
  602.     size -= i; /* how much we sent */
  603.     string += i;
  604.   }
  605.   (*bn) (BLOCK_NONE,NIL);
  606.   return T; /* all done */
  607. }
  608. /* TCP/IP close
  609.  * Accepts: TCP/IP stream
  610.  */
  611. void tcp_close (TCPSTREAM *stream)
  612. {
  613.   tcp_abort (stream); /* nuke the stream */
  614. /* flush host names */
  615.   if (stream->host) fs_give ((void **) &stream->host);
  616.   if (stream->remotehost) fs_give ((void **) &stream->remotehost);
  617.   if (stream->localhost) fs_give ((void **) &stream->localhost);
  618.   fs_give ((void **) &stream); /* flush the stream */
  619. }
  620. /* TCP/IP abort stream
  621.  * Accepts: TCP/IP stream
  622.  * Returns: NIL always
  623.  */
  624. long tcp_abort (TCPSTREAM *stream)
  625. {
  626.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  627.   if (stream->tcpsi >= 0) { /* no-op if no socket */
  628.     (*bn) (BLOCK_TCPCLOSE,NIL);
  629.     close (stream->tcpsi); /* nuke the socket */
  630.     if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
  631.     stream->tcpsi = stream->tcpso = -1;
  632.   }
  633.   (*bn) (BLOCK_NONE,NIL);
  634.   return NIL;
  635. }
  636. /* TCP/IP get host name
  637.  * Accepts: TCP/IP stream
  638.  * Returns: host name for this stream
  639.  */
  640. char *tcp_host (TCPSTREAM *stream)
  641. {
  642.   return stream->host; /* use tcp_remotehost() if want guarantees */
  643. }
  644. /* TCP/IP get remote host name
  645.  * Accepts: TCP/IP stream
  646.  * Returns: host name for this stream
  647.  */
  648. char *tcp_remotehost (TCPSTREAM *stream)
  649. {
  650.   if (!stream->remotehost) {
  651.     struct sockaddr_in sin;
  652.     int sinlen = sizeof (struct sockaddr_in);
  653.     stream->remotehost = /* get socket's peer name */
  654.       getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ?
  655. cpystr (stream->host) : tcp_name (&sin,NIL);
  656.   }
  657.   return stream->remotehost;
  658. }
  659. /* TCP/IP return port for this stream
  660.  * Accepts: TCP/IP stream
  661.  * Returns: port number for this stream
  662.  */
  663. unsigned long tcp_port (TCPSTREAM *stream)
  664. {
  665.   return stream->port; /* return port number */
  666. }
  667. /* TCP/IP get local host name
  668.  * Accepts: TCP/IP stream
  669.  * Returns: local host name
  670.  */
  671. char *tcp_localhost (TCPSTREAM *stream)
  672. {
  673.   if (!stream->localhost) {
  674.     struct sockaddr_in sin;
  675.     int sinlen = sizeof (struct sockaddr_in);
  676.     stream->localhost = /* get socket's name */
  677.       ((stream->port & 0xffff000) ||
  678.        getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen)) ?
  679.  cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
  680.   }
  681.   return stream->localhost; /* return local host name */
  682. }
  683. /* TCP/IP get client host name (server calls only)
  684.  * Returns: client host name
  685.  */
  686. static char *myClientHost = NIL;
  687. char *tcp_clienthost ()
  688. {
  689.   if (!myClientHost) {
  690.     struct sockaddr_in sin;
  691.     int sinlen = sizeof (struct sockaddr_in);
  692. /* get stdin's peer name */
  693.     myClientHost = getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
  694.       cpystr ("UNKNOWN") : tcp_name (&sin,T);
  695.   }
  696.   return myClientHost;
  697. }
  698. /* TCP/IP get server host name (server calls only)
  699.  * Returns: server host name
  700.  */
  701. static char *myServerHost = NIL;
  702. static long myServerPort = -1;
  703. char *tcp_serverhost ()
  704. {
  705.   if (!myServerHost) {
  706.     struct sockaddr_in sin;
  707.     int sinlen = sizeof (struct sockaddr_in);
  708. /* get stdin's name */
  709.     if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen))
  710.       myServerHost = cpystr (mylocalhost ());
  711.     else {
  712.       myServerHost = tcp_name (&sin,NIL);
  713.       myServerPort = ntohs (sin.sin_port);
  714.     }
  715.   }
  716.   return myServerHost;
  717. }
  718. /* TCP/IP get server port number (server calls only)
  719.  * Returns: server port number
  720.  */
  721. long tcp_serverport ()
  722. {
  723.   if (!myServerHost) tcp_serverhost ();
  724.   return myServerPort;
  725. }
  726. /* TCP/IP return canonical form of host name
  727.  * Accepts: host name
  728.  * Returns: canonical form of host name
  729.  */
  730. char *tcp_canonical (char *name)
  731. {
  732.   char *ret,host[MAILTMPLEN];
  733.   struct hostent *he;
  734.   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  735.   void *data;
  736. /* look like domain literal? */
  737.   if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
  738.   (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
  739.   data = (*bn) (BLOCK_SENSITIVE,NIL);
  740. /* note that Unix requires lowercase! */
  741.   ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
  742.   (*bn) (BLOCK_NONSENSITIVE,data);
  743.   (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
  744.   return ret;
  745. }
  746. /* TCP/IP return name from socket
  747.  * Accepts: socket
  748.  *     verbose flag
  749.  * Returns: cpystr name
  750.  */
  751. char *tcp_name (struct sockaddr_in *sin,long flag)
  752. {
  753.   char *s,tmp[MAILTMPLEN];
  754.   if (allowreversedns) {
  755.     struct hostent *he;
  756.     blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
  757.     void *data;
  758.     (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
  759.     data = (*bn) (BLOCK_SENSITIVE,NIL);
  760. /* translate address to name */
  761.     if (!(he = gethostbyaddr ((char *) &sin->sin_addr,
  762.       sizeof (struct in_addr),sin->sin_family)))
  763.       sprintf (s = tmp,"[%s]",inet_ntoa (sin->sin_addr));
  764.     else if (flag) sprintf (s = tmp,"%s [%s]",he->h_name,
  765.     inet_ntoa (sin->sin_addr));
  766.     else s = he->h_name;
  767.     (*bn) (BLOCK_NONSENSITIVE,data);
  768.     (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
  769.   }
  770.   else sprintf (s = tmp,"[%s]",inet_ntoa (sin->sin_addr));
  771.   return cpystr (s);
  772. }