📄 tcp_unix.c
字号:
/* * Program: UNIX TCP/IP routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 1 August 1988 * Last Edited: 17 October 2000 * * Copyright 2000 by the University of Washington * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appears in all copies and that both the * above copyright notice and this permission notice appear in supporting * documentation, and that the name of the University of Washington not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. This software is made available * "as is", and * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */#undef write /* don't use redefined write() */ static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */static long ttmo_open = 0; /* TCP timeouts, in seconds */static long ttmo_read = 0;static long ttmo_write = 0;static long rshtimeout = 15; /* rsh timeout */static char *rshcommand = NIL; /* rsh command */static char *rshpath = NIL; /* rsh path */static long sshtimeout = 15; /* ssh timeout */static char *sshcommand = NIL; /* ssh command */static char *sshpath = NIL; /* ssh path */static long allowreversedns = /* allow reverse DNS lookup */#ifdef DISABLE_REVERSE_DNS_LOOKUP NIL /* Not recommended, especially if using Kerberos authentication */#else T#endif ;extern long maxposint; /* get this from write.c *//* Local function prototypes */int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst, unsigned long port);long tcp_abort (TCPSTREAM *stream);char *tcp_name (struct sockaddr_in *sin,long flag);/* TCP/IP manipulate parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */void *tcp_parameters (long function,void *value){ void *ret = NIL; switch ((int) function) { case SET_TIMEOUT: tmoh = (tcptimeout_t) value; case GET_TIMEOUT: ret = (void *) tmoh; break; case SET_OPENTIMEOUT: ttmo_open = (long) value; case GET_OPENTIMEOUT: ret = (void *) ttmo_open; break; case SET_READTIMEOUT: ttmo_read = (long) value; case GET_READTIMEOUT: ret = (void *) ttmo_read; break; case SET_WRITETIMEOUT: ttmo_write = (long) value; case GET_WRITETIMEOUT: ret = (void *) ttmo_write; break; case SET_ALLOWREVERSEDNS: allowreversedns = (long) value; case GET_ALLOWREVERSEDNS: ret = (void *) allowreversedns; break; case SET_RSHTIMEOUT: rshtimeout = (long) value; case GET_RSHTIMEOUT: ret = (void *) rshtimeout; break; case SET_RSHCOMMAND: if (rshcommand) fs_give ((void **) &rshcommand); rshcommand = cpystr ((char *) value); case GET_RSHCOMMAND: ret = (void *) rshcommand; break; case SET_RSHPATH: if (rshpath) fs_give ((void **) &rshpath); rshpath = cpystr ((char *) value); case GET_RSHPATH: ret = (void *) rshpath; break; case SET_SSHTIMEOUT: sshtimeout = (long) value; case GET_SSHTIMEOUT: ret = (void *) sshtimeout; break; case SET_SSHCOMMAND: if (sshcommand) fs_give ((void **) &sshcommand); sshcommand = cpystr ((char *) value); case GET_SSHCOMMAND: ret = (void *) sshcommand; break; case SET_SSHPATH: if (sshpath) fs_give ((void **) &sshpath); sshpath = cpystr ((char *) value); case GET_SSHPATH: ret = (void *) sshpath; break; } return ret;}/* TCP/IP open * Accepts: host name * contact service name * contact port number and optional silent flag * Returns: TCP/IP stream if success else NIL */TCPSTREAM *tcp_open (char *host,char *service,unsigned long port){ TCPSTREAM *stream = NIL; int i,sock; int ctr = 0; int silent = (port & NET_SILENT) ? T : NIL; int *ctrp = &ctr; char *s; struct sockaddr_in sin; struct hostent *he; char hostname[MAILTMPLEN]; char tmp[MAILTMPLEN]; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data; /* don't do open timeout if this requested */ if (port & NET_NOOPENTIMEOUT) ctrp = NIL; port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sin.sin_port = sv->s_port); /* copy port number in network format */ else sin.sin_port = htons (port); /* The domain literal form is used (rather than simply the dotted decimal as with other Unix programs) because it has to be a valid "host name" in mailsystem terminology. */ /* look like domain literal? */ if (host[0] == '[' && host[(strlen (host))-1] == ']') { strcpy (hostname,host+1); /* yes, copy number part */ hostname[(strlen (hostname))-1] = '\0'; if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) { sin.sin_family = AF_INET; /* family is always Internet */ strcpy (hostname,host); /* hostname is user's argument */ /* get an open socket for this system */ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); } else { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* lookup host name */ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */ data = (*bn) (BLOCK_SENSITIVE,NIL); if (he = gethostbyname (lcase (strcpy (hostname,host)))) { (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); /* copy address type */ sin.sin_family = he->h_addrtype; /* copy host name */ strcpy (hostname,he->h_name);#ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */ for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) { if (i && !silent) mm_log (tmp,WARN); memcpy (&sin.sin_addr,s,he->h_length); (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); (*bn) (BLOCK_NONE,NIL); }#else /* the one true address then */ memcpy (&sin.sin_addr,he->h_addr,he->h_length); (*bn) (BLOCK_DNSLOOKUP,NIL); sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port); (*bn) (BLOCK_NONE,NIL);#endif } else { (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); sprintf (tmp,"No such host as %.80s",host); mm_log (tmp,ERROR); return NIL; } } if (sock >= 0) { /* won */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init sockets */ stream->tcpsi = stream->tcpso = sock; /* stash in the snuck-in byte */ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0]; /* copy official host name */ stream->host = cpystr (hostname); } else if (!silent) mm_log (tmp,ERROR); return stream; /* return success */}/* Open a TCP socket * Accepts: Internet socket address block * scratch buffer * pointer to "first byte read in" storage or NIL * host name for error message * port number for error message * Returns: socket if success, else -1 with error string in scratch buffer */int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst, unsigned long port){ int i,ti,sock,flgs; time_t now; struct protoent *pt = getprotobyname ("ip"); fd_set fds,efds; struct timeval tmo; sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr)); mm_log (tmp,NIL); /* make a socket */ if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) { sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno)); return -1; } if (!ctr) { /* no open timeout wanted */ /* open connection */ while ((i = connect (sock,(struct sockaddr *) sin, sizeof (struct sockaddr_in))) < 0 && errno == EINTR); if (i < 0) { /* failed? */ sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno)); close (sock); /* flush socket */ return -1; } } else { /* want open timeout */ /* get current socket flags */ flgs = fcntl (sock,F_GETFL,0); /* set non-blocking */ fcntl (sock,F_SETFL,flgs | FNDELAY); /* open connection */ while ((i = connect (sock,(struct sockaddr *) sin, sizeof (struct sockaddr_in))) < 0 && errno == EINTR); if (i < 0) switch (errno) { /* failed? */ case EAGAIN: /* DG brain damage */ case EINPROGRESS: /* what we expect to happen */ case EISCONN: /* restart after interrupt? */ case EADDRINUSE: /* restart after interrupt? */ break; /* well, not really, it was interrupted */ default: sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno)); close (sock); /* flush socket */ return -1; } now = time (0); /* open timeout */ ti = ttmo_open ? now + ttmo_open : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ FD_SET (sock,&fds); /* block for error or writeable */ FD_SET (sock,&efds); do { /* block under timeout */ tmo.tv_sec = ti ? ti - now : 0; i = select (sock+1,0,&fds,&efds,ti ? &tmo : 0); now = time (0); } while (((i < 0) && (errno == EINTR)) || (ti && !i && (ti > now))); if (i > 0) { /* success, make sure really connected */ fcntl (sock,F_SETFL,flgs);/* restore blocking status */ /* This used to be a zero-byte read(), but that crashes Solaris */ /* get socket status */ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR)); } if (i <= 0) { /* timeout or error? */ i = i ? errno : ETIMEDOUT;/* determine error code */ close (sock); /* flush socket */ errno = i; /* return error code */ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port, strerror (errno)); return -1; } } for (i = 65536; (i > 4096) && /* set receive buffer size to 64K */ setsockopt (sock,SOL_SOCKET,SO_RCVBUF,(void *) &i,sizeof(i)); i -= 1024); for (i = 65536; (i > 4096) && /* set send buffer size to 64K */ setsockopt (sock,SOL_SOCKET,SO_SNDBUF,(void *) &i,sizeof(i)); i -= 1024); return sock; /* return the socket */} /* TCP/IP authenticated open * Accepts: host name * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */#define MAXARGV 20TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf){ TCPSTREAM *stream = NIL; struct hostent *he; char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1]; int i,ti,pipei[2],pipeo[2]; time_t now; struct timeval tmo; fd_set fds,efds; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); void *data; if (*service == '*') { /* want ssh? */ /* return immediately if ssh disabled */ if (!(sshpath && (ti = sshtimeout))) return NIL; /* ssh command prototype defined yet? */ if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd"); } else if (ti = rshtimeout) { /* set rsh timeout */ /* rsh path/command prototypes defined yet? */ if (!rshpath) rshpath = cpystr (RSHPATH); if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd"); } else return NIL; /* rsh disabled */ /* look like domain literal? */ if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') { strcpy (host,mb->host+1); /* yes, copy without brackets */ host[i-1] = '\0'; if (inet_addr (host) == -1) { sprintf (tmp,"Bad format domain-literal: %.80s",host); mm_log (tmp,ERROR); return NIL; } } else { /* note that Unix requires lowercase! */ (*bn) (BLOCK_DNSLOOKUP,NIL); data = (*bn) (BLOCK_SENSITIVE,NIL); if (he = gethostbyname (lcase (strcpy (host,mb->host)))) strcpy (host,he->h_name); (*bn) (BLOCK_NONSENSITIVE,data); (*bn) (BLOCK_NONE,NIL); } if (*service == '*') /* build ssh command */ sprintf (tmp,sshcommand,sshpath,host, mb->user[0] ? mb->user : myusername (),service + 1); else sprintf (tmp,rshcommand,rshpath,host, mb->user[0] ? mb->user : myusername (),service); for (i = 1,path = argv[0] = strtok (tmp," "); (i < MAXARGV) && (argv[i] = strtok (NIL," ")); i++); argv[i] = NIL; /* make sure argv tied off */ /* make command pipes */ if (pipe (pipei) < 0) return NIL; if (pipe (pipeo) < 0) { close (pipei[0]); close (pipei[1]); return NIL; } (*bn) (BLOCK_TCPOPEN,NIL); /* quell alarm up here for NeXT */ if ((i = fork ()) < 0) { /* make inferior process */ close (pipei[0]); close (pipei[1]); close (pipeo[0]); close (pipeo[1]); return NIL; } if (!i) { /* if child */ alarm (0); /* never have alarms in children */ if (!fork ()) { /* make grandchild so it's inherited by init */ int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1]))); dup2 (pipei[1],1); /* parent's input is my output */ dup2 (pipei[1],2); /* parent's input is my error output too */ dup2 (pipeo[0],0); /* parent's output is my input */ /* close all unnecessary descriptors */ for (i = 3; i <= maxfd; i++) close (i); setpgrp (0,getpid ()); /* be our own process group */ execv (path,argv); /* now run it */ } _exit (1); /* child is done */ } grim_pid_reap (i,NIL); /* reap child; grandchild now owned by init */ close (pipei[1]); /* close child's side of the pipes */ close (pipeo[0]); /* create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); /* copy remote host name from argument */ stream->remotehost = cpystr (stream->host = cpystr (host)); stream->tcpsi = pipei[0]; /* init sockets */ stream->tcpso = pipeo[1]; stream->ictr = 0; /* init input counter */ stream->port = 0xffffffff; /* no port number */ ti += now = time (0); /* open timeout */ tmo.tv_usec = 0; /* initialize usec timeout */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -