* Update to version 2.19.1
[alpine.git] / imap / src / osdep / dos / tcp_wsk.c
blob226750cb86c9398dac9f250c99dcccc37a268b4e
1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
4 */
6 /*
7 * Program: Winsock TCP/IP routines
9 * Author: Mark Crispin from Mike Seibel's Winsock code
11 * Date: 11 April 1989
12 * Last Edited: 3 April 2010
14 * Previous versions of this file were:
16 * Copyright 1988-2008 University of Washington
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
26 #define TCPMAXSEND 32768
28 /* Private functions */
30 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
31 unsigned long port);
32 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
33 long *contd);
34 long tcp_abort (TCPSTREAM *stream);
35 long tcp_close_socket (SOCKET *sock);
36 char *tcp_name (struct sockaddr_in *sin,long flag);
37 char *tcp_name_valid (char *s);
40 /* Private data */
42 int wsa_initted = 0; /* init ? */
43 static int wsa_sock_open = 0; /* keep track of open sockets */
44 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
45 static long ttmo_read = 0; /* TCP timeouts, in seconds */
46 static long ttmo_write = 0;
47 static long allowreversedns = T;/* allow reverse DNS lookup */
48 static long tcpdebug = NIL; /* extra TCP debugging telemetry */
50 /* TCP/IP manipulate parameters
51 * Accepts: function code
52 * function-dependent value
53 * Returns: function-dependent return value
56 void *tcp_parameters (long function,void *value)
58 void *ret = NIL;
59 switch ((int) function) {
60 case SET_TIMEOUT:
61 tmoh = (tcptimeout_t) value;
62 case GET_TIMEOUT:
63 ret = (void *) tmoh;
64 break;
65 case SET_READTIMEOUT:
66 ttmo_read = (long) value;
67 case GET_READTIMEOUT:
68 ret = (void *) ttmo_read;
69 break;
70 case SET_WRITETIMEOUT:
71 ttmo_write = (long) value;
72 case GET_WRITETIMEOUT:
73 ret = (void *) ttmo_write;
74 break;
75 case SET_ALLOWREVERSEDNS:
76 allowreversedns = (long) value;
77 case GET_ALLOWREVERSEDNS:
78 ret = (void *) allowreversedns;
79 break;
80 case SET_TCPDEBUG:
81 tcpdebug = (long) value;
82 case GET_TCPDEBUG:
83 ret = (void *) tcpdebug;
84 break;
86 return ret;
89 /* TCP/IP open
90 * Accepts: host name
91 * contact service name
92 * contact port number and optional silent flag
93 * Returns: TCP/IP stream if success else NIL
96 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
98 TCPSTREAM *stream = NIL;
99 int i;
100 SOCKET sock = INVALID_SOCKET;
101 int silent = (port & NET_SILENT) ? T : NIL;
102 char *s;
103 struct sockaddr_in sin;
104 struct hostent *he;
105 char hostname[MAILTMPLEN];
106 char tmp[MAILTMPLEN];
107 struct servent *sv = NIL;
108 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
109 if (!wsa_initted++) { /* init Windows Sockets */
110 WSADATA wsock;
111 if (i = (int) WSAStartup (WSA_VERSION,&wsock)) {
112 wsa_initted = 0; /* in case we try again */
113 sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
114 mm_log (tmp,ERROR);
115 return NIL;
118 port &= 0xffff; /* erase flags */
119 /* lookup service */
120 if (service && (sv = getservbyname (service,"tcp")))
121 port = ntohs (sin.sin_port = sv->s_port);
122 /* copy port number in network format */
123 else sin.sin_port = htons ((u_short) port);
124 /* The domain literal form is used (rather than simply the dotted decimal
125 as with other Windows programs) because it has to be a valid "host name"
126 in mailsystem terminology. */
127 sin.sin_family = AF_INET; /* family is always Internet */
128 /* look like domain literal? */
129 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
130 strcpy (tmp,host+1); /* yes, copy number part */
131 tmp[strlen (tmp)-1] = '\0';
132 if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) {
133 sprintf (tmp,"Bad format domain-literal: %.80s",host);
134 mm_log (tmp,ERROR);
135 return NIL;
137 else {
138 sin.sin_family = AF_INET; /* family is always Internet */
139 strcpy (hostname,host);
140 (*bn) (BLOCK_TCPOPEN,NIL);
141 sock = tcp_socket_open (&sin,tmp,hostname,port);
142 (*bn) (BLOCK_NONE,NIL);
146 else { /* lookup host name */
147 if (tcpdebug) {
148 sprintf (tmp,"DNS resolution %.80s",host);
149 mm_log (tmp,TCPDEBUG);
151 (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
152 if (!(he = gethostbyname (lcase (strcpy (tmp,host)))))
153 sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host);
154 (*bn) (BLOCK_NONE,NIL);
155 if (he) { /* DNS resolution won? */
156 if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
157 /* copy address type */
158 sin.sin_family = he->h_addrtype;
159 /* copy host name */
160 strcpy (hostname,he->h_name);
161 wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
162 loop */
163 for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) {
164 if (i && !silent) mm_log (tmp,WARN);
165 memcpy (&sin.sin_addr,s,he->h_length);
166 (*bn) (BLOCK_TCPOPEN,NIL);
167 sock = tcp_socket_open (&sin,tmp,hostname,port);
168 (*bn) (BLOCK_NONE,NIL);
170 wsa_sock_open--; /* undo protection */
173 if (sock == INVALID_SOCKET) { /* error? */
174 if (!silent) mm_log (tmp,ERROR);
175 tcp_close_socket (&sock); /* do possible cleanup action */
177 else { /* got a socket, create TCP/IP stream */
178 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
179 sizeof (TCPSTREAM));
180 stream->port = port; /* port number */
181 /* init socket */
182 stream->tcpsi = stream->tcpso = sock;
183 stream->ictr = 0; /* init input counter */
184 /* copy official host name */
185 stream->host = cpystr (hostname);
186 if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
188 return stream; /* return success */
191 /* Open a TCP socket
192 * Accepts: Internet socket address block
193 * scratch buffer
194 * host name for error message
195 * port number for error message
196 * Returns: socket if success, else -1 with error string in scratch buffer
199 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
200 unsigned long port)
202 int sock;
203 char *s;
204 sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
205 mm_log (tmp,NIL);
206 /* get a TCP stream */
207 if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) {
208 sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError());
209 return -1;
211 wsa_sock_open++; /* count this socket as open */
212 /* open connection */
213 if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) ==
214 SOCKET_ERROR) {
215 switch (WSAGetLastError ()){/* analyze error */
216 case WSAECONNREFUSED:
217 s = "Refused";
218 break;
219 case WSAENOBUFS:
220 s = "Insufficient system resources";
221 break;
222 case WSAETIMEDOUT:
223 s = "Timed out";
224 break;
225 case WSAEHOSTUNREACH:
226 s = "Host unreachable";
227 break;
228 default:
229 s = "Unknown error";
230 break;
232 sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s,
233 WSAGetLastError ());
234 tcp_close_socket (&sock); /* flush socket */
235 sock = INVALID_SOCKET;
237 return sock; /* return the socket */
240 /* TCP/IP authenticated open
241 * Accepts: NETMBX specifier
242 * service name
243 * returned user name buffer
244 * Returns: TCP/IP stream if success else NIL
247 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
249 return NIL; /* always NIL on Windows */
252 /* TCP receive line
253 * Accepts: TCP stream
254 * Returns: text line string or NIL if failure
257 char *tcp_getline (TCPSTREAM *stream)
259 unsigned long n,contd;
260 char *ret = tcp_getline_work (stream,&n,&contd);
261 if (ret && contd) { /* got a line needing continuation? */
262 STRINGLIST *stl = mail_newstringlist ();
263 STRINGLIST *stc = stl;
264 do { /* collect additional lines */
265 stc->text.data = (unsigned char *) ret;
266 stc->text.size = n;
267 stc = stc->next = mail_newstringlist ();
268 ret = tcp_getline_work (stream,&n,&contd);
269 } while (ret && contd);
270 if (ret) { /* stash final part of line on list */
271 stc->text.data = (unsigned char *) ret;
272 stc->text.size = n;
273 /* determine how large a buffer we need */
274 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
275 ret = fs_get (n + 1); /* copy parts into buffer */
276 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
277 memcpy (ret + n,stc->text.data,stc->text.size);
278 ret[n] = '\0';
280 mail_free_stringlist (&stl);/* either way, done with list */
282 return ret;
285 /* TCP receive line or partial line
286 * Accepts: TCP stream
287 * pointer to return size
288 * pointer to return continuation flag
289 * Returns: text line string, size and continuation flag, or NIL if failure
292 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
293 long *contd)
295 unsigned long n;
296 char *s,*ret,c,d;
297 *contd = NIL; /* assume no continuation */
298 /* make sure have data */
299 if (!tcp_getdata (stream)) return NIL;
300 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
301 d = *stream->iptr++; /* slurp another character */
302 if ((c == '\015') && (d == '\012')) {
303 ret = (char *) fs_get (n--);
304 memcpy (ret,s,*size = n); /* copy into a free storage string */
305 ret[n] = '\0'; /* tie off string with null */
306 return ret;
309 /* copy partial string from buffer */
310 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
311 /* get more data from the net */
312 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
313 /* special case of newline broken by buffer */
314 else if ((c == '\015') && (*stream->iptr == '\012')) {
315 stream->iptr++; /* eat the line feed */
316 stream->ictr--;
317 ret[*size = --n] = '\0'; /* tie off string with null */
319 else *contd = LONGT; /* continuation needed */
320 return ret;
323 /* TCP/IP receive buffer
324 * Accepts: TCP/IP stream
325 * size in bytes
326 * buffer to read into
327 * Returns: T if success, NIL otherwise
330 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
332 unsigned long n;
333 /* make sure socket still alive */
334 if (stream->tcpsi == INVALID_SOCKET) return NIL;
335 /* can transfer bytes from buffer? */
336 if (n = min (size,stream->ictr)) {
337 memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
338 s += n; /* update pointer */
339 stream->iptr +=n;
340 size -= n; /* update # of bytes to do */
341 stream->ictr -=n;
343 if (size) {
344 int i;
345 fd_set fds;
346 struct timeval tmo;
347 time_t tc,t = time (0);
348 blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
349 (*bn) (BLOCK_TCPREAD,NIL);
350 while (size > 0) { /* until request satisfied */
351 time_t tl = time (0);
352 if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
353 FD_ZERO (&fds); /* initialize selection vector */
354 FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
355 tmo.tv_sec = ttmo_read;
356 tmo.tv_usec = 0;
357 /* block and read */
358 switch ((stream->tcpsi == stream->tcpso) ?
359 select (stream->tcpsi+1,&fds,0,0,
360 ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
361 case SOCKET_ERROR: /* error */
362 if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
363 break;
364 case 0: /* timeout */
365 tc = time (0);
366 if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
367 return tcp_abort (stream);
368 default:
369 if (stream->tcpsi == stream->tcpso)
370 while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) ==
371 SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
372 else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) <
373 0) && (errno == EINTR));
374 switch (i) {
375 case SOCKET_ERROR: /* error */
376 case 0: /* no data read */
377 return tcp_abort (stream);
378 default:
379 s += i; /* point at new place to write */
380 size -= i; /* reduce byte count */
381 if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
385 (*bn) (BLOCK_NONE,NIL);
387 *s = '\0'; /* tie off string */
388 return T;
391 /* TCP/IP receive data
392 * Accepts: TCP/IP stream
393 * Returns: T if success, NIL otherwise
396 long tcp_getdata (TCPSTREAM *stream)
398 struct timeval tmo;
399 int i;
400 fd_set fds;
401 time_t tc,t = time (0);
402 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
403 FD_ZERO (&fds); /* initialize selection vector */
404 if (stream->tcpsi == INVALID_SOCKET) return NIL;
405 (*bn) (BLOCK_TCPREAD,NIL);
406 tmo.tv_sec = ttmo_read;
407 tmo.tv_usec = 0;
408 while (stream->ictr < 1) { /* if nothing in the buffer */
409 time_t tl = time (0);
410 if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
411 FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
412 /* block and read */
413 switch ((stream->tcpsi == stream->tcpso) ?
414 select (stream->tcpsi+1,&fds,0,0,
415 ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
416 case SOCKET_ERROR: /* error */
417 if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
418 break;
419 case 0: /* timeout */
420 tc = time (0);
421 if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
422 return tcp_abort (stream);
423 default:
424 if (stream->tcpsi == stream->tcpso)
425 while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
426 SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
427 else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
428 (errno == EINTR));
429 switch (i) {
430 case SOCKET_ERROR: /* error */
431 case 0: /* no data read */
432 return tcp_abort (stream);
433 default:
434 stream->ictr = i; /* set new byte count */
435 /* point at TCP buffer */
436 stream->iptr = stream->ibuf;
437 if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
441 (*bn) (BLOCK_NONE,NIL);
442 return T;
445 /* TCP/IP send string as record
446 * Accepts: TCP/IP stream
447 * string pointer
448 * Returns: T if success else NIL
451 long tcp_soutr (TCPSTREAM *stream,char *string)
453 return tcp_sout (stream,string,(unsigned long) strlen (string));
457 /* TCP/IP send string
458 * Accepts: TCP/IP stream
459 * string pointer
460 * byte count
461 * Returns: T if success else NIL
464 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
466 int i;
467 struct timeval tmo;
468 fd_set fds;
469 time_t tc,t = time (0);
470 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
471 tmo.tv_sec = ttmo_write;
472 tmo.tv_usec = 0;
473 FD_ZERO (&fds); /* initialize selection vector */
474 if (stream->tcpso == INVALID_SOCKET) return NIL;
475 (*bn) (BLOCK_TCPWRITE,NIL);
476 while (size > 0) { /* until request satisfied */
477 time_t tl = time (0);
478 if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
479 FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
480 /* block and write */
481 switch ((stream->tcpsi == stream->tcpso) ?
482 select (stream->tcpso+1,NULL,&fds,NULL,
483 tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) {
484 case SOCKET_ERROR: /* error */
485 if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
486 break;
487 case 0: /* timeout */
488 tc = time (0);
489 if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
490 return tcp_abort (stream);
491 default:
492 if (stream->tcpsi == stream->tcpso)
493 while (((i = send (stream->tcpso,string,
494 (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) &&
495 (WSAGetLastError () == WSAEINTR));
496 else while (((i = write (stream->tcpso,string,
497 min (size,TCPMAXSEND))) < 0) &&
498 (errno == EINTR));
499 if (i == SOCKET_ERROR) return tcp_abort (stream);
500 size -= i; /* count this size */
501 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
502 string += i;
505 (*bn) (BLOCK_NONE,NIL);
506 return T; /* all done */
510 /* TCP/IP close
511 * Accepts: TCP/IP stream
514 void tcp_close (TCPSTREAM *stream)
516 tcp_abort (stream); /* nuke the sockets */
517 /* flush host names */
518 if (stream->host) fs_give ((void **) &stream->host);
519 if (stream->remotehost) fs_give ((void **) &stream->remotehost);
520 if (stream->localhost) fs_give ((void **) &stream->localhost);
521 fs_give ((void **) &stream); /* flush the stream */
525 /* TCP/IP abort sockets
526 * Accepts: TCP/IP stream
527 * Returns: NIL, always
530 long tcp_abort (TCPSTREAM *stream)
532 if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
533 else stream->tcpso = INVALID_SOCKET;
534 return tcp_close_socket (&stream->tcpsi);
538 /* TCP/IP abort stream
539 * Accepts: WinSock socket
540 * Returns: NIL, always
543 long tcp_close_socket (SOCKET *sock)
545 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
546 /* something to close? */
547 if (sock && (*sock != INVALID_SOCKET)) {
548 (*bn) (BLOCK_TCPCLOSE,NIL);
549 closesocket (*sock); /* WinSock socket close */
550 *sock = INVALID_SOCKET;
551 (*bn) (BLOCK_NONE,NIL);
552 wsa_sock_open--; /* drop this socket */
554 /* no more open streams? */
555 if (wsa_initted && !wsa_sock_open) {
556 mm_log ("Winsock cleanup",NIL);
557 wsa_initted = 0; /* no more sockets, so... */
558 WSACleanup (); /* free up resources until needed */
560 return NIL;
563 /* TCP/IP get host name
564 * Accepts: TCP/IP stream
565 * Returns: host name for this stream
568 char *tcp_host (TCPSTREAM *stream)
570 return stream->host; /* use tcp_remotehost() if want guarantees */
574 /* TCP/IP get remote host name
575 * Accepts: TCP/IP stream
576 * Returns: host name for this stream
579 char *tcp_remotehost (TCPSTREAM *stream)
581 if (!stream->remotehost) {
582 struct sockaddr_in sin;
583 int sinlen = sizeof (struct sockaddr_in);
584 stream->remotehost = /* get socket's peer name */
585 ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
586 SOCKET_ERROR) || (sinlen <= 0)) ?
587 cpystr (stream->host) : tcp_name (&sin,NIL);
589 return stream->remotehost;
593 /* TCP/IP return port for this stream
594 * Accepts: TCP/IP stream
595 * Returns: port number for this stream
598 unsigned long tcp_port (TCPSTREAM *stream)
600 return stream->port; /* return port number */
604 /* TCP/IP get local host name
605 * Accepts: TCP/IP stream
606 * Returns: local host name
609 char *tcp_localhost (TCPSTREAM *stream)
611 if (!stream->localhost) {
612 struct sockaddr_in sin;
613 int sinlen = sizeof (struct sockaddr_in);
614 stream->localhost = /* get socket's name */
615 ((stream->port & 0xffff000) ||
616 ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
617 SOCKET_ERROR) || (sinlen <= 0))) ?
618 cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
620 return stream->localhost; /* return local host name */
623 /* TCP/IP get client host address (server calls only)
624 * Returns: client host address
627 char *tcp_clientaddr ()
629 if (!myClientAddr) {
630 struct sockaddr_in sin;
631 int sinlen = sizeof (struct sockaddr_in);
632 myClientAddr = /* get stdin's peer name */
633 ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
634 (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
636 return myClientAddr;
640 /* TCP/IP get client host name (server calls only)
641 * Returns: client host name
644 char *tcp_clienthost ()
646 if (!myClientHost) {
647 struct sockaddr_in sin;
648 int sinlen = sizeof (struct sockaddr_in);
649 myClientHost = /* get stdin's peer name */
650 ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
651 (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T);
653 return myClientHost;
656 /* TCP/IP get server host address (server calls only)
657 * Returns: server host address
660 char *tcp_serveraddr ()
662 if (!myServerAddr) {
663 struct sockaddr_in sin;
664 int sinlen = sizeof (struct sockaddr_in);
665 myServerAddr = /* get stdin's peer name */
666 ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
667 (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
669 return myServerAddr;
673 /* TCP/IP get server host name (server calls only)
674 * Returns: server host name
677 static long myServerPort = -1;
679 char *tcp_serverhost ()
681 if (!myServerHost) {
682 struct sockaddr_in sin;
683 int sinlen = sizeof (struct sockaddr_in);
684 if (!wsa_initted++) { /* init Windows Sockets */
685 WSADATA wsock;
686 if (WSAStartup (WSA_VERSION,&wsock)) {
687 wsa_initted = 0;
688 return "random-pc"; /* try again later? */
691 /* get stdin's name */
692 if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
693 (sinlen <= 0)) myServerHost = cpystr (mylocalhost ());
694 else {
695 myServerHost = tcp_name (&sin,NIL);
696 myServerPort = ntohs (sin.sin_port);
699 return myServerHost;
703 /* TCP/IP get server port number (server calls only)
704 * Returns: server port number
707 long tcp_serverport ()
709 if (!myServerHost) tcp_serverhost ();
710 return myServerPort;
713 /* TCP/IP return canonical form of host name
714 * Accepts: host name
715 * Returns: canonical form of host name
718 char *tcp_canonical (char *name)
720 char *ret,host[MAILTMPLEN];
721 struct hostent *he;
722 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
723 /* look like domain literal? */
724 if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
725 (*bn) (BLOCK_DNSLOOKUP,NIL);
726 if (tcpdebug) {
727 sprintf (host,"DNS canonicalization %.80s",name);
728 mm_log (host,TCPDEBUG);
730 /* note that NT requires lowercase! */
731 ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
732 (*bn) (BLOCK_NONE,NIL);
733 if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
734 return cpystr(ret);
738 /* TCP/IP return name from socket
739 * Accepts: socket
740 * verbose flag
741 * Returns: cpystr name
744 char *tcp_name (struct sockaddr_in *sin,long flag)
746 char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
747 sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
748 if (allowreversedns) {
749 struct hostent *he;
750 blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
751 void *data;
752 if (tcpdebug) {
753 sprintf (tmp,"Reverse DNS resolution %s",adr);
754 mm_log (tmp,TCPDEBUG);
756 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
757 data = (*bn) (BLOCK_SENSITIVE,NIL);
758 /* translate address to name */
759 if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
760 sizeof (struct in_addr),
761 sin->sin_family)) ?
762 (char *) he->h_name : NIL)) {
763 /* produce verbose form if needed */
764 if (flag) sprintf (ret = tmp,"%s %s",t,adr);
765 else ret = t;
767 (*bn) (BLOCK_NONSENSITIVE,data);
768 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
769 if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
771 return cpystr (ret);
774 /* Return my local host name
775 * Returns: my local host name
778 char *mylocalhost (void)
780 if (!myLocalHost) {
781 char tmp[MAILTMPLEN];
782 if (!wsa_initted++) { /* init Windows Sockets */
783 WSADATA wsock;
784 if (WSAStartup (WSA_VERSION,&wsock))
785 wsa_initted = 0;
787 if (wsa_initted && gethostname (tmp,MAILTMPLEN-1) != SOCKET_ERROR)
788 myLocalHost = tcp_canonical (tmp));
790 return myLocalHost ? myLocalHost : "random-pc";
794 /* Validate name
795 * Accepts: domain name
796 * Returns: T if valid, NIL otherwise
799 char *tcp_name_valid (char *s)
801 int c;
802 char *ret,*tail;
803 /* must be non-empty and not too long */
804 if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
805 /* must be alnum, dot, or hyphen */
806 while ((c = *s++) && (s <= tail) &&
807 (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
808 ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
809 if (c) ret = NIL;
811 return ret;