1 /* ========================================================================
2  * Copyright 2008-2010 Mark Crispin
3  * ========================================================================
4  */
5 
6 /*
7  * Program:	Winsock TCP/IP routines
8  *
9  * Author:	Mark Crispin from Mike Seibel's Winsock code
10  *
11  * Date:	11 April 1989
12  * Last Edited:	3 April 2010
13  *
14  * Previous versions of this file were:
15  *
16  * Copyright 1988-2008 University of Washington
17  *
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
21  *
22  *     http://www.apache.org/licenses/LICENSE-2.0
23  */
24 
25 
26 #define TCPMAXSEND 32768
27 
28 /* Private functions */
29 
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);
38 
39 
40 /* Private data */
41 
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 */
49 
50 /* TCP/IP manipulate parameters
51  * Accepts: function code
52  *	    function-dependent value
53  * Returns: function-dependent return value
54  */
55 
tcp_parameters(long function,void * value)56 void *tcp_parameters (long function,void *value)
57 {
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;
85   }
86   return ret;
87 }
88 
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
94  */
95 
tcp_open(char * host,char * service,unsigned long port)96 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
97 {
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;
116     }
117   }
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;
136     }
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);
143     }
144   }
145 
146   else {			/* lookup host name */
147     if (tcpdebug) {
148       sprintf (tmp,"DNS resolution %.80s",host);
149       mm_log (tmp,TCPDEBUG);
150     }
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);
169       }
170       wsa_sock_open--;		/* undo protection */
171     }
172   }
173   if (sock == INVALID_SOCKET) {	/* error? */
174     if (!silent) mm_log (tmp,ERROR);
175     tcp_close_socket (&sock);	/* do possible cleanup action */
176   }
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);
187   }
188   return stream;		/* return success */
189 }
190 
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
197  */
198 
tcp_socket_open(struct sockaddr_in * sin,char * tmp,char * hst,unsigned long port)199 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
200 		     unsigned long port)
201 {
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;
210   }
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;
231     }
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;
236   }
237   return sock;			/* return the socket */
238 }
239 
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
245  */
246 
tcp_aopen(NETMBX * mb,char * service,char * usrbuf)247 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
248 {
249   return NIL;			/* always NIL on Windows */
250 }
251 
252 /* TCP receive line
253  * Accepts: TCP stream
254  * Returns: text line string or NIL if failure
255  */
256 
tcp_getline(TCPSTREAM * stream)257 char *tcp_getline (TCPSTREAM *stream)
258 {
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';
279     }
280     mail_free_stringlist (&stl);/* either way, done with list */
281   }
282   return ret;
283 }
284 
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
290  */
291 
tcp_getline_work(TCPSTREAM * stream,unsigned long * size,long * contd)292 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
293 			       long *contd)
294 {
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;
307     }
308   }
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 */
318   }
319   else *contd = LONGT;		/* continuation needed */
320   return ret;
321 }
322 
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
328  */
329 
tcp_getbuffer(TCPSTREAM * stream,unsigned long size,char * s)330 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
331 {
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;
342   }
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);
382 	}
383       }
384     }
385     (*bn) (BLOCK_NONE,NIL);
386   }
387   *s = '\0';			/* tie off string */
388   return T;
389 }
390 
391 /* TCP/IP receive data
392  * Accepts: TCP/IP stream
393  * Returns: T if success, NIL otherwise
394  */
395 
tcp_getdata(TCPSTREAM * stream)396 long tcp_getdata (TCPSTREAM *stream)
397 {
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);
438       }
439     }
440   }
441   (*bn) (BLOCK_NONE,NIL);
442   return T;
443 }
444 
445 /* TCP/IP send string as record
446  * Accepts: TCP/IP stream
447  *	    string pointer
448  * Returns: T if success else NIL
449  */
450 
tcp_soutr(TCPSTREAM * stream,char * string)451 long tcp_soutr (TCPSTREAM *stream,char *string)
452 {
453   return tcp_sout (stream,string,(unsigned long) strlen (string));
454 }
455 
456 
457 /* TCP/IP send string
458  * Accepts: TCP/IP stream
459  *	    string pointer
460  *	    byte count
461  * Returns: T if success else NIL
462  */
463 
tcp_sout(TCPSTREAM * stream,char * string,unsigned long size)464 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
465 {
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;
503     }
504   }
505   (*bn) (BLOCK_NONE,NIL);
506   return T;			/* all done */
507 }
508 
509 
510 /* TCP/IP close
511  * Accepts: TCP/IP stream
512  */
513 
tcp_close(TCPSTREAM * stream)514 void tcp_close (TCPSTREAM *stream)
515 {
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 */
522 }
523 
524 
525 /* TCP/IP abort sockets
526  * Accepts: TCP/IP stream
527  * Returns: NIL, always
528  */
529 
tcp_abort(TCPSTREAM * stream)530 long tcp_abort (TCPSTREAM *stream)
531 {
532   if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
533   else stream->tcpso = INVALID_SOCKET;
534   return tcp_close_socket (&stream->tcpsi);
535 }
536 
537 
538 /* TCP/IP abort stream
539  * Accepts: WinSock socket
540  * Returns: NIL, always
541  */
542 
tcp_close_socket(SOCKET * sock)543 long tcp_close_socket (SOCKET *sock)
544 {
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 */
553   }
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 */
559   }
560   return NIL;
561 }
562 
563 /* TCP/IP get host name
564  * Accepts: TCP/IP stream
565  * Returns: host name for this stream
566  */
567 
tcp_host(TCPSTREAM * stream)568 char *tcp_host (TCPSTREAM *stream)
569 {
570   return stream->host;		/* use tcp_remotehost() if want guarantees */
571 }
572 
573 
574 /* TCP/IP get remote host name
575  * Accepts: TCP/IP stream
576  * Returns: host name for this stream
577  */
578 
tcp_remotehost(TCPSTREAM * stream)579 char *tcp_remotehost (TCPSTREAM *stream)
580 {
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);
588   }
589   return stream->remotehost;
590 }
591 
592 
593 /* TCP/IP return port for this stream
594  * Accepts: TCP/IP stream
595  * Returns: port number for this stream
596  */
597 
tcp_port(TCPSTREAM * stream)598 unsigned long tcp_port (TCPSTREAM *stream)
599 {
600   return stream->port;		/* return port number */
601 }
602 
603 
604 /* TCP/IP get local host name
605  * Accepts: TCP/IP stream
606  * Returns: local host name
607  */
608 
tcp_localhost(TCPSTREAM * stream)609 char *tcp_localhost (TCPSTREAM *stream)
610 {
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);
619   }
620   return stream->localhost;	/* return local host name */
621 }
622 
623 /* TCP/IP get client host address (server calls only)
624  * Returns: client host address
625  */
626 
tcp_clientaddr()627 char *tcp_clientaddr ()
628 {
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));
635   }
636   return myClientAddr;
637 }
638 
639 
640 /* TCP/IP get client host name (server calls only)
641  * Returns: client host name
642  */
643 
tcp_clienthost()644 char *tcp_clienthost ()
645 {
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);
652   }
653   return myClientHost;
654 }
655 
656 /* TCP/IP get server host address (server calls only)
657  * Returns: server host address
658  */
659 
tcp_serveraddr()660 char *tcp_serveraddr ()
661 {
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));
668   }
669   return myServerAddr;
670 }
671 
672 
673 /* TCP/IP get server host name (server calls only)
674  * Returns: server host name
675  */
676 
677 static long myServerPort = -1;
678 
tcp_serverhost()679 char *tcp_serverhost ()
680 {
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? */
689       }
690     }
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);
697     }
698   }
699   return myServerHost;
700 }
701 
702 
703 /* TCP/IP get server port number (server calls only)
704  * Returns: server port number
705  */
706 
tcp_serverport()707 long tcp_serverport ()
708 {
709   if (!myServerHost) tcp_serverhost ();
710   return myServerPort;
711 }
712 
713 /* TCP/IP return canonical form of host name
714  * Accepts: host name
715  * Returns: canonical form of host name
716  */
717 
tcp_canonical(char * name)718 char *tcp_canonical (char *name)
719 {
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);
729   }
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);
735 }
736 
737 
738 /* TCP/IP return name from socket
739  * Accepts: socket
740  *	    verbose flag
741  * Returns: cpystr name
742  */
743 
tcp_name(struct sockaddr_in * sin,long flag)744 char *tcp_name (struct sockaddr_in *sin,long flag)
745 {
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);
755     }
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;
766     }
767     (*bn) (BLOCK_NONSENSITIVE,data);
768     (*bn) (BLOCK_NONE,NIL);	/* alarms OK now */
769     if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
770   }
771   return cpystr (ret);
772 }
773 
774 /* Return my local host name
775  * Returns: my local host name
776  */
777 
mylocalhost(void)778 char *mylocalhost (void)
779 {
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;
786     }
787     if (wsa_initted && gethostname (tmp,MAILTMPLEN-1) != SOCKET_ERROR)
788       myLocalHost = tcp_canonical (tmp));
789   }
790   return myLocalHost ? myLocalHost : "random-pc";
791 }
792 
793 
794 /* Validate name
795  * Accepts: domain name
796  * Returns: T if valid, NIL otherwise
797  */
798 
tcp_name_valid(char * s)799 char *tcp_name_valid (char *s)
800 {
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;
810   }
811   return ret;
812 }
813