1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
7 * Program: UNIX TCP/IP routines
12 * Last Edited: 29 August 2011
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
28 #undef write /* don't use redefined write() */
31 #define NI_MAXHOST 1025
35 static tcptimeout_t tmoh
= NIL
; /* TCP timeout handler routine */
36 static long ttmo_open
= 0; /* TCP timeouts, in seconds */
37 static long ttmo_read
= 0;
38 static long ttmo_write
= 0;
39 static long rshtimeout
= 15; /* rsh timeout */
40 static char *rshcommand
= NIL
; /* rsh command */
41 static char *rshpath
= NIL
; /* rsh path */
42 static long sshtimeout
= 15; /* ssh timeout */
43 static char *sshcommand
= NIL
; /* ssh command */
44 static char *sshpath
= NIL
; /* ssh path */
45 static long allowreversedns
= T
;/* allow reverse DNS lookup */
46 static long tcpdebug
= NIL
; /* extra TCP debugging telemetry */
47 static char *myClientAddr
= NIL
;/* client IP address */
48 static char *myClientHost
= NIL
;/* client DNS name */
49 static long myClientPort
= -1; /* client port number */
50 static char *myServerAddr
= NIL
;/* server IP address */
51 static char *myServerHost
= NIL
;/* server DNS name */
52 static long myServerPort
= -1; /* server port number */
54 extern long maxposint
; /* get this from write.c */
56 /* Local function prototypes */
58 int tcp_socket_open (int family
,void *adr
,size_t adrlen
,unsigned short port
,
59 char *tmp
,int *ctr
,char *hst
);
60 static char *tcp_getline_work (TCPSTREAM
*stream
,unsigned long *size
,
62 long tcp_abort (TCPSTREAM
*stream
);
63 char *tcp_name (struct sockaddr
*sadr
,long flag
);
64 char *tcp_name_valid (char *s
);
66 /* TCP/IP manipulate parameters
67 * Accepts: function code
68 * function-dependent value
69 * Returns: function-dependent return value
72 void *tcp_parameters (long function
,void *value
)
75 switch ((int) function
) {
77 tmoh
= (tcptimeout_t
) value
;
82 ttmo_open
= (long) value
;
84 ret
= (void *) ttmo_open
;
87 ttmo_read
= (long) value
;
89 ret
= (void *) ttmo_read
;
91 case SET_WRITETIMEOUT
:
92 ttmo_write
= (long) value
;
93 case GET_WRITETIMEOUT
:
94 ret
= (void *) ttmo_write
;
96 case SET_ALLOWREVERSEDNS
:
97 allowreversedns
= (long) value
;
98 case GET_ALLOWREVERSEDNS
:
99 ret
= (void *) allowreversedns
;
102 tcpdebug
= (long) value
;
104 ret
= (void *) tcpdebug
;
108 rshtimeout
= (long) value
;
110 ret
= (void *) rshtimeout
;
113 if (rshcommand
) fs_give ((void **) &rshcommand
);
114 rshcommand
= cpystr ((char *) value
);
116 ret
= (void *) rshcommand
;
119 if (rshpath
) fs_give ((void **) &rshpath
);
120 rshpath
= cpystr ((char *) value
);
122 ret
= (void *) rshpath
;
125 sshtimeout
= (long) value
;
127 ret
= (void *) sshtimeout
;
130 if (sshcommand
) fs_give ((void **) &sshcommand
);
131 sshcommand
= cpystr ((char *) value
);
133 ret
= (void *) sshcommand
;
136 if (sshpath
) fs_give ((void **) &sshpath
);
137 sshpath
= cpystr ((char *) value
);
139 ret
= (void *) sshpath
;
147 * contact service name
148 * contact port number and optional silent flag
149 * Returns: TCP/IP stream if success else NIL
152 TCPSTREAM
*tcp_open (char *host
,char *service
,unsigned long port
)
154 TCPSTREAM
*stream
= NIL
;
158 int silent
= (port
& NET_SILENT
) ? T
: NIL
;
159 int *ctrp
= (port
& NET_NOOPENTIMEOUT
) ? NIL
: &ctr
;
160 char *s
,tmp
[MAILTMPLEN
];
161 char *hostname
= NIL
;
165 struct servent
*sv
= NIL
;
166 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
168 port
&= 0xffff; /* erase flags */
170 if (service
&& (sv
= getservbyname (service
,"tcp")))
171 port
= ntohs (sv
->s_port
);
172 /* The domain literal form is used (rather than simply the dotted decimal
173 as with other Unix programs) because it has to be a valid "host name"
174 in mailsystem terminology. */
175 /* look like domain literal? */
176 if (host
[0] == '[' && host
[(strlen (host
))-1] == ']') {
177 strcpy (tmp
,host
+1); /* yes, copy number part */
178 tmp
[(strlen (tmp
))-1] = '\0';
179 if ((adr
= ip_stringtoaddr (tmp
,&adrlen
,&family
)) != NULL
) {
180 (*bn
) (BLOCK_TCPOPEN
,NIL
);
181 /* get an open socket for this system */
182 sock
= tcp_socket_open (family
,adr
,adrlen
,port
,tmp
,ctrp
,
183 hostname
= cpystr(host
));
184 (*bn
) (BLOCK_NONE
,NIL
);
185 fs_give ((void **) &adr
);
187 else sprintf (tmp
,"Bad format domain-literal: %.80s",host
);
190 else { /* lookup host name */
192 sprintf (tmp
,"DNS resolution %.80s",host
);
193 mm_log (tmp
,TCPDEBUG
);
195 (*bn
) (BLOCK_DNSLOOKUP
,NIL
);/* quell alarms */
196 data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
197 if (!(s
= ip_nametoaddr (host
,&adrlen
,&family
,&hostname
,&next
,&cleanup
)))
198 sprintf (tmp
,"No such host as %.80s",host
);
199 (*bn
) (BLOCK_NONSENSITIVE
,data
);
200 (*bn
) (BLOCK_NONE
,NIL
);
201 if (s
) { /* DNS resolution won? */
202 if (tcpdebug
) mm_log ("DNS resolution done",TCPDEBUG
);
204 (*bn
) (BLOCK_TCPOPEN
,NIL
);
205 if (((sock
= tcp_socket_open (family
,s
,adrlen
,port
,tmp
,ctrp
,
207 (s
= ip_nametoaddr (NIL
,&adrlen
,&family
,&hostname
,&next
,
208 &cleanup
)) && !silent
)
210 (*bn
) (BLOCK_NONE
,NIL
);
211 } while ((sock
< 0) && s
);/* repeat until success or no more addreses */
213 ip_nametoaddr (NIL
,NIL
,NIL
,NIL
,NIL
,&cleanup
);
215 if (sock
< 0) { /* lost? */
216 if (!silent
) mm_log (tmp
,ERROR
);
217 if (hostname
) fs_give ((void **) &hostname
);
220 stream
= (TCPSTREAM
*) memset (fs_get (sizeof (TCPSTREAM
)),0,
222 stream
->port
= port
; /* port number */
224 stream
->tcpsi
= stream
->tcpso
= sock
;
225 /* stash in the snuck-in byte */
226 if ((stream
->ictr
= ctr
) != 0) *(stream
->iptr
= stream
->ibuf
) = tmp
[0];
227 stream
->host
= hostname
; /* copy official host name */
228 if (tcpdebug
) mm_log ("Stream open and ready for read",TCPDEBUG
);
230 return stream
; /* return success */
234 * Accepts: protocol family
235 * address to connect to
239 * pointer to "first byte read in" storage or NIL
240 * host name for error message
241 * Returns: socket if success, else -1 with error string in scratch buffer
244 int tcp_socket_open (int family
,void *adr
,size_t adrlen
,unsigned short port
,
245 char *tmp
,int *ctr
,char *hst
)
250 fd_set rfds
,wfds
,efds
;
251 char buf
[NI_MAXHOST
];
253 struct sockaddr
*sadr
= ip_sockaddr (family
,adr
,adrlen
,port
,&len
);
254 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
256 void *data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
257 sprintf (tmp
,"Trying IP address [%s]",ip_sockaddrtostring (sadr
,buf
));
260 if ((sock
= socket (sadr
->sa_family
,SOCK_STREAM
,0)) < 0) {
261 sprintf (tmp
,"Unable to create TCP socket: %s",strerror (errno
));
262 (*bn
) (BLOCK_NONSENSITIVE
,data
);
264 else if (sock
>= FD_SETSIZE
) {/* unselectable sockets are useless */
265 sprintf (tmp
,"Unable to create selectable TCP socket (%d >= %d)",
267 (*bn
) (BLOCK_NONSENSITIVE
,data
);
273 else { /* get current socket flags */
274 flgs
= fcntl (sock
,F_GETFL
,0);
275 /* set non-blocking if want open timeout */
276 if (ctr
) fcntl (sock
,F_SETFL
,flgs
| FNDELAY
);
277 /* open connection */
278 while ((i
= connect (sock
,sadr
,len
)) < 0 && (errno
== EINTR
));
279 (*bn
) (BLOCK_NONSENSITIVE
,data
);
280 if (i
< 0) switch (errno
) { /* failed? */
281 case EAGAIN
: /* DG brain damage */
282 case EINPROGRESS
: /* what we expect to happen */
283 case EALREADY
: /* or another form of it */
284 case EISCONN
: /* restart after interrupt? */
285 case EADDRINUSE
: /* restart after interrupt? */
286 break; /* well, not really, it was interrupted */
288 sprintf (tmp
,"Can't connect to %.80s,%u: %s",hst
,(unsigned int) port
,
290 close (sock
); /* flush socket */
293 if ((sock
>= 0) && ctr
) { /* want open timeout? */
294 now
= time (0); /* open timeout */
295 ti
= ttmo_open
? now
+ ttmo_open
: 0;
297 FD_ZERO (&rfds
); /* initialize selection vector */
299 FD_ZERO (&efds
); /* handle errors too */
300 FD_SET (sock
,&rfds
); /* block for readable, writeable, or error */
303 do { /* block under timeout */
304 tmo
.tv_sec
= ti
? ti
- now
: 0;
305 i
= select (sock
+1,&rfds
,&wfds
,&efds
,ti
? &tmo
: NIL
);
306 now
= time (0); /* fake timeout if interrupt & time expired */
307 if ((i
< 0) && (errno
== EINTR
) && ti
&& (ti
<= now
)) i
= 0;
308 } while ((i
< 0) && (errno
== EINTR
));
309 if (i
> 0) { /* success, make sure really connected */
310 /* restore blocking status */
311 fcntl (sock
,F_SETFL
,flgs
);
312 /* This used to be a zero-byte read(), but that crashes Solaris */
313 /* get socket status */
314 if(FD_ISSET(sock
,&rfds
))
315 while (((i
= *ctr
= read (sock
,tmp
,0)) < 0) && (errno
== EINTR
));
317 if (i
<= 0) { /* timeout or error? */
318 i
= i
? errno
: ETIMEDOUT
;/* determine error code */
319 close (sock
); /* flush socket */
321 errno
= i
; /* return error code */
322 sprintf (tmp
,"Connection failed to %.80s,%lu: %s",hst
,
323 (unsigned long) port
,strerror (errno
));
327 fs_give ((void **) &sadr
);
328 return sock
; /* return the socket */
331 /* TCP/IP authenticated open
334 * returned user name buffer
335 * Returns: TCP/IP stream if success else NIL
340 TCPSTREAM
*tcp_aopen (NETMBX
*mb
,char *service
,char *usrbuf
)
342 TCPSTREAM
*stream
= NIL
;
344 char host
[MAILTMPLEN
],tmp
[MAILTMPLEN
],*path
,*argv
[MAXARGV
+1],*r
;
345 int i
,ti
,pipei
[2],pipeo
[2];
350 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
351 #ifdef SSHPATH /* ssh path defined yet? */
352 if (!sshpath
) sshpath
= cpystr (SSHPATH
);
354 #ifdef RSHPATH /* rsh path defined yet? */
355 if (!rshpath
) rshpath
= cpystr (RSHPATH
);
357 if (*service
== '*') { /* want ssh? */
358 /* return immediately if ssh disabled */
359 if (!(sshpath
&& (ti
= sshtimeout
))) return NIL
;
360 /* ssh command prototype defined yet? */
361 if (!sshcommand
) sshcommand
= cpystr ("%s %s -l %s exec /etc/r%sd");
364 else if (rshpath
&& (ti
= rshtimeout
)) {
365 /* rsh command prototype defined yet? */
366 if (!rshcommand
) rshcommand
= cpystr ("%s %s -l %s exec /etc/r%sd");
368 else return NIL
; /* rsh disabled */
369 /* look like domain literal? */
370 if (mb
->host
[0] == '[' && mb
->host
[i
= (strlen (mb
->host
))-1] == ']') {
371 strcpy (host
,mb
->host
+1); /* yes, copy without brackets */
373 /* validate domain literal */
374 if ((adr
= ip_stringtoaddr (host
,&len
,&i
)) != NULL
) fs_give ((void **) &adr
);
376 sprintf (tmp
,"Bad format domain-literal: %.80s",host
);
382 strcpy (host
,r
= tcp_canonical (mb
->host
));
383 fs_give((void **) &r
);
386 if (*service
== '*') /* build ssh command */
387 sprintf (tmp
,sshcommand
,sshpath
,host
,
388 mb
->user
[0] ? mb
->user
: myusername (),service
+ 1);
389 else sprintf (tmp
,rshcommand
,rshpath
,host
,
390 mb
->user
[0] ? mb
->user
: myusername (),service
);
392 char msg
[MAILTMPLEN
];
393 sprintf (msg
,"Trying %.100s",tmp
);
394 mm_log (msg
,TCPDEBUG
);
396 /* parse command into argv */
397 for (i
= 1,path
= argv
[0] = strtok_r (tmp
," ",&r
);
398 (i
< MAXARGV
) && (argv
[i
] = strtok_r (NIL
," ",&r
)); i
++);
399 argv
[i
] = NIL
; /* make sure argv tied off */
400 /* make command pipes */
401 if (pipe (pipei
) < 0) return NIL
;
402 if ((pipei
[0] >= FD_SETSIZE
) || (pipei
[1] >= FD_SETSIZE
) ||
403 (pipe (pipeo
) < 0)) {
404 close (pipei
[0]); close (pipei
[1]);
407 (*bn
) (BLOCK_TCPOPEN
,NIL
); /* quell alarm up here for NeXT */
408 if ((pipeo
[0] >= FD_SETSIZE
) || (pipeo
[1] >= FD_SETSIZE
) ||
409 ((i
= fork ()) < 0)) { /* make inferior process */
410 close (pipei
[0]); close (pipei
[1]);
411 close (pipeo
[0]); close (pipeo
[1]);
412 (*bn
) (BLOCK_NONE
,NIL
);
415 if (!i
) { /* if child */
416 alarm (0); /* never have alarms in children */
417 if (!fork ()) { /* make grandchild so it's inherited by init */
418 int cf
; /* don't alter parent vars in case vfork() */
419 int maxfd
= max (20,max (max(pipei
[0],pipei
[1]),max(pipeo
[0],pipeo
[1])));
420 dup2 (pipei
[1],1); /* parent's input is my output */
421 dup2 (pipei
[1],2); /* parent's input is my error output too */
422 dup2 (pipeo
[0],0); /* parent's output is my input */
423 /* close all unnecessary descriptors */
424 for (cf
= 3; cf
<= maxfd
; cf
++) close (cf
);
425 setpgrp (0,getpid ()); /* be our own process group */
426 _exit (execv (path
,argv
));/* now run it */
428 _exit (1); /* child is done */
430 grim_pid_reap (i
,NIL
); /* reap child; grandchild now owned by init */
431 close (pipei
[1]); /* close child's side of the pipes */
434 /* create TCP/IP stream */
435 stream
= (TCPSTREAM
*) memset (fs_get (sizeof (TCPSTREAM
)),0,
437 /* copy remote host name from argument */
438 stream
->remotehost
= cpystr (stream
->host
= cpystr (host
));
439 stream
->tcpsi
= pipei
[0]; /* init sockets */
440 stream
->tcpso
= pipeo
[1];
441 stream
->ictr
= 0; /* init input counter */
442 stream
->port
= 0xffffffff; /* no port number */
443 ti
+= now
= time (0); /* open timeout */
444 tmo
.tv_usec
= 0; /* initialize usec timeout */
445 FD_ZERO (&fds
); /* initialize selection vector */
446 FD_ZERO (&efds
); /* handle errors too */
447 FD_SET (stream
->tcpsi
,&fds
); /* set bit in selection vector */
448 FD_SET (stream
->tcpsi
,&efds
); /* set bit in error selection vector */
449 FD_SET (stream
->tcpso
,&efds
); /* set bit in error selection vector */
450 do { /* block under timeout */
451 tmo
.tv_sec
= ti
- now
;
452 i
= select (max (stream
->tcpsi
,stream
->tcpso
)+1,&fds
,NIL
,&efds
,&tmo
);
453 now
= time (0); /* fake timeout if interrupt & time expired */
454 if ((i
< 0) && (errno
== EINTR
) && ti
&& (ti
<= now
)) i
= 0;
455 } while ((i
< 0) && (errno
== EINTR
));
456 if (i
<= 0) { /* timeout or error? */
457 sprintf (tmp
,i
? "error in %s to IMAP server" :
458 "%s to IMAP server timed out",(*service
== '*') ? "ssh" : "rsh");
460 tcp_close (stream
); /* punt stream */
463 (*bn
) (BLOCK_NONE
,NIL
);
464 /* return user name */
465 strcpy (usrbuf
,mb
->user
[0] ? mb
->user
: myusername ());
466 return stream
; /* return success */
470 * Accepts: TCP stream
471 * Returns: text line string or NIL if failure
474 char *tcp_getline (TCPSTREAM
*stream
)
476 unsigned long n
,contd
;
477 char *ret
= tcp_getline_work (stream
,&n
,&contd
);
478 if (ret
&& contd
) { /* got a line needing continuation? */
479 STRINGLIST
*stl
= mail_newstringlist ();
480 STRINGLIST
*stc
= stl
;
481 do { /* collect additional lines */
482 stc
->text
.data
= (unsigned char *) ret
;
484 stc
= stc
->next
= mail_newstringlist ();
485 ret
= tcp_getline_work (stream
,&n
,&contd
);
486 } while (ret
&& contd
);
487 if (ret
) { /* stash final part of line on list */
488 stc
->text
.data
= (unsigned char *) ret
;
490 /* determine how large a buffer we need */
491 for (n
= 0, stc
= stl
; stc
; stc
= stc
->next
) n
+= stc
->text
.size
;
492 ret
= fs_get (n
+ 1); /* copy parts into buffer */
493 for (n
= 0, stc
= stl
; stc
; n
+= stc
->text
.size
, stc
= stc
->next
)
494 memcpy (ret
+ n
,stc
->text
.data
,stc
->text
.size
);
497 mail_free_stringlist (&stl
);/* either way, done with list */
502 /* TCP receive line or partial line
503 * Accepts: TCP stream
504 * pointer to return size
505 * pointer to return continuation flag
506 * Returns: text line string, size and continuation flag, or NIL if failure
509 static char *tcp_getline_work (TCPSTREAM
*stream
,unsigned long *size
,
514 *contd
= NIL
; /* assume no continuation */
515 /* make sure have data */
516 if (!tcp_getdata (stream
)) return NIL
;
517 for (s
= stream
->iptr
, n
= 0, c
= '\0'; stream
->ictr
--; n
++, c
= d
) {
518 d
= *stream
->iptr
++; /* slurp another character */
519 if ((c
== '\015') && (d
== '\012')) {
520 ret
= (char *) fs_get (n
--);
521 memcpy (ret
,s
,*size
= n
); /* copy into a free storage string */
522 ret
[n
] = '\0'; /* tie off string with null */
526 /* copy partial string from buffer */
527 memcpy ((ret
= (char *) fs_get (n
)),s
,*size
= n
);
528 /* get more data from the net */
529 if (!tcp_getdata (stream
)) fs_give ((void **) &ret
);
530 /* special case of newline broken by buffer */
531 else if ((c
== '\015') && (*stream
->iptr
== '\012')) {
532 stream
->iptr
++; /* eat the line feed */
534 ret
[*size
= --n
] = '\0'; /* tie off string with null */
536 else *contd
= LONGT
; /* continuation needed */
540 /* TCP/IP receive buffer
541 * Accepts: TCP/IP stream
543 * buffer to read into
544 * Returns: T if success, NIL otherwise
547 long tcp_getbuffer (TCPSTREAM
*stream
,unsigned long size
,char *s
)
550 /* make sure socket still alive */
551 if (stream
->tcpsi
< 0) return NIL
;
552 /* can transfer bytes from buffer? */
553 if ((n
= min (size
,stream
->ictr
)) != 0L) {
554 memcpy (s
,stream
->iptr
,n
); /* yes, slurp as much as we can from it */
555 s
+= n
; /* update pointer */
557 size
-= n
; /* update # of bytes to do */
565 blocknotify_t bn
=(blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
566 (*bn
) (BLOCK_TCPREAD
,NIL
);
567 while (size
> 0) { /* until request satisfied */
568 time_t tl
= time (0);
570 time_t ti
= ttmo_read
? now
+ ttmo_read
: 0;
571 if (tcpdebug
) mm_log ("Reading TCP buffer",TCPDEBUG
);
573 FD_ZERO (&fds
); /* initialize selection vector */
574 FD_ZERO (&efds
); /* handle errors too */
575 /* set bit in selection vectors */
576 FD_SET (stream
->tcpsi
,&fds
);
577 FD_SET (stream
->tcpsi
,&efds
);
578 errno
= NIL
; /* initially no error */
579 do { /* block under timeout */
580 tmo
.tv_sec
= ti
? ti
- now
: 0;
581 i
= select (stream
->tcpsi
+1,&fds
,NIL
,&efds
,ti
? &tmo
: NIL
);
582 now
= time (0); /* fake timeout if interrupt & time expired */
583 if ((i
< 0) && (errno
== EINTR
) && ti
&& (ti
<= now
)) i
= 0;
584 } while ((i
< 0) && (errno
== EINTR
));
585 if (i
) { /* non-timeout result from select? */
586 if (i
> 0) /* read what we can */
587 while (((i
= read (stream
->tcpsi
,s
,(int) min (maxposint
,size
))) < 0)
588 && (errno
== EINTR
));
589 if (i
<= 0) { /* error seen? */
591 char tmp
[MAILTMPLEN
];
592 if (i
) sprintf (s
= tmp
,"TCP buffer read I/O error %d",errno
);
593 else s
= "TCP buffer read end of file";
596 return tcp_abort (stream
);
598 s
+= i
; /* success, point at new place to write */
599 size
-= i
; /* reduce byte count */
600 if (tcpdebug
) mm_log ("Successfully read TCP buffer",TCPDEBUG
);
602 /* timeout, punt unless told not to */
603 else if (!tmoh
|| !(*tmoh
) (now
- t
,now
- tl
, stream
->host
)) {
604 if (tcpdebug
) mm_log ("TCP buffer read timeout",TCPDEBUG
);
605 return tcp_abort (stream
);
608 (*bn
) (BLOCK_NONE
,NIL
);
610 *s
= '\0'; /* tie off string */
614 /* TCP/IP receive data
615 * Accepts: TCP/IP stream
616 * Returns: T if success, NIL otherwise
619 long tcp_getdata (TCPSTREAM
*stream
)
625 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
626 if (stream
->tcpsi
< 0) return NIL
;
627 (*bn
) (BLOCK_TCPREAD
,NIL
);
628 while (stream
->ictr
< 1) { /* if nothing in the buffer */
629 time_t tl
= time (0); /* start of request */
631 time_t ti
= ttmo_read
? now
+ ttmo_read
: 0;
632 if (tcpdebug
) mm_log ("Reading TCP data",TCPDEBUG
);
634 FD_ZERO (&fds
); /* initialize selection vector */
635 FD_ZERO (&efds
); /* handle errors too */
636 FD_SET (stream
->tcpsi
,&fds
);/* set bit in selection vectors */
637 FD_SET (stream
->tcpsi
,&efds
);
638 errno
= NIL
; /* initially no error */
639 do { /* block under timeout */
640 tmo
.tv_sec
= ti
? ti
- now
: 0;
641 i
= select (stream
->tcpsi
+1,&fds
,NIL
,&efds
,ti
? &tmo
: NIL
);
642 now
= time (0); /* fake timeout if interrupt & time expired */
643 if ((i
< 0) && (errno
== EINTR
) && ti
&& (ti
<= now
)) i
= 0;
644 } while ((i
< 0) && (errno
== EINTR
));
645 if (i
) { /* non-timeout result from select? */
646 /* read what we can */
647 if (i
> 0) while (((i
= read (stream
->tcpsi
,stream
->ibuf
,BUFLEN
)) < 0) &&
649 if (i
<= 0) { /* error seen? */
651 char *s
,tmp
[MAILTMPLEN
];
652 if (i
) sprintf (s
= tmp
,"TCP data read I/O error %d",errno
);
653 else s
= "TCP data read end of file";
656 return tcp_abort (stream
);
658 stream
->ictr
= i
; /* success, set new count and pointer */
659 stream
->iptr
= stream
->ibuf
;
660 if (tcpdebug
) mm_log ("Successfully read TCP data",TCPDEBUG
);
662 /* timeout, punt unless told not to */
663 else if (!tmoh
|| !(*tmoh
) (now
- t
,now
- tl
, stream
->host
)) {
664 if (tcpdebug
) mm_log ("TCP data read timeout",TCPDEBUG
);
665 return tcp_abort (stream
);/* error or timeout no-continue */
668 (*bn
) (BLOCK_NONE
,NIL
);
672 /* TCP/IP send string as record
673 * Accepts: TCP/IP stream
675 * Returns: T if success else NIL
678 long tcp_soutr (TCPSTREAM
*stream
,char *string
)
680 return tcp_sout (stream
,string
,(unsigned long) strlen (string
));
684 /* TCP/IP send string
685 * Accepts: TCP/IP stream
688 * Returns: T if success else NIL
691 long tcp_sout (TCPSTREAM
*stream
,char *string
,unsigned long size
)
697 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
698 if (stream
->tcpso
< 0) return NIL
;
699 (*bn
) (BLOCK_TCPWRITE
,NIL
);
700 while (size
> 0) { /* until request satisfied */
701 time_t tl
= time (0); /* start of request */
703 time_t ti
= ttmo_write
? now
+ ttmo_write
: 0;
704 if (tcpdebug
) mm_log ("Writing to TCP",TCPDEBUG
);
706 FD_ZERO (&fds
); /* initialize selection vector */
707 FD_ZERO (&efds
); /* handle errors too */
708 FD_SET (stream
->tcpso
,&fds
);/* set bit in selection vector */
709 FD_SET(stream
->tcpso
,&efds
);/* set bit in error selection vector */
710 errno
= NIL
; /* block and write */
711 do { /* block under timeout */
712 tmo
.tv_sec
= ti
? ti
- now
: 0;
713 i
= select (stream
->tcpso
+1,NIL
,&fds
,&efds
,ti
? &tmo
: NIL
);
714 now
= time (0); /* fake timeout if interrupt & time expired */
715 if ((i
< 0) && (errno
== EINTR
) && ti
&& (ti
<= now
)) i
= 0;
716 } while ((i
< 0) && (errno
== EINTR
));
717 if (i
) { /* non-timeout result from select? */
718 /* write what we can */
719 if (i
> 0) while (((i
= write (stream
->tcpso
,string
,size
)) < 0) &&
721 if (i
<= 0) { /* error seen? */
723 char tmp
[MAILTMPLEN
];
724 sprintf (tmp
,"TCP write I/O error %d",errno
);
725 mm_log (tmp
,TCPDEBUG
);
727 return tcp_abort (stream
);
729 string
+= i
; /* how much we sent */
730 size
-= i
; /* count this size */
731 if (tcpdebug
) mm_log ("successfully wrote to TCP",TCPDEBUG
);
733 /* timeout, punt unless told not to */
734 else if (!tmoh
|| !(*tmoh
) (now
- t
,now
- tl
, stream
->host
)) {
735 if (tcpdebug
) mm_log ("TCP write timeout",TCPDEBUG
);
736 return tcp_abort (stream
);
739 (*bn
) (BLOCK_NONE
,NIL
);
740 return T
; /* all done */
744 * Accepts: TCP/IP stream
747 void tcp_close (TCPSTREAM
*stream
)
749 tcp_abort (stream
); /* nuke the stream */
750 /* flush host names */
751 if (stream
->host
) fs_give ((void **) &stream
->host
);
752 if (stream
->remotehost
) fs_give ((void **) &stream
->remotehost
);
753 if (stream
->localhost
) fs_give ((void **) &stream
->localhost
);
754 fs_give ((void **) &stream
); /* flush the stream */
758 /* TCP/IP abort stream
759 * Accepts: TCP/IP stream
760 * Returns: NIL always
763 long tcp_abort (TCPSTREAM
*stream
)
765 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
766 if (stream
->tcpsi
>= 0) { /* no-op if no socket */
767 (*bn
) (BLOCK_TCPCLOSE
,NIL
);
768 close (stream
->tcpsi
); /* nuke the socket */
769 if (stream
->tcpsi
!= stream
->tcpso
) close (stream
->tcpso
);
770 stream
->tcpsi
= stream
->tcpso
= -1;
772 (*bn
) (BLOCK_NONE
,NIL
);
776 /* TCP/IP get host name
777 * Accepts: TCP/IP stream
778 * Returns: host name for this stream
781 char *tcp_host (TCPSTREAM
*stream
)
783 /* use tcp_remotehost() if want guarantees */
784 return stream
? stream
->host
: "UNKNOWN";
788 /* TCP/IP get remote host name
789 * Accepts: TCP/IP stream
790 * Returns: host name for this stream
793 char *tcp_remotehost (TCPSTREAM
*stream
)
795 if (!stream
->remotehost
) {
797 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
798 stream
->remotehost
= /* get socket's peer name */
799 getpeername (stream
->tcpsi
,sadr
,(void *) &sadrlen
) ?
800 cpystr (stream
->host
) : tcp_name (sadr
,NIL
);
801 fs_give ((void **) &sadr
);
803 return stream
->remotehost
;
807 /* TCP/IP return port for this stream
808 * Accepts: TCP/IP stream
809 * Returns: port number for this stream
812 unsigned long tcp_port (TCPSTREAM
*stream
)
814 return stream
->port
; /* return port number */
818 /* TCP/IP get local host name
819 * Accepts: TCP/IP stream
820 * Returns: local host name
823 char *tcp_localhost (TCPSTREAM
*stream
)
825 if (!stream
->localhost
) {
827 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
828 stream
->localhost
= /* get socket's name */
829 ((stream
->port
& 0xffff000) ||
830 getsockname (stream
->tcpsi
,sadr
,(void *) &sadrlen
)) ?
831 cpystr (mylocalhost ()) : tcp_name (sadr
,NIL
);
832 fs_give ((void **) &sadr
);
834 return stream
->localhost
; /* return local host name */
837 /* TCP/IP get client host address (server calls only)
838 * Returns: client host address
841 char *tcp_clientaddr ()
844 char buf
[NI_MAXHOST
];
846 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
847 if (getpeername (0,sadr
,(void *) &sadrlen
))
848 myClientAddr
= cpystr ("UNKNOWN");
849 else { /* get stdin's peer name */
850 myClientAddr
= cpystr (ip_sockaddrtostring (sadr
,buf
));
851 if (myClientPort
< 0) myClientPort
= ip_sockaddrtoport (sadr
);
853 fs_give ((void **) &sadr
);
859 /* TCP/IP get client host name (server calls only)
860 * Returns: client host name
863 char *tcp_clienthost ()
866 char buf
[NI_MAXHOST
];
868 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
869 if (getpeername (0,sadr
,(void *) &sadrlen
)) {
870 char *s
,*t
,*v
,tmp
[MAILTMPLEN
];
871 if ((s
= getenv (t
= "SSH_CLIENT")) ||
872 (s
= getenv (t
= "KRB5REMOTEADDR")) ||
873 (s
= getenv (t
= "SSH2_CLIENT"))) {
874 if ((v
= strchr (s
,' ')) != NULL
) *v
= '\0';
875 sprintf (v
= tmp
,"%.80s=%.80s",t
,s
);
878 myClientHost
= cpystr (v
);
880 else { /* get stdin's peer name */
881 myClientHost
= tcp_name (sadr
,T
);
882 if (!myClientAddr
) myClientAddr
= cpystr (ip_sockaddrtostring(sadr
,buf
));
883 if (myClientPort
< 0) myClientPort
= ip_sockaddrtoport (sadr
);
885 fs_give ((void **) &sadr
);
891 /* TCP/IP get client port number (server calls only)
892 * Returns: client port number
895 long tcp_clientport ()
897 if (!myClientHost
&& !myClientAddr
) tcp_clientaddr ();
901 /* TCP/IP get server host address (server calls only)
902 * Returns: server host address
905 char *tcp_serveraddr ()
908 char buf
[NI_MAXHOST
];
910 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
911 if (getsockname (0,sadr
,(void *) &sadrlen
))
912 myServerAddr
= cpystr ("UNKNOWN");
913 else { /* get stdin's name */
914 myServerAddr
= cpystr (ip_sockaddrtostring (sadr
,buf
));
915 if (myServerPort
< 0) myServerPort
= ip_sockaddrtoport (sadr
);
917 fs_give ((void **) &sadr
);
923 /* TCP/IP get server host name (server calls only)
924 * Returns: server host name
927 char *tcp_serverhost ()
929 if (!myServerHost
) { /* once-only */
930 char buf
[NI_MAXHOST
];
932 struct sockaddr
*sadr
= ip_newsockaddr (&sadrlen
);
933 /* get stdin's name */
934 if (getsockname (0,sadr
,(void *) &sadrlen
))
935 myServerHost
= cpystr (mylocalhost ());
936 else { /* get stdin's name */
937 myServerHost
= tcp_name (sadr
,NIL
);
938 if (!myServerAddr
) myServerAddr
= cpystr (ip_sockaddrtostring(sadr
,buf
));
939 if (myServerPort
< 0) myServerPort
= ip_sockaddrtoport (sadr
);
941 fs_give ((void **) &sadr
);
947 /* TCP/IP get server port number (server calls only)
948 * Returns: server port number
951 long tcp_serverport ()
953 if (!myServerHost
&& !myServerAddr
) tcp_serveraddr ();
957 /* TCP/IP return canonical form of host name
959 * Returns: canonical form of host name
962 char *tcp_canonical (char *name
)
964 char *ret
,host
[MAILTMPLEN
];
965 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
967 /* look like domain literal? */
968 if (name
[0] == '[' && name
[strlen (name
) - 1] == ']') return name
;
969 (*bn
) (BLOCK_DNSLOOKUP
,NIL
); /* quell alarms */
970 data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
972 sprintf (host
,"DNS canonicalization %.80s",name
);
973 mm_log (host
,TCPDEBUG
);
975 /* get canonical name */
976 if (!ip_nametoaddr (name
,NIL
,NIL
,&ret
,NIL
,NIL
)) ret
= cpystr (name
);
977 (*bn
) (BLOCK_NONSENSITIVE
,data
);
978 (*bn
) (BLOCK_NONE
,NIL
); /* alarms OK now */
979 if (tcpdebug
) mm_log ("DNS canonicalization done",TCPDEBUG
);
983 /* TCP/IP return name from socket
986 * Returns: cpystr name
989 char *tcp_name (struct sockaddr
*sadr
,long flag
)
991 char *ret
,*t
,adr
[MAILTMPLEN
],tmp
[MAILTMPLEN
],buf
[NI_MAXHOST
];
992 sprintf (ret
= adr
,"[%.80s]",ip_sockaddrtostring (sadr
,buf
));
993 if (allowreversedns
) {
994 blocknotify_t bn
= (blocknotify_t
)mail_parameters(NIL
,GET_BLOCKNOTIFY
,NIL
);
997 sprintf (tmp
,"Reverse DNS resolution %s",adr
);
998 mm_log (tmp
,TCPDEBUG
);
1000 (*bn
) (BLOCK_DNSLOOKUP
,NIL
);/* quell alarms */
1001 data
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
1002 /* translate address to name */
1003 if ((t
= tcp_name_valid (ip_sockaddrtoname (sadr
,buf
))) != NULL
) {
1004 /* produce verbose form if needed */
1005 if (flag
) sprintf (ret
= tmp
,"%s %s",t
,adr
);
1008 (*bn
) (BLOCK_NONSENSITIVE
,data
);
1009 (*bn
) (BLOCK_NONE
,NIL
); /* alarms OK now */
1010 if (tcpdebug
) mm_log ("Reverse DNS resolution done",TCPDEBUG
);
1012 return cpystr (ret
);
1016 /* TCP/IP validate name
1017 * Accepts: domain name
1018 * Returns: name if valid, NIL otherwise
1021 char *tcp_name_valid (char *s
)
1025 /* must be non-empty and not too long */
1026 if ((ret
= (s
&& *s
) ? s
: NIL
) && (tail
= ret
+ NETMAXHOST
)) {
1027 /* must be alnum, dot, or hyphen */
1028 while ((c
= *s
++) && (s
<= tail
) &&
1029 (((c
>= 'A') && (c
<= 'Z')) || ((c
>= 'a') && (c
<= 'z')) ||
1030 ((c
>= '0') && (c
<= '9')) || (c
== '-') || (c
== '.')));
1036 /* TCP/IP check if client is given host name
1037 * Accepts: candidate host name
1038 * Returns: T if match, NIL otherwise
1041 long tcp_isclienthost (char *host
)
1044 size_t adrlen
,sadrlen
,len
;
1046 char buf
[NI_MAXHOST
];
1047 struct sockaddr
*sadr
;
1048 void *cleanup
= NIL
;
1050 /* make sure that myClientAddr is set */
1051 if (tcp_clienthost () && myClientAddr
) {
1052 /* get sockaddr of client */
1053 for (adr
= ip_nametoaddr (host
,&adrlen
,&family
,NIL
,&next
,&cleanup
);
1055 adr
= ip_nametoaddr (NIL
,&adrlen
,&family
,NIL
,&next
,&cleanup
)) {
1056 /* build sockaddr of given address */
1057 sadr
= ip_sockaddr (family
,adr
,adrlen
,1,&len
);
1058 if (!strcmp (myClientAddr
,ip_sockaddrtostring (sadr
,buf
))) ret
= LONGT
;
1059 fs_give ((void **) &sadr
); /* done with client sockaddr */
1061 ip_nametoaddr (NIL
,NIL
,NIL
,NIL
,NIL
,&cleanup
);
1066 /* Following statement must be at end of this module */
1068 #undef fork /* undo any use of vfork() */