imapext-2007

view src/osdep/nt/tcp_nt.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line source
1 /* ========================================================================
2 * Copyright 1988-2007 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
14 /*
15 * Program: Winsock TCP/IP routines
16 *
17 * Author: Mark Crispin from Mike Seibel's Winsock code
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 11 April 1989
26 * Last Edited: 13 January 2007
27 */
29 #include "ip_nt.c"
32 #define TCPMAXSEND 32768
34 /* Private functions */
36 int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
37 char *tmp,char *hst);
38 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
39 long *contd);
40 long tcp_abort (TCPSTREAM *stream);
41 long tcp_close_socket (SOCKET *sock);
42 char *tcp_name (struct sockaddr *sadr,long flag);
43 char *tcp_name_valid (char *s);
46 /* Private data */
48 int wsa_initted = 0; /* init ? */
49 static int wsa_sock_open = 0; /* keep track of open sockets */
50 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
51 static long ttmo_open = 0; /* TCP timeouts, in seconds */
52 static long ttmo_read = 0;
53 static long ttmo_write = 0;
54 static long allowreversedns = T;/* allow reverse DNS lookup */
55 static long tcpdebug = NIL; /* extra TCP debugging telemetry */
56 static char *myClientAddr = NIL;/* client host address */
57 static char *myClientHost = NIL;/* client host name */
58 static long myClientPort = -1; /* client port */
59 static char *myServerAddr = NIL;/* server host address */
60 static char *myServerHost = NIL;/* server host name */
61 static long myServerPort = -1; /* server port */
63 extern long maxposint; /* get this from write.c */
65 /* TCP/IP manipulate parameters
66 * Accepts: function code
67 * function-dependent value
68 * Returns: function-dependent return value
69 */
71 void *tcp_parameters (long function,void *value)
72 {
73 void *ret = NIL;
74 switch ((int) function) {
75 case SET_TIMEOUT:
76 tmoh = (tcptimeout_t) value;
77 case GET_TIMEOUT:
78 ret = (void *) tmoh;
79 break;
80 case SET_OPENTIMEOUT:
81 ttmo_open = (long) value ? (long) value : (long) WSA_INFINITE;
82 case GET_OPENTIMEOUT:
83 ret = (void *) ttmo_open;
84 break;
85 case SET_READTIMEOUT:
86 ttmo_read = (long) value;
87 case GET_READTIMEOUT:
88 ret = (void *) ttmo_read;
89 break;
90 case SET_WRITETIMEOUT:
91 ttmo_write = (long) value;
92 case GET_WRITETIMEOUT:
93 ret = (void *) ttmo_write;
94 break;
95 case SET_ALLOWREVERSEDNS:
96 allowreversedns = (long) value;
97 case GET_ALLOWREVERSEDNS:
98 ret = (void *) allowreversedns;
99 break;
100 case SET_TCPDEBUG:
101 tcpdebug = (long) value;
102 case GET_TCPDEBUG:
103 ret = (void *) tcpdebug;
104 break;
105 }
106 return ret;
107 }
109 /* TCP/IP open
110 * Accepts: host name
111 * contact service name
112 * contact port number and optional silent flag
113 * Returns: TCP/IP stream if success else NIL
114 */
116 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
117 {
118 TCPSTREAM *stream = NIL;
119 int i,family;
120 SOCKET sock = INVALID_SOCKET;
121 int silent = (port & NET_SILENT) ? T : NIL;
122 char *s,*hostname,tmp[MAILTMPLEN];
123 void *adr,*next;
124 size_t adrlen;
125 struct servent *sv = NIL;
126 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
127 if (!wsa_initted++) { /* init Windows Sockets */
128 WSADATA wsock;
129 if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) {
130 wsa_initted = 0; /* in case we try again */
131 sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
132 mm_log (tmp,ERROR);
133 return NIL;
134 }
135 }
136 port &= 0xffff; /* erase flags */
137 /* lookup service */
138 if (service && (sv = getservbyname (service,"tcp")))
139 port = ntohs (sv->s_port);
140 /* The domain literal form is used (rather than simply the dotted decimal
141 as with other Windows programs) because it has to be a valid "host name"
142 in mailsystem terminology. */
143 /* look like domain literal? */
144 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
145 strcpy (tmp,host+1); /* yes, copy number part */
146 tmp[strlen (tmp)-1] = '\0';
147 if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
148 (*bn) (BLOCK_TCPOPEN,NIL);
149 sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp,
150 hostname = host);
151 (*bn) (BLOCK_NONE,NIL);
152 fs_give ((void **) &adr);
153 }
154 else sprintf (tmp,"Bad format domain-literal: %.80s",host);
155 }
157 else { /* lookup host name */
158 if (tcpdebug) {
159 sprintf (tmp,"DNS resolution %.80s",host);
160 mm_log (tmp,TCPDEBUG);
161 }
162 (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
163 if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
164 sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host);
165 (*bn) (BLOCK_NONE,NIL);
166 if (s) { /* DNS resolution won? */
167 if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
168 wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
169 loop */
170 do {
171 (*bn) (BLOCK_TCPOPEN,NIL);
172 if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port,
173 tmp,hostname)) == INVALID_SOCKET) &&
174 (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
175 !silent) mm_log (tmp,WARN);
176 (*bn) (BLOCK_NONE,NIL);
177 } while ((sock == INVALID_SOCKET) && s);
178 wsa_sock_open--; /* undo protection */
179 }
180 }
181 if (sock == INVALID_SOCKET) { /* do possible cleanup action */
182 if (!silent) mm_log (tmp,ERROR);
183 tcp_close_socket (&sock);
184 }
185 else { /* got a socket, create TCP/IP stream */
186 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
187 sizeof (TCPSTREAM));
188 stream->port = port; /* port number */
189 /* init socket */
190 stream->tcpsi = stream->tcpso = sock;
191 stream->ictr = 0; /* init input counter */
192 /* copy official host name */
193 stream->host = cpystr (hostname);
194 if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
195 }
196 return stream; /* return success */
197 }
199 /* Open a TCP socket
200 * Accepts: protocol family
201 * address to connect to
202 * address length
203 * port
204 * scratch buffer
205 * host name
206 * Returns: socket if success, else SOCKET_ERROR with error string in scratch
207 */
209 int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
210 char *tmp,char *hst)
211 {
212 int sock,err;
213 char *s,errmsg[100];
214 size_t len;
215 DWORD eo;
216 WSAEVENT event;
217 WSANETWORKEVENTS events;
218 unsigned long cmd = 0;
219 struct protoent *pt = getprotobyname ("tcp");
220 struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
221 sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
222 mm_log (tmp,NIL);
223 /* get a TCP stream */
224 if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) ==
225 INVALID_SOCKET)
226 sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ());
227 else {
228 /* On Windows, FD_SETSIZE is the number of descriptors which can be
229 * held in an fd_set, as opposed to the maximum descriptor value on UNIX.
230 * Similarly, an fd_set in Windows is a vector of descriptor values, as
231 * opposed to a bitmask of set fds on UNIX. Thus, an fd_set can hold up
232 * to FD_SETSIZE values which can be larger than FD_SETSIZE, and the test
233 * that is used on UNIX is unnecessary here.
234 */
235 wsa_sock_open++; /* count this socket as open */
236 /* set socket nonblocking */
237 if (ttmo_open) WSAEventSelect (sock,event = WSACreateEvent (),FD_CONNECT);
238 else event = 0; /* no event */
239 /* open connection */
240 err = (connect (sock,sadr,len) == SOCKET_ERROR) ? WSAGetLastError () : NIL;
241 /* if timer in effect, wait for event */
242 if (event) while (err == WSAEWOULDBLOCK)
243 switch (eo = WSAWaitForMultipleEvents (1,&event,T,ttmo_open*1000,NIL)) {
244 case WSA_WAIT_EVENT_0: /* got an event? */
245 err = (WSAEnumNetworkEvents (sock,event,&events) == SOCKET_ERROR) ?
246 WSAGetLastError () : events.iErrorCode[FD_CONNECT_BIT];
247 break;
248 case WSA_WAIT_IO_COMPLETION:
249 break; /* fAlertable is NIL so shouldn't happen */
250 default: /* all other conditions */
251 err = eo; /* error from WSAWaitForMultipleEvents() */
252 break;
253 }
255 switch (err) { /* analyze result from connect and wait */
256 case 0: /* got a connection */
257 s = NIL;
258 if (event) { /* unset blocking mode */
259 WSAEventSelect (sock,event,NIL);
260 if (ioctlsocket (sock,FIONBIO,&cmd) == SOCKET_ERROR)
261 sprintf (s = errmsg,"Can't set blocking mode (%d)",
262 WSAGetLastError ());
263 }
264 break;
265 case WSAECONNREFUSED:
266 s = "Refused";
267 break;
268 case WSAENOBUFS:
269 s = "Insufficient system resources";
270 break;
271 case WSA_WAIT_TIMEOUT: case WSAETIMEDOUT:
272 s = "Timed out";
273 break;
274 case WSAEHOSTUNREACH:
275 s = "Host unreachable";
276 break;
277 default: /* horrible error 69 */
278 sprintf (s = errmsg,"Unknown error (%d)",err);
279 break;
280 }
281 /* flush event */
282 if (event) WSACloseEvent (event);
283 if (s) { /* got an error? */
284 sprintf (tmp,"Can't connect to %.80s,%ld: %.80s",hst,port,s);
285 tcp_close_socket (&sock); /* flush socket */
286 sock = INVALID_SOCKET;
287 }
288 }
289 fs_give ((void **) &sadr); /* and socket address */
290 return sock; /* return the socket */
291 }
293 /* TCP/IP authenticated open
294 * Accepts: NETMBX specifier
295 * service name
296 * returned user name buffer
297 * Returns: TCP/IP stream if success else NIL
298 */
300 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
301 {
302 return NIL; /* always NIL on Windows */
303 }
305 /* TCP receive line
306 * Accepts: TCP stream
307 * Returns: text line string or NIL if failure
308 */
310 char *tcp_getline (TCPSTREAM *stream)
311 {
312 unsigned long n,contd;
313 char *ret = tcp_getline_work (stream,&n,&contd);
314 if (ret && contd) { /* got a line needing continuation? */
315 STRINGLIST *stl = mail_newstringlist ();
316 STRINGLIST *stc = stl;
317 do { /* collect additional lines */
318 stc->text.data = (unsigned char *) ret;
319 stc->text.size = n;
320 stc = stc->next = mail_newstringlist ();
321 ret = tcp_getline_work (stream,&n,&contd);
322 } while (ret && contd);
323 if (ret) { /* stash final part of line on list */
324 stc->text.data = (unsigned char *) ret;
325 stc->text.size = n;
326 /* determine how large a buffer we need */
327 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
328 ret = fs_get (n + 1); /* copy parts into buffer */
329 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
330 memcpy (ret + n,stc->text.data,stc->text.size);
331 ret[n] = '\0';
332 }
333 mail_free_stringlist (&stl);/* either way, done with list */
334 }
335 return ret;
336 }
338 /* TCP receive line or partial line
339 * Accepts: TCP stream
340 * pointer to return size
341 * pointer to return continuation flag
342 * Returns: text line string, size and continuation flag, or NIL if failure
343 */
345 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
346 long *contd)
347 {
348 unsigned long n;
349 char *s,*ret,c,d;
350 *contd = NIL; /* assume no continuation */
351 /* make sure have data */
352 if (!tcp_getdata (stream)) return NIL;
353 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
354 d = *stream->iptr++; /* slurp another character */
355 if ((c == '\015') && (d == '\012')) {
356 ret = (char *) fs_get (n--);
357 memcpy (ret,s,*size = n); /* copy into a free storage string */
358 ret[n] = '\0'; /* tie off string with null */
359 return ret;
360 }
361 }
362 /* copy partial string from buffer */
363 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
364 /* get more data from the net */
365 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
366 /* special case of newline broken by buffer */
367 else if ((c == '\015') && (*stream->iptr == '\012')) {
368 stream->iptr++; /* eat the line feed */
369 stream->ictr--;
370 ret[*size = --n] = '\0'; /* tie off string with null */
371 }
372 else *contd = LONGT; /* continuation needed */
373 return ret;
374 }
376 /* TCP/IP receive buffer
377 * Accepts: TCP/IP stream
378 * size in bytes
379 * buffer to read into
380 * Returns: T if success, NIL otherwise
381 */
383 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
384 {
385 unsigned long n;
386 /* make sure socket still alive */
387 if (stream->tcpsi == INVALID_SOCKET) return NIL;
388 /* can transfer bytes from buffer? */
389 if (n = min (size,stream->ictr)) {
390 memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
391 s += n; /* update pointer */
392 stream->iptr +=n;
393 size -= n; /* update # of bytes to do */
394 stream->ictr -=n;
395 }
396 if (size) {
397 int i;
398 fd_set fds,efds;
399 struct timeval tmo;
400 time_t t = time (0);
401 blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
402 (*bn) (BLOCK_TCPREAD,NIL);
403 while (size > 0) { /* until request satisfied */
404 if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
405 /* simple case if not a socket */
406 if (stream->tcpsi != stream->tcpso)
407 while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
408 (errno == EINTR));
409 else { /* socket case */
410 time_t tl = time (0);
411 time_t now = tl;
412 time_t ti = ttmo_read ? now + ttmo_read : 0;
413 tmo.tv_usec = 0;
414 FD_ZERO (&fds); /* initialize selection vector */
415 FD_ZERO (&efds); /* handle errors too */
416 /* set bit in selection vectors */
417 FD_SET (stream->tcpsi,&fds);
418 FD_SET (stream->tcpsi,&efds);
419 errno = NIL; /* initially no error */
420 do { /* block under timeout */
421 tmo.tv_sec = (long) (ti ? ti - now : 0);
422 i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
423 now = time (0); /* fake timeout if interrupt & time expired */
424 if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
425 (ti <= now)) i = 0;
426 } while ((i < 0) && (errno == WSAEINTR));
427 /* success from select, read what we can */
428 if (i > 0) while (((i = recv (stream->tcpsi,s,
429 (int) min (maxposint,size),0)) ==
430 SOCKET_ERROR) &&
431 ((errno = WSAGetLastError ()) == WSAEINTR));
432 else if (!i) { /* timeout, ignore if told to resume */
433 if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue;
434 /* otherwise punt */
435 if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
436 return tcp_abort (stream);
437 }
438 }
439 if (i <= 0) { /* error seen? */
440 if (tcpdebug) {
441 char tmp[MAILTMPLEN];
442 if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
443 else s = "TCP buffer read end of file";
444 mm_log (s,TCPDEBUG);
445 }
446 return tcp_abort (stream);
447 }
448 s += i; /* point at new place to write */
449 size -= i; /* reduce byte count */
450 if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
451 }
452 (*bn) (BLOCK_NONE,NIL);
453 }
454 *s = '\0'; /* tie off string */
455 return LONGT;
456 }
458 /* TCP/IP receive data
459 * Accepts: TCP/IP stream
460 * Returns: T if success, NIL otherwise
461 */
463 long tcp_getdata (TCPSTREAM *stream)
464 {
465 int i;
466 fd_set fds,efds;
467 struct timeval tmo;
468 time_t t = time (0);
469 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
470 if (stream->tcpsi == INVALID_SOCKET) return NIL;
471 (*bn) (BLOCK_TCPREAD,NIL);
472 while (stream->ictr < 1) { /* if nothing in the buffer */
473 if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
474 /* simple case if not a socket */
475 if (stream->tcpsi != stream->tcpso)
476 while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
477 (errno == EINTR));
478 else {
479 time_t tl = time (0);
480 time_t now = tl;
481 time_t ti = ttmo_read ? now + ttmo_read : 0;
482 tmo.tv_usec = 0;
483 FD_ZERO (&fds); /* initialize selection vector */
484 FD_ZERO (&efds); /* handle errors too */
485 /* set bit in selection vectors */
486 FD_SET (stream->tcpsi,&fds);
487 FD_SET (stream->tcpsi,&efds);
488 errno = NIL; /* initially no error */
489 do { /* block under timeout */
490 tmo.tv_sec = (long) (ti ? ti - now : 0);
491 i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
492 now = time (0); /* fake timeout if interrupt & time expired */
493 if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
494 (ti <= now)) i = 0;
495 } while ((i < 0) && (errno == WSAEINTR));
496 /* success from select, read what we can */
497 if (i > 0) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
498 SOCKET_ERROR) &&
499 ((errno = WSAGetLastError ()) == WSAEINTR));
500 else if (!i) { /* timeout, ignore if told to resume */
501 if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue;
502 /* otherwise punt */
503 if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
504 return tcp_abort (stream);
505 }
506 }
507 if (i <= 0) { /* error seen? */
508 if (tcpdebug) {
509 char *s,tmp[MAILTMPLEN];
510 if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
511 else s = "TCP data read end of file";
512 mm_log (tmp,TCPDEBUG);
513 }
514 return tcp_abort (stream);
515 }
516 stream->iptr = stream->ibuf;/* point at TCP buffer */
517 stream->ictr = i; /* set new byte count */
518 if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
519 }
520 (*bn) (BLOCK_NONE,NIL);
521 return T;
522 }
524 /* TCP/IP send string as record
525 * Accepts: TCP/IP stream
526 * string pointer
527 * Returns: T if success else NIL
528 */
530 long tcp_soutr (TCPSTREAM *stream,char *string)
531 {
532 return tcp_sout (stream,string,(unsigned long) strlen (string));
533 }
536 /* TCP/IP send string
537 * Accepts: TCP/IP stream
538 * string pointer
539 * byte count
540 * Returns: T if success else NIL
541 */
543 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
544 {
545 int i;
546 struct timeval tmo;
547 fd_set fds,efds;
548 time_t t = time (0);
549 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
550 tmo.tv_sec = ttmo_write;
551 tmo.tv_usec = 0;
552 FD_ZERO (&fds); /* initialize selection vector */
553 if (stream->tcpso == INVALID_SOCKET) return NIL;
554 (*bn) (BLOCK_TCPWRITE,NIL);
555 while (size > 0) { /* until request satisfied */
556 if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
557 /* simple case if not a socket */
558 if (stream->tcpsi != stream->tcpso)
559 while (((i = write (stream->tcpso,string,min (size,TCPMAXSEND))) < 0) &&
560 (errno == EINTR));
561 else {
562 time_t tl = time (0); /* start of request */
563 time_t now = tl;
564 time_t ti = ttmo_write ? now + ttmo_write : 0;
565 tmo.tv_usec = 0;
566 FD_ZERO (&fds); /* initialize selection vector */
567 FD_ZERO (&efds); /* handle errors too */
568 /* set bit in selection vectors */
569 FD_SET (stream->tcpso,&fds);
570 FD_SET(stream->tcpso,&efds);
571 errno = NIL; /* block and write */
572 do { /* block under timeout */
573 tmo.tv_sec = (long) (ti ? ti - now : 0);
574 i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
575 now = time (0); /* fake timeout if interrupt & time expired */
576 if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
577 (ti <= now)) i = 0;
578 } while ((i < 0) && (errno == WSAEINTR));
579 /* OK to send data? */
580 if (i > 0) while (((i = send (stream->tcpso,string,
581 (int) min (size,TCPMAXSEND),0)) ==
582 SOCKET_ERROR) &&
583 ((errno = WSAGetLastError ()) == WSAEINTR));
584 else if (!i) { /* timeout, ignore if told to resume */
585 if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl))) continue;
586 /* otherwise punt */
587 if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
588 return tcp_abort (stream);
589 }
590 }
591 if (i <= 0) { /* error seen? */
592 if (tcpdebug) {
593 char tmp[MAILTMPLEN];
594 sprintf (tmp,"TCP write I/O error %d",errno);
595 mm_log (tmp,TCPDEBUG);
596 }
597 return tcp_abort (stream);
598 }
599 string += i; /* how much we sent */
600 size -= i; /* count this size */
601 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
602 }
603 (*bn) (BLOCK_NONE,NIL);
604 return T; /* all done */
605 }
607 /* TCP/IP close
608 * Accepts: TCP/IP stream
609 */
611 void tcp_close (TCPSTREAM *stream)
612 {
613 tcp_abort (stream); /* nuke the sockets */
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 }
622 /* TCP/IP abort sockets
623 * Accepts: TCP/IP stream
624 * Returns: NIL, always
625 */
627 long tcp_abort (TCPSTREAM *stream)
628 {
629 if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
630 else stream->tcpso = INVALID_SOCKET;
631 return tcp_close_socket (&stream->tcpsi);
632 }
635 /* TCP/IP abort stream
636 * Accepts: WinSock socket
637 * Returns: NIL, always
638 */
640 long tcp_close_socket (SOCKET *sock)
641 {
642 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
643 /* something to close? */
644 if (sock && (*sock != INVALID_SOCKET)) {
645 (*bn) (BLOCK_TCPCLOSE,NIL);
646 closesocket (*sock); /* WinSock socket close */
647 *sock = INVALID_SOCKET;
648 (*bn) (BLOCK_NONE,NIL);
649 wsa_sock_open--; /* drop this socket */
650 }
651 /* no more open streams? */
652 if (wsa_initted && !wsa_sock_open) {
653 mm_log ("Winsock cleanup",NIL);
654 wsa_initted = 0; /* no more sockets, so... */
655 WSACleanup (); /* free up resources until needed */
656 }
657 return NIL;
658 }
660 /* TCP/IP get host name
661 * Accepts: TCP/IP stream
662 * Returns: host name for this stream
663 */
665 char *tcp_host (TCPSTREAM *stream)
666 {
667 return stream->host; /* use tcp_remotehost() if want guarantees */
668 }
671 /* TCP/IP get remote host name
672 * Accepts: TCP/IP stream
673 * Returns: host name for this stream
674 */
676 char *tcp_remotehost (TCPSTREAM *stream)
677 {
678 if (!stream->remotehost) {
679 size_t sadrlen;
680 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
681 stream->remotehost = /* get socket's peer name */
682 ((getpeername (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
683 (sadrlen <= 0)) ? cpystr (stream->host) : tcp_name (sadr,NIL);
684 fs_give ((void **) &sadr);
685 }
686 return stream->remotehost;
687 }
690 /* TCP/IP return port for this stream
691 * Accepts: TCP/IP stream
692 * Returns: port number for this stream
693 */
695 unsigned long tcp_port (TCPSTREAM *stream)
696 {
697 return stream->port; /* return port number */
698 }
701 /* TCP/IP get local host name
702 * Accepts: TCP/IP stream
703 * Returns: local host name
704 */
706 char *tcp_localhost (TCPSTREAM *stream)
707 {
708 if (!stream->localhost) {
709 size_t sadrlen;
710 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
711 stream->localhost = /* get socket's name */
712 ((stream->port & 0xffff000) ||
713 ((getsockname (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
714 (sadrlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
715 fs_give ((void **) &sadr);
716 }
717 return stream->localhost; /* return local host name */
718 }
720 /* TCP/IP get client host address (server calls only)
721 * Returns: client host address
722 */
724 char *tcp_clientaddr ()
725 {
726 if (!myClientAddr) {
727 size_t sadrlen;
728 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
729 if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
730 (sadrlen <= 0)) myClientAddr = cpystr ("UNKNOWN");
731 else { /* get stdin's peer name */
732 myClientAddr = cpystr (ip_sockaddrtostring (sadr));
733 if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
734 }
735 fs_give ((void **) &sadr);
736 }
737 return myClientAddr;
738 }
741 /* TCP/IP get client host name (server calls only)
742 * Returns: client host name
743 */
745 char *tcp_clienthost ()
746 {
747 if (!myClientHost) {
748 size_t sadrlen;
749 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
750 if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
751 (sadrlen <= 0)) myClientHost = cpystr ("UNKNOWN");
752 else { /* get stdin's peer name */
753 myClientHost = tcp_name (sadr,T);
754 if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
755 if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
756 }
757 fs_give ((void **) &sadr);
758 }
759 return myClientHost;
760 }
763 /* TCP/IP get client port number (server calls only)
764 * Returns: client port number
765 */
767 long tcp_clientport ()
768 {
769 if (!myClientHost && !myClientAddr) tcp_clientaddr ();
770 return myClientPort;
771 }
773 /* TCP/IP get server host address (server calls only)
774 * Returns: server host address
775 */
777 char *tcp_serveraddr ()
778 {
779 if (!myServerAddr) {
780 size_t sadrlen;
781 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
782 if (!wsa_initted++) { /* init Windows Sockets */
783 WSADATA wsock;
784 if (WSAStartup (WINSOCK_VERSION,&wsock)) {
785 wsa_initted = 0;
786 return "random-pc"; /* try again later? */
787 }
788 }
789 if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
790 (sadrlen <= 0)) myServerAddr = cpystr ("UNKNOWN");
791 else { /* get stdin's name */
792 myServerAddr = cpystr (ip_sockaddrtostring (sadr));
793 if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
794 }
795 fs_give ((void **) &sadr);
796 }
797 return myServerAddr;
798 }
801 /* TCP/IP get server host name (server calls only)
802 * Returns: server host name
803 */
805 char *tcp_serverhost ()
806 {
807 if (!myServerHost) { /* once-only */
808 size_t sadrlen;
809 struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
810 if (!wsa_initted++) { /* init Windows Sockets */
811 WSADATA wsock;
812 if (WSAStartup (WINSOCK_VERSION,&wsock)) {
813 wsa_initted = 0;
814 return "random-pc"; /* try again later? */
815 }
816 }
817 /* get stdin's name */
818 if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
819 (sadrlen <= 0)) myServerHost = cpystr (mylocalhost ());
820 else { /* get stdin's name */
821 myServerHost = tcp_name (sadr,NIL);
822 if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
823 if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
824 }
825 fs_give ((void **) &sadr);
826 }
827 return myServerHost;
828 }
831 /* TCP/IP get server port number (server calls only)
832 * Returns: server port number
833 */
835 long tcp_serverport ()
836 {
837 if (!myServerHost && !myServerAddr) tcp_serveraddr ();
838 return myServerPort;
839 }
841 /* TCP/IP return canonical form of host name
842 * Accepts: host name
843 * Returns: canonical form of host name
844 */
846 char *tcp_canonical (char *name)
847 {
848 char *ret,host[MAILTMPLEN];
849 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
850 /* look like domain literal? */
851 if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
852 (*bn) (BLOCK_DNSLOOKUP,NIL);
853 if (tcpdebug) {
854 sprintf (host,"DNS canonicalization %.80s",name);
855 mm_log (host,TCPDEBUG);
856 }
857 /* get canonical name */
858 if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
859 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
860 if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
861 return ret;
862 }
865 /* TCP/IP return name from socket
866 * Accepts: socket
867 * verbose flag
868 * Returns: cpystr name
869 */
871 char *tcp_name (struct sockaddr *sadr,long flag)
872 {
873 char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
874 sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
875 if (allowreversedns) {
876 blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
877 if (tcpdebug) {
878 sprintf (tmp,"Reverse DNS resolution %s",adr);
879 mm_log (tmp,TCPDEBUG);
880 }
881 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
882 /* translate address to name */
883 if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
884 /* produce verbose form if needed */
885 if (flag) sprintf (ret = tmp,"%s %s",t,adr);
886 else ret = t;
887 }
888 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
889 if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
890 }
891 return cpystr (ret);
892 }
894 /* Validate name
895 * Accepts: domain name
896 * Returns: T if valid, NIL otherwise
897 */
899 char *tcp_name_valid (char *s)
900 {
901 int c;
902 char *ret,*tail;
903 /* must be non-empty and not too long */
904 if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
905 /* must be alnum, dot, or hyphen */
906 while ((c = *s++) && (s <= tail) &&
907 (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
908 ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
909 if (c) ret = NIL;
910 }
911 return ret;
912 }

UW-IMAP'd extensions by yuuji