📄 tcp_nt.c
字号:
/* ======================================================================== * Copyright 1988-2007 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * * ======================================================================== *//* * Program: Winsock TCP/IP routines * * Author: Mark Crispin from Mike Seibel's Winsock code * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 11 April 1989 * Last Edited: 13 January 2007 */#include "ip_nt.c"#define TCPMAXSEND 32768/* Private functions */int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,char *hst);static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size, long *contd);long tcp_abort (TCPSTREAM *stream);long tcp_close_socket (SOCKET *sock);char *tcp_name (struct sockaddr *sadr,long flag);char *tcp_name_valid (char *s);/* Private data */int wsa_initted = 0; /* init ? */static int wsa_sock_open = 0; /* keep track of open sockets */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 allowreversedns = T;/* allow reverse DNS lookup */static long tcpdebug = NIL; /* extra TCP debugging telemetry */static char *myClientAddr = NIL;/* client host address */static char *myClientHost = NIL;/* client host name */static long myClientPort = -1; /* client port */static char *myServerAddr = NIL;/* server host address */static char *myServerHost = NIL;/* server host name */static long myServerPort = -1; /* server port */extern long maxposint; /* get this from write.c *//* 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 ? (long) value : (long) WSA_INFINITE; 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_TCPDEBUG: tcpdebug = (long) value; case GET_TCPDEBUG: ret = (void *) tcpdebug; 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,family; SOCKET sock = INVALID_SOCKET; int silent = (port & NET_SILENT) ? T : NIL; char *s,*hostname,tmp[MAILTMPLEN]; void *adr,*next; size_t adrlen; struct servent *sv = NIL; blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); if (!wsa_initted++) { /* init Windows Sockets */ WSADATA wsock; if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) { wsa_initted = 0; /* in case we try again */ sprintf (tmp,"Unable to start Windows Sockets (%d)",i); mm_log (tmp,ERROR); return NIL; } } port &= 0xffff; /* erase flags */ /* lookup service */ if (service && (sv = getservbyname (service,"tcp"))) port = ntohs (sv->s_port); /* The domain literal form is used (rather than simply the dotted decimal as with other Windows 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 (tmp,host+1); /* yes, copy number part */ tmp[strlen (tmp)-1] = '\0'; if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) { (*bn) (BLOCK_TCPOPEN,NIL); sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp, hostname = host); (*bn) (BLOCK_NONE,NIL); fs_give ((void **) &adr); } else sprintf (tmp,"Bad format domain-literal: %.80s",host); } else { /* lookup host name */ if (tcpdebug) { sprintf (tmp,"DNS resolution %.80s",host); mm_log (tmp,TCPDEBUG); } (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next))) sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host); (*bn) (BLOCK_NONE,NIL); if (s) { /* DNS resolution won? */ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG); wsa_sock_open++; /* prevent tcp_close_socket() from freeing in loop */ do { (*bn) (BLOCK_TCPOPEN,NIL); if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port, tmp,hostname)) == INVALID_SOCKET) && (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) && !silent) mm_log (tmp,WARN); (*bn) (BLOCK_NONE,NIL); } while ((sock == INVALID_SOCKET) && s); wsa_sock_open--; /* undo protection */ } } if (sock == INVALID_SOCKET) { /* do possible cleanup action */ if (!silent) mm_log (tmp,ERROR); tcp_close_socket (&sock); } else { /* got a socket, create TCP/IP stream */ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0, sizeof (TCPSTREAM)); stream->port = port; /* port number */ /* init socket */ stream->tcpsi = stream->tcpso = sock; stream->ictr = 0; /* init input counter */ /* copy official host name */ stream->host = cpystr (hostname); if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG); } return stream; /* return success */}/* Open a TCP socket * Accepts: protocol family * address to connect to * address length * port * scratch buffer * host name * Returns: socket if success, else SOCKET_ERROR with error string in scratch */int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port, char *tmp,char *hst){ int sock,err; char *s,errmsg[100]; size_t len; DWORD eo; WSAEVENT event; WSANETWORKEVENTS events; unsigned long cmd = 0; struct protoent *pt = getprotobyname ("tcp"); struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len); sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr)); mm_log (tmp,NIL); /* get a TCP stream */ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) == INVALID_SOCKET) sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ()); else { /* On Windows, FD_SETSIZE is the number of descriptors which can be * held in an fd_set, as opposed to the maximum descriptor value on UNIX. * Similarly, an fd_set in Windows is a vector of descriptor values, as * opposed to a bitmask of set fds on UNIX. Thus, an fd_set can hold up * to FD_SETSIZE values which can be larger than FD_SETSIZE, and the test * that is used on UNIX is unnecessary here. */ wsa_sock_open++; /* count this socket as open */ /* set socket nonblocking */ if (ttmo_open) WSAEventSelect (sock,event = WSACreateEvent (),FD_CONNECT); else event = 0; /* no event */ /* open connection */ err = (connect (sock,sadr,len) == SOCKET_ERROR) ? WSAGetLastError () : NIL; /* if timer in effect, wait for event */ if (event) while (err == WSAEWOULDBLOCK) switch (eo = WSAWaitForMultipleEvents (1,&event,T,ttmo_open*1000,NIL)) { case WSA_WAIT_EVENT_0: /* got an event? */ err = (WSAEnumNetworkEvents (sock,event,&events) == SOCKET_ERROR) ? WSAGetLastError () : events.iErrorCode[FD_CONNECT_BIT]; break; case WSA_WAIT_IO_COMPLETION: break; /* fAlertable is NIL so shouldn't happen */ default: /* all other conditions */ err = eo; /* error from WSAWaitForMultipleEvents() */ break; } switch (err) { /* analyze result from connect and wait */ case 0: /* got a connection */ s = NIL; if (event) { /* unset blocking mode */ WSAEventSelect (sock,event,NIL); if (ioctlsocket (sock,FIONBIO,&cmd) == SOCKET_ERROR) sprintf (s = errmsg,"Can't set blocking mode (%d)", WSAGetLastError ()); } break; case WSAECONNREFUSED: s = "Refused"; break; case WSAENOBUFS: s = "Insufficient system resources"; break; case WSA_WAIT_TIMEOUT: case WSAETIMEDOUT: s = "Timed out"; break; case WSAEHOSTUNREACH: s = "Host unreachable"; break; default: /* horrible error 69 */ sprintf (s = errmsg,"Unknown error (%d)",err); break; } /* flush event */ if (event) WSACloseEvent (event); if (s) { /* got an error? */ sprintf (tmp,"Can't connect to %.80s,%ld: %.80s",hst,port,s); tcp_close_socket (&sock); /* flush socket */ sock = INVALID_SOCKET; } } fs_give ((void **) &sadr); /* and socket address */ return sock; /* return the socket */} /* TCP/IP authenticated open * Accepts: NETMBX specifier * service name * returned user name buffer * Returns: TCP/IP stream if success else NIL */TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf){ return NIL; /* always NIL on Windows */}/* TCP receive line * Accepts: TCP stream * Returns: text line string or NIL if failure */char *tcp_getline (TCPSTREAM *stream){ unsigned long n,contd; char *ret = tcp_getline_work (stream,&n,&contd); if (ret && contd) { /* got a line needing continuation? */ STRINGLIST *stl = mail_newstringlist (); STRINGLIST *stc = stl; do { /* collect additional lines */ stc->text.data = (unsigned char *) ret; stc->text.size = n; stc = stc->next = mail_newstringlist (); ret = tcp_getline_work (stream,&n,&contd); } while (ret && contd); if (ret) { /* stash final part of line on list */ stc->text.data = (unsigned char *) ret; stc->text.size = n; /* determine how large a buffer we need */ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size; ret = fs_get (n + 1); /* copy parts into buffer */ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next) memcpy (ret + n,stc->text.data,stc->text.size); ret[n] = '\0'; } mail_free_stringlist (&stl);/* either way, done with list */ } return ret;}/* TCP receive line or partial line * Accepts: TCP stream * pointer to return size * pointer to return continuation flag * Returns: text line string, size and continuation flag, or NIL if failure */static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size, long *contd){ unsigned long n; char *s,*ret,c,d; *contd = NIL; /* assume no continuation */ /* make sure have data */ if (!tcp_getdata (stream)) return NIL; for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) { d = *stream->iptr++; /* slurp another character */ if ((c == '\015') && (d == '\012')) { ret = (char *) fs_get (n--); memcpy (ret,s,*size = n); /* copy into a free storage string */ ret[n] = '\0'; /* tie off string with null */ return ret; } } /* copy partial string from buffer */ memcpy ((ret = (char *) fs_get (n)),s,*size = n); /* get more data from the net */ if (!tcp_getdata (stream)) fs_give ((void **) &ret); /* special case of newline broken by buffer */ else if ((c == '\015') && (*stream->iptr == '\012')) { stream->iptr++; /* eat the line feed */ stream->ictr--; ret[*size = --n] = '\0'; /* tie off string with null */ } else *contd = LONGT; /* continuation needed */ return ret;}/* TCP/IP receive buffer * Accepts: TCP/IP stream * size in bytes * buffer to read into * Returns: T if success, NIL otherwise */long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s){ unsigned long n; /* make sure socket still alive */ if (stream->tcpsi == INVALID_SOCKET) return NIL; /* can transfer bytes from buffer? */ if (n = min (size,stream->ictr)) { memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */ s += n; /* update pointer */ stream->iptr +=n; size -= n; /* update # of bytes to do */ stream->ictr -=n; } if (size) { int i; fd_set fds,efds; struct timeval tmo; time_t t = time (0); blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); (*bn) (BLOCK_TCPREAD,NIL); while (size > 0) { /* until request satisfied */ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG); /* simple case if not a socket */ if (stream->tcpsi != stream->tcpso) while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) && (errno == EINTR)); else { /* socket case */ time_t tl = time (0); time_t now = tl; time_t ti = ttmo_read ? now + ttmo_read : 0; tmo.tv_usec = 0; FD_ZERO (&fds); /* initialize selection vector */ FD_ZERO (&efds); /* handle errors too */ /* set bit in selection vectors */ FD_SET (stream->tcpsi,&fds); FD_SET (stream->tcpsi,&efds); errno = NIL; /* initially no error */ do { /* block under timeout */ tmo.tv_sec = (long) (ti ? ti - now : 0); i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL); now = time (0); /* fake timeout if interrupt & time expired */ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti && (ti <= now)) i = 0; } while ((i < 0) && (errno == WSAEINTR)); /* success from select, read what we can */ if (i > 0) while (((i = recv (stream->tcpsi,s, (int) min (maxposint,size),0)) == SOCKET_ERROR) && ((errno = WSAGetLastError ()) == WSAEINTR)); else if (!i) { /* timeout, ignore if told to resume */ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue; /* otherwise punt */ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG); return tcp_abort (stream); } } if (i <= 0) { /* error seen? */ if (tcpdebug) { char tmp[MAILTMPLEN]; if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno); else s = "TCP buffer read end of file"; mm_log (s,TCPDEBUG); } return tcp_abort (stream); } s += i; /* point at new place to write */ size -= i; /* reduce byte count */ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG); } (*bn) (BLOCK_NONE,NIL); } *s = '\0'; /* tie off string */ return LONGT;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -