* When Alpine opens a folder in a server whose address is given numerically
[alpine.git] / imap / src / osdep / unix / tcp_unix.c
blobaaa246750be49bddd3d0e5dc0dfca358dfcd4d49
1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
4 */
6 /*
7 * Program: UNIX TCP/IP routines
9 * Author: Mark Crispin
11 * Date: 1 August 1988
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
26 #include "ip_unix.c"
28 #undef write /* don't use redefined write() */
30 #ifndef NI_MAXHOST
31 #define NI_MAXHOST 1025
32 #endif
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,
61 long *contd);
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)
74 void *ret = NIL;
75 switch ((int) function) {
76 case SET_TIMEOUT:
77 tmoh = (tcptimeout_t) value;
78 case GET_TIMEOUT:
79 ret = (void *) tmoh;
80 break;
81 case SET_OPENTIMEOUT:
82 ttmo_open = (long) value;
83 case GET_OPENTIMEOUT:
84 ret = (void *) ttmo_open;
85 break;
86 case SET_READTIMEOUT:
87 ttmo_read = (long) value;
88 case GET_READTIMEOUT:
89 ret = (void *) ttmo_read;
90 break;
91 case SET_WRITETIMEOUT:
92 ttmo_write = (long) value;
93 case GET_WRITETIMEOUT:
94 ret = (void *) ttmo_write;
95 break;
96 case SET_ALLOWREVERSEDNS:
97 allowreversedns = (long) value;
98 case GET_ALLOWREVERSEDNS:
99 ret = (void *) allowreversedns;
100 break;
101 case SET_TCPDEBUG:
102 tcpdebug = (long) value;
103 case GET_TCPDEBUG:
104 ret = (void *) tcpdebug;
105 break;
107 case SET_RSHTIMEOUT:
108 rshtimeout = (long) value;
109 case GET_RSHTIMEOUT:
110 ret = (void *) rshtimeout;
111 break;
112 case SET_RSHCOMMAND:
113 if (rshcommand) fs_give ((void **) &rshcommand);
114 rshcommand = cpystr ((char *) value);
115 case GET_RSHCOMMAND:
116 ret = (void *) rshcommand;
117 break;
118 case SET_RSHPATH:
119 if (rshpath) fs_give ((void **) &rshpath);
120 rshpath = cpystr ((char *) value);
121 case GET_RSHPATH:
122 ret = (void *) rshpath;
123 break;
124 case SET_SSHTIMEOUT:
125 sshtimeout = (long) value;
126 case GET_SSHTIMEOUT:
127 ret = (void *) sshtimeout;
128 break;
129 case SET_SSHCOMMAND:
130 if (sshcommand) fs_give ((void **) &sshcommand);
131 sshcommand = cpystr ((char *) value);
132 case GET_SSHCOMMAND:
133 ret = (void *) sshcommand;
134 break;
135 case SET_SSHPATH:
136 if (sshpath) fs_give ((void **) &sshpath);
137 sshpath = cpystr ((char *) value);
138 case GET_SSHPATH:
139 ret = (void *) sshpath;
140 break;
142 return ret;
145 /* TCP/IP open
146 * Accepts: host name
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;
155 int family;
156 int sock = -1;
157 int ctr = 0;
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;
162 void *adr;
163 size_t adrlen;
164 void *cleanup = NIL;
165 struct servent *sv = NIL;
166 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
167 void *data,*next;
168 port &= 0xffff; /* erase flags */
169 /* lookup service */
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 */
191 if (tcpdebug) {
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);
203 do {
204 (*bn) (BLOCK_TCPOPEN,NIL);
205 if (((sock = tcp_socket_open (family,s,adrlen,port,tmp,ctrp,
206 hostname)) < 0) &&
207 (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next,
208 &cleanup)) && !silent)
209 mm_log (tmp,WARN);
210 (*bn) (BLOCK_NONE,NIL);
211 } while ((sock < 0) && s);/* repeat until success or no more addresses */
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);
219 else { /* won */
220 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
221 sizeof (TCPSTREAM));
222 stream->port = port; /* port number */
223 /* init sockets */
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 */
233 /* Open a TCP socket
234 * Accepts: protocol family
235 * address to connect to
236 * address length
237 * port
238 * scratch buffer
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)
247 int i,ti,sock,flgs;
248 size_t len;
249 time_t now;
250 fd_set rfds,wfds,efds;
251 char buf[NI_MAXHOST];
252 struct timeval tmo;
253 struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
254 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
255 /* fetid Solaris */
256 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
257 sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr,buf));
258 mm_log (tmp,NIL);
259 /* make a socket */
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)",
266 sock,FD_SETSIZE);
267 (*bn) (BLOCK_NONSENSITIVE,data);
268 close (sock);
269 sock = -1;
270 errno = EMFILE;
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 */
287 default:
288 sprintf (tmp,"Can't connect to %.80s,%u: %s",hst,(unsigned int) port,
289 strerror (errno));
290 close (sock); /* flush socket */
291 sock = -1;
293 if ((sock >= 0) && ctr) { /* want open timeout? */
294 now = time (0); /* open timeout */
295 ti = ttmo_open ? now + ttmo_open : 0;
296 tmo.tv_usec = 0;
297 FD_ZERO (&rfds); /* initialize selection vector */
298 FD_ZERO (&wfds);
299 FD_ZERO (&efds); /* handle errors too */
300 FD_SET (sock,&rfds); /* block for readable, writeable, or error */
301 FD_SET (sock,&wfds);
302 FD_SET (sock,&efds);
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,1)) < 0) && (errno == EINTR));
317 if (i <= 0) { /* timeout or error? */
318 i = i ? errno : ETIMEDOUT;/* determine error code */
319 close (sock); /* flush socket */
320 sock = -1;
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
332 * Accepts: host name
333 * service name
334 * returned user name buffer
335 * Returns: TCP/IP stream if success else NIL
338 #define MAXARGV 20
340 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
342 TCPSTREAM *stream = NIL;
343 void *adr;
344 char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r;
345 int i,ti,pipei[2],pipeo[2];
346 size_t len;
347 time_t now;
348 struct timeval tmo;
349 fd_set fds,efds;
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);
353 #endif
354 #ifdef RSHPATH /* rsh path defined yet? */
355 if (!rshpath) rshpath = cpystr (RSHPATH);
356 #endif
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");
363 /* want rsh? */
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 */
372 host[i-1] = '\0';
373 /* validate domain literal */
374 if ((adr = ip_stringtoaddr (host,&len,&i)) != NULL) fs_give ((void **) &adr);
375 else {
376 sprintf (tmp,"Bad format domain-literal: %.80s",host);
377 mm_log (tmp,ERROR);
378 return NIL;
381 else {
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);
391 if (tcpdebug) {
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]);
405 return NIL;
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);
413 return 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 */
432 close (pipeo[0]);
434 /* create TCP/IP stream */
435 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
436 sizeof (TCPSTREAM));
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");
459 mm_log (tmp,WARN);
460 tcp_close (stream); /* punt stream */
461 stream = NIL;
463 (*bn) (BLOCK_NONE,NIL);
464 /* return user name */
465 strcpy (usrbuf,mb->user[0] ? mb->user : myusername ());
466 return stream; /* return success */
469 /* TCP receive line
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;
483 stc->text.size = n;
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;
489 stc->text.size = n;
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);
495 ret[n] = '\0';
497 mail_free_stringlist (&stl);/* either way, done with list */
499 return ret;
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,
510 long *contd)
512 unsigned long n;
513 char *s,*ret,c,d;
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 */
523 return ret;
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 */
533 stream->ictr--;
534 ret[*size = --n] = '\0'; /* tie off string with null */
536 else *contd = LONGT; /* continuation needed */
537 return ret;
540 /* TCP/IP receive buffer
541 * Accepts: TCP/IP stream
542 * size in bytes
543 * buffer to read into
544 * Returns: T if success, NIL otherwise
547 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
549 unsigned long n;
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 */
556 stream->iptr +=n;
557 size -= n; /* update # of bytes to do */
558 stream->ictr -=n;
560 if (size) {
561 int i;
562 fd_set fds,efds;
563 struct timeval tmo;
564 time_t t = time (0);
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);
569 time_t now = tl;
570 time_t ti = ttmo_read ? now + ttmo_read : 0;
571 if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
572 tmo.tv_usec = 0;
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? */
590 if (tcpdebug) {
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";
594 mm_log (s,TCPDEBUG);
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 */
611 return LONGT;
614 /* TCP/IP receive data
615 * Accepts: TCP/IP stream
616 * Returns: T if success, NIL otherwise
619 long tcp_getdata (TCPSTREAM *stream)
621 int i;
622 fd_set fds,efds;
623 struct timeval tmo;
624 time_t t = time (0);
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 */
630 time_t now = tl;
631 time_t ti = ttmo_read ? now + ttmo_read : 0;
632 if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
633 tmo.tv_usec = 0;
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) &&
648 (errno == EINTR));
649 if (i <= 0) { /* error seen? */
650 if (tcpdebug) {
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";
654 mm_log (s,TCPDEBUG);
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);
669 return T;
672 /* TCP/IP send string as record
673 * Accepts: TCP/IP stream
674 * string pointer
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
686 * string pointer
687 * byte count
688 * Returns: T if success else NIL
691 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
693 int i;
694 fd_set fds,efds;
695 struct timeval tmo;
696 time_t t = time (0);
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 */
702 time_t now = tl;
703 time_t ti = ttmo_write ? now + ttmo_write : 0;
704 if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
705 tmo.tv_usec = 0;
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) &&
720 (errno == EINTR));
721 if (i <= 0) { /* error seen? */
722 if (tcpdebug) {
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 */
743 /* TCP/IP close
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);
773 return 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) {
796 size_t sadrlen;
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) {
826 size_t sadrlen;
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 ()
843 if (!myClientAddr) {
844 char buf[NI_MAXHOST];
845 size_t sadrlen;
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);
855 return myClientAddr;
859 /* TCP/IP get client host name (server calls only)
860 * Returns: client host name
863 char *tcp_clienthost ()
865 if (!myClientHost) {
866 char buf[NI_MAXHOST];
867 size_t sadrlen;
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);
877 else v = "UNKNOWN";
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);
887 return myClientHost;
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 ();
898 return myClientPort;
901 /* TCP/IP get server host address (server calls only)
902 * Returns: server host address
905 char *tcp_serveraddr ()
907 if (!myServerAddr) {
908 char buf[NI_MAXHOST];
909 size_t sadrlen;
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);
919 return myServerAddr;
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];
931 size_t sadrlen;
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);
943 return myServerHost;
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 ();
954 return myServerPort;
957 /* TCP/IP return canonical form of host name
958 * Accepts: 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);
966 void *data;
967 /* look like domain literal? */
968 if (name[0] == '[' && name[strlen (name) - 1] == ']') return cpystr(name);
969 (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
970 data = (*bn) (BLOCK_SENSITIVE,NIL);
971 if (tcpdebug) {
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);
980 return ret;
983 /* TCP/IP return name from socket
984 * Accepts: socket
985 * verbose flag
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);
995 void *data;
996 if (tcpdebug) {
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);
1006 else ret = t;
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)
1023 int c;
1024 char *ret,*tail;
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 == '.')));
1031 if (c) ret = NIL;
1033 return ret;
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)
1043 int family;
1044 size_t adrlen,sadrlen,len;
1045 void *adr,*next;
1046 char buf[NI_MAXHOST];
1047 struct sockaddr *sadr;
1048 void *cleanup = NIL;
1049 long ret = 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);
1054 adr && !ret;
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);
1063 return ret;
1066 char *tcp_getsize (TCPSTREAM *stream, unsigned long size)
1068 char *ret = NIL;
1069 unsigned long got = 0L, need = size, n;
1070 int done = 0;
1072 while(!done){
1073 if(!tcp_getdata (stream)) return ret; /* return what we have */
1074 n = stream->ictr < need ? stream->ictr : need;
1075 fs_resize((void **) &ret, got + n + 1);
1076 memcpy(ret + got, stream->iptr, n);
1077 ret[got+n] = '\0';
1078 got += n;
1079 need -= n;
1080 stream->iptr += n;
1081 stream->ictr -= n;
1082 if(need == 0L) done++;
1085 return ret;
1088 /* Following statement must be at end of this module */
1090 #undef fork /* undo any use of vfork() */
1092 void tcp_end(void)
1094 if(rshcommand) fs_give((void **) &rshcommand);
1095 if(rshpath) fs_give((void **) &rshpath);
1096 if(sshcommand) fs_give((void **) &sshcommand);
1097 if(sshpath) fs_give((void **) &sshpath);
1098 if(myClientAddr) fs_give((void **) &myClientAddr);
1099 if(myClientHost) fs_give((void **) &myClientHost);
1100 if(myServerAddr) fs_give((void **) &myServerAddr);
1101 if(myServerHost) fs_give((void **) &myServerHost);