* Update to version 2.19.1
[alpine.git] / imap / src / osdep / amiga / tcp_ami.c
blobc0666bd3bb5f94afa5dbcbe86947cec1adfa26a5
1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
4 */
6 /*
7 * Program: Amiga TCP/IP routines
9 * Author: Mark Crispin
11 * Date: 1 August 1988
12 * Last Edited: 3 April 2010
14 * Previous versions of this file were:
16 * Copyright 1988-2008 University of Washington
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
25 #undef write /* don't use redefined write() */
27 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
28 static long ttmo_open = 0; /* TCP timeouts, in seconds */
29 static long ttmo_read = 0;
30 static long ttmo_write = 0;
31 static long allowreversedns = T;/* allow reverse DNS lookup */
32 static long tcpdebug = NIL; /* extra TCP debugging telemetry */
34 extern long maxposint; /* get this from write.c */
36 /* Local function prototypes */
38 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
39 unsigned long port);
40 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
41 long *contd);
42 long tcp_abort (TCPSTREAM *stream);
43 char *tcp_name (struct sockaddr_in *sin,long flag);
44 char *tcp_name_valid (char *s);
46 /* TCP/IP manipulate parameters
47 * Accepts: function code
48 * function-dependent value
49 * Returns: function-dependent return value
52 void *tcp_parameters (long function,void *value)
54 void *ret = NIL;
55 switch ((int) function) {
56 case SET_TIMEOUT:
57 tmoh = (tcptimeout_t) value;
58 case GET_TIMEOUT:
59 ret = (void *) tmoh;
60 break;
61 case SET_OPENTIMEOUT:
62 ttmo_open = (long) value;
63 case GET_OPENTIMEOUT:
64 ret = (void *) ttmo_open;
65 break;
66 case SET_READTIMEOUT:
67 ttmo_read = (long) value;
68 case GET_READTIMEOUT:
69 ret = (void *) ttmo_read;
70 break;
71 case SET_WRITETIMEOUT:
72 ttmo_write = (long) value;
73 case GET_WRITETIMEOUT:
74 ret = (void *) ttmo_write;
75 break;
76 case SET_ALLOWREVERSEDNS:
77 allowreversedns = (long) value;
78 case GET_ALLOWREVERSEDNS:
79 ret = (void *) allowreversedns;
80 break;
81 case SET_TCPDEBUG:
82 tcpdebug = (long) value;
83 case GET_TCPDEBUG:
84 ret = (void *) tcpdebug;
85 break;
87 return ret;
90 /* TCP/IP open
91 * Accepts: host name
92 * contact service name
93 * contact port number and optional silent flag
94 * Returns: TCP/IP stream if success else NIL
97 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
99 TCPSTREAM *stream = NIL;
100 int i;
101 int sock = -1;
102 int ctr = 0;
103 int silent = (port & NET_SILENT) ? T : NIL;
104 int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
105 char *s;
106 struct sockaddr_in sin;
107 struct hostent *he;
108 char hostname[MAILTMPLEN];
109 char tmp[MAILTMPLEN];
110 struct servent *sv = NIL;
111 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
112 void *data;
113 port &= 0xffff; /* erase flags */
114 /* lookup service */
115 if (service && (sv = getservbyname (service,"tcp")))
116 port = ntohs (sin.sin_port = sv->s_port);
117 /* copy port number in network format */
118 else sin.sin_port = htons (port);
119 /* The domain literal form is used (rather than simply the dotted decimal
120 as with other Amiga programs) because it has to be a valid "host name"
121 in mailsystem terminology. */
122 /* look like domain literal? */
123 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
124 strcpy (hostname,host+1); /* yes, copy number part */
125 hostname[(strlen (hostname))-1] = '\0';
126 if ((sin.sin_addr.s_addr = inet_addr (hostname)) == -1)
127 sprintf (tmp,"Bad format domain-literal: %.80s",host);
128 else {
129 sin.sin_family = AF_INET; /* family is always Internet */
130 strcpy (hostname,host); /* hostname is user's argument */
131 (*bn) (BLOCK_TCPOPEN,NIL);
132 /* get an open socket for this system */
133 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
134 (*bn) (BLOCK_NONE,NIL);
138 else { /* lookup host name */
139 if (tcpdebug) {
140 sprintf (tmp,"DNS resolution %.80s",host);
141 mm_log (tmp,TCPDEBUG);
143 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
144 data = (*bn) (BLOCK_SENSITIVE,NIL);
145 if (!(he = gethostbyname (lcase (strcpy (hostname,host)))))
146 sprintf (tmp,"No such host as %.80s",host);
147 (*bn) (BLOCK_NONSENSITIVE,data);
148 (*bn) (BLOCK_NONE,NIL);
149 if (he) { /* DNS resolution won? */
150 if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
151 /* copy address type */
152 sin.sin_family = he->h_addrtype;
153 /* copy host name */
154 strcpy (hostname,he->h_name);
155 #ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */
156 for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) {
157 if (i && !silent) mm_log (tmp,WARN);
158 memcpy (&sin.sin_addr,s,he->h_length);
159 (*bn) (BLOCK_TCPOPEN,NIL);
160 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
161 (*bn) (BLOCK_NONE,NIL);
163 #else /* the one true address then */
164 memcpy (&sin.sin_addr,he->h_addr,he->h_length);
165 (*bn) (BLOCK_TCPOPEN,NIL);
166 sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
167 (*bn) (BLOCK_NONE,NIL);
168 #endif
171 if (sock >= 0) { /* won */
172 stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
173 sizeof (TCPSTREAM));
174 stream->port = port; /* port number */
175 /* init sockets */
176 stream->tcpsi = stream->tcpso = sock;
177 /* stash in the snuck-in byte */
178 if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
179 /* copy official host name */
180 stream->host = cpystr (hostname);
181 if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
183 else if (!silent) mm_log (tmp,ERROR);
184 return stream; /* return success */
187 /* Open a TCP socket
188 * Accepts: Internet socket address block
189 * scratch buffer
190 * pointer to "first byte read in" storage or NIL
191 * host name for error message
192 * port number for error message
193 * Returns: socket if success, else -1 with error string in scratch buffer
196 int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
197 unsigned long port)
199 int i,ti,sock,flgs;
200 time_t now;
201 struct protoent *pt = getprotobyname ("tcp");
202 fd_set fds,efds;
203 struct timeval tmo;
204 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
205 void *data = (*bn) (BLOCK_SENSITIVE,NIL);
206 sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
207 mm_log (tmp,NIL);
208 /* make a socket */
209 if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
210 sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
211 (*bn) (BLOCK_NONSENSITIVE,data);
212 return -1;
214 else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
215 sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
216 sock,FD_SETSIZE);
217 (*bn) (BLOCK_NONSENSITIVE,data);
218 close (sock);
219 errno = EMFILE;
220 return -1;
222 flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
223 /* set non-blocking if want open timeout */
224 if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
225 /* open connection */
226 while ((i = connect (sock,(struct sockaddr *) sin,
227 sizeof (struct sockaddr_in))) < 0 && (errno == EINTR));
228 (*bn) (BLOCK_NONSENSITIVE,data);
229 if (i < 0) switch (errno) { /* failed? */
230 case EAGAIN: /* DG brain damage */
231 case EINPROGRESS: /* what we expect to happen */
232 case EALREADY: /* or another form of it */
233 case EISCONN: /* restart after interrupt? */
234 case EADDRINUSE: /* restart after interrupt? */
235 break; /* well, not really, it was interrupted */
236 default:
237 sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
238 close (sock); /* flush socket */
239 return -1;
242 if (ctr) { /* want open timeout */
243 now = time (0); /* open timeout */
244 ti = ttmo_open ? now + ttmo_open : 0;
245 tmo.tv_usec = 0;
246 FD_ZERO (&fds); /* initialize selection vector */
247 FD_ZERO (&efds); /* handle errors too */
248 FD_SET (sock,&fds); /* block for error or readable */
249 FD_SET (sock,&efds);
250 do { /* block under timeout */
251 tmo.tv_sec = ti ? ti - now : 0;
252 i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
253 now = time (0); /* fake timeout if interrupt & time expired */
254 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
255 } while ((i < 0) && (errno == EINTR));
256 if (i > 0) { /* success, make sure really connected */
257 fcntl (sock,F_SETFL,flgs);/* restore blocking status */
258 /* This used to be a zero-byte read(), but that crashes Solaris */
259 /* get socket status */
260 while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
262 if (i <= 0) { /* timeout or error? */
263 i = i ? errno : ETIMEDOUT;/* determine error code */
264 close (sock); /* flush socket */
265 errno = i; /* return error code */
266 sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port,
267 strerror (errno));
268 return -1;
271 return sock; /* return the socket */
274 /* TCP/IP authenticated open
275 * Accepts: host name
276 * service name
277 * returned user name buffer
278 * Returns: TCP/IP stream if success else NIL
281 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
283 return NIL; /* disabled */
286 /* TCP receive line
287 * Accepts: TCP stream
288 * Returns: text line string or NIL if failure
291 char *tcp_getline (TCPSTREAM *stream)
293 unsigned long n,contd;
294 char *ret = tcp_getline_work (stream,&n,&contd);
295 if (ret && contd) { /* got a line needing continuation? */
296 STRINGLIST *stl = mail_newstringlist ();
297 STRINGLIST *stc = stl;
298 do { /* collect additional lines */
299 stc->text.data = (unsigned char *) ret;
300 stc->text.size = n;
301 stc = stc->next = mail_newstringlist ();
302 ret = tcp_getline_work (stream,&n,&contd);
303 } while (ret && contd);
304 if (ret) { /* stash final part of line on list */
305 stc->text.data = (unsigned char *) ret;
306 stc->text.size = n;
307 /* determine how large a buffer we need */
308 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
309 ret = fs_get (n + 1); /* copy parts into buffer */
310 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
311 memcpy (ret + n,stc->text.data,stc->text.size);
312 ret[n] = '\0';
314 mail_free_stringlist (&stl);/* either way, done with list */
316 return ret;
319 /* TCP receive line or partial line
320 * Accepts: TCP stream
321 * pointer to return size
322 * pointer to return continuation flag
323 * Returns: text line string, size and continuation flag, or NIL if failure
326 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
327 long *contd)
329 unsigned long n;
330 char *s,*ret,c,d;
331 *contd = NIL; /* assume no continuation */
332 /* make sure have data */
333 if (!tcp_getdata (stream)) return NIL;
334 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
335 d = *stream->iptr++; /* slurp another character */
336 if ((c == '\015') && (d == '\012')) {
337 ret = (char *) fs_get (n--);
338 memcpy (ret,s,*size = n); /* copy into a free storage string */
339 ret[n] = '\0'; /* tie off string with null */
340 return ret;
343 /* copy partial string from buffer */
344 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
345 /* get more data from the net */
346 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
347 /* special case of newline broken by buffer */
348 else if ((c == '\015') && (*stream->iptr == '\012')) {
349 stream->iptr++; /* eat the line feed */
350 stream->ictr--;
351 ret[*size = --n] = '\0'; /* tie off string with null */
353 else *contd = LONGT; /* continuation needed */
354 return ret;
357 /* TCP/IP receive buffer
358 * Accepts: TCP/IP stream
359 * size in bytes
360 * buffer to read into
361 * Returns: T if success, NIL otherwise
364 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
366 unsigned long n;
367 /* make sure socket still alive */
368 if (stream->tcpsi < 0) return NIL;
369 /* can transfer bytes from buffer? */
370 if (n = min (size,stream->ictr)) {
371 memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
372 s += n; /* update pointer */
373 stream->iptr +=n;
374 size -= n; /* update # of bytes to do */
375 stream->ictr -=n;
377 if (size) {
378 int i;
379 fd_set fds,efds;
380 struct timeval tmo;
381 time_t t = time (0);
382 blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
383 (*bn) (BLOCK_TCPREAD,NIL);
384 while (size > 0) { /* until request satisfied */
385 time_t tl = time (0);
386 time_t now = tl;
387 time_t ti = ttmo_read ? now + ttmo_read : 0;
388 if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
389 tmo.tv_usec = 0;
390 FD_ZERO (&fds); /* initialize selection vector */
391 FD_ZERO (&efds); /* handle errors too */
392 FD_SET (stream->tcpsi,&fds);
393 FD_SET (stream->tcpsi,&efds);
394 errno = NIL; /* block and read */
395 do { /* block under timeout */
396 tmo.tv_sec = ti ? ti - now : 0;
397 i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
398 now = time (0); /* fake timeout if interrupt & time expired */
399 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
400 } while ((i < 0) && (errno == EINTR));
401 if (i > 0) { /* select says there's data to read? */
402 while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
403 (errno == EINTR));
404 if (i < 1) return tcp_abort (stream);
405 s += i; /* point at new place to write */
406 size -= i; /* reduce byte count */
407 if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
409 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
410 return tcp_abort (stream);
412 (*bn) (BLOCK_NONE,NIL);
414 *s = '\0'; /* tie off string */
415 return T;
418 /* TCP/IP receive data
419 * Accepts: TCP/IP stream
420 * Returns: T if success, NIL otherwise
423 long tcp_getdata (TCPSTREAM *stream)
425 int i;
426 fd_set fds,efds;
427 struct timeval tmo;
428 time_t t = time (0);
429 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
430 if (stream->tcpsi < 0) return NIL;
431 (*bn) (BLOCK_TCPREAD,NIL);
432 while (stream->ictr < 1) { /* if nothing in the buffer */
433 time_t tl = time (0); /* start of request */
434 time_t now = tl;
435 time_t ti = ttmo_read ? now + ttmo_read : 0;
436 if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
437 tmo.tv_usec = 0;
438 FD_ZERO (&fds); /* initialize selection vector */
439 FD_ZERO (&efds); /* handle errors too */
440 FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
441 FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
442 errno = NIL; /* block and read */
443 do { /* block under timeout */
444 tmo.tv_sec = ti ? ti - now : 0;
445 i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
446 now = time (0); /* fake timeout if interrupt & time expired */
447 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
448 } while ((i < 0) && (errno == EINTR));
449 if (i > 0) { /* got data? */
450 while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
451 (errno == EINTR));
452 if (i < 1) return tcp_abort (stream);
453 stream->iptr = stream->ibuf;/* point at TCP buffer */
454 stream->ictr = i; /* set new byte count */
455 if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
457 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
458 return tcp_abort (stream);/* error or timeout no-continue */
460 (*bn) (BLOCK_NONE,NIL);
461 return T;
464 /* TCP/IP send string as record
465 * Accepts: TCP/IP stream
466 * string pointer
467 * Returns: T if success else NIL
470 long tcp_soutr (TCPSTREAM *stream,char *string)
472 return tcp_sout (stream,string,(unsigned long) strlen (string));
476 /* TCP/IP send string
477 * Accepts: TCP/IP stream
478 * string pointer
479 * byte count
480 * Returns: T if success else NIL
483 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
485 int i;
486 fd_set fds,efds;
487 struct timeval tmo;
488 time_t t = time (0);
489 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
490 if (stream->tcpso < 0) return NIL;
491 (*bn) (BLOCK_TCPWRITE,NIL);
492 while (size > 0) { /* until request satisfied */
493 time_t tl = time (0); /* start of request */
494 time_t now = tl;
495 time_t ti = ttmo_write ? now + ttmo_write : 0;
496 if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
497 tmo.tv_usec = 0;
498 FD_ZERO (&fds); /* initialize selection vector */
499 FD_ZERO (&efds); /* handle errors too */
500 FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
501 FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
502 errno = NIL; /* block and write */
503 do { /* block under timeout */
504 tmo.tv_sec = ti ? ti - now : 0;
505 i = select (stream->tcpso+1,0,&fds,&efds,ti ? &tmo : 0);
506 now = time (0); /* fake timeout if interrupt & time expired */
507 if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
508 } while ((i < 0) && (errno == EINTR));
509 if (i > 0) { /* OK to send data? */
510 while (((i = write (stream->tcpso,string,size)) < 0) &&(errno == EINTR));
511 if (i < 0) return tcp_abort (stream);
512 size -= i; /* how much we sent */
513 string += i;
514 if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
516 else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
517 return tcp_abort (stream);/* error or timeout no-continue */
519 (*bn) (BLOCK_NONE,NIL);
520 return T; /* all done */
523 /* TCP/IP close
524 * Accepts: TCP/IP stream
527 void tcp_close (TCPSTREAM *stream)
529 tcp_abort (stream); /* nuke the stream */
530 /* flush host names */
531 if (stream->host) fs_give ((void **) &stream->host);
532 if (stream->remotehost) fs_give ((void **) &stream->remotehost);
533 if (stream->localhost) fs_give ((void **) &stream->localhost);
534 fs_give ((void **) &stream); /* flush the stream */
538 /* TCP/IP abort stream
539 * Accepts: TCP/IP stream
540 * Returns: NIL always
543 long tcp_abort (TCPSTREAM *stream)
545 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
546 if (stream->tcpsi >= 0) { /* no-op if no socket */
547 (*bn) (BLOCK_TCPCLOSE,NIL);
548 close (stream->tcpsi); /* nuke the socket */
549 if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
550 stream->tcpsi = stream->tcpso = -1;
552 (*bn) (BLOCK_NONE,NIL);
553 return NIL;
556 /* TCP/IP get host name
557 * Accepts: TCP/IP stream
558 * Returns: host name for this stream
561 char *tcp_host (TCPSTREAM *stream)
563 return stream->host; /* use tcp_remotehost() if want guarantees */
567 /* TCP/IP get remote host name
568 * Accepts: TCP/IP stream
569 * Returns: host name for this stream
572 char *tcp_remotehost (TCPSTREAM *stream)
574 if (!stream->remotehost) {
575 struct sockaddr_in sin;
576 int sinlen = sizeof (struct sockaddr_in);
577 stream->remotehost = /* get socket's peer name */
578 (getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
579 (sin.sin_family != AF_INET)) ?
580 cpystr (stream->host) : tcp_name (&sin,NIL);
582 return stream->remotehost;
586 /* TCP/IP return port for this stream
587 * Accepts: TCP/IP stream
588 * Returns: port number for this stream
591 unsigned long tcp_port (TCPSTREAM *stream)
593 return stream->port; /* return port number */
597 /* TCP/IP get local host name
598 * Accepts: TCP/IP stream
599 * Returns: local host name
602 char *tcp_localhost (TCPSTREAM *stream)
604 if (!stream->localhost) {
605 struct sockaddr_in sin;
606 int sinlen = sizeof (struct sockaddr_in);
607 stream->localhost = /* get socket's name */
608 ((stream->port & 0xffff000) ||
609 getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
610 (sin.sin_family != AF_INET)) ?
611 cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
613 return stream->localhost; /* return local host name */
616 /* TCP/IP get client host address (server calls only)
617 * Returns: client host address
620 static char *myClientAddr = NIL;
622 char *tcp_clientaddr ()
624 if (!myClientAddr) {
625 struct sockaddr_in sin;
626 int sinlen = sizeof (struct sockaddr_in);
627 myClientAddr = /* get stdin's peer name */
628 cpystr (getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
629 "UNKNOWN" : ((sin.sin_family == AF_INET) ?
630 inet_ntoa (sin.sin_addr) : "NON-IPv4"));
632 return myClientAddr;
636 /* TCP/IP get client host name (server calls only)
637 * Returns: client host name
640 static char *myClientHost = NIL;
642 char *tcp_clienthost ()
644 if (!myClientHost) {
645 struct sockaddr_in sin;
646 int sinlen = sizeof (struct sockaddr_in);
647 myClientHost = /* get stdin's peer name */
648 getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
649 cpystr ("UNKNOWN") : ((sin.sin_family == AF_INET) ?
650 tcp_name (&sin,T) : cpystr ("NON-IPv4"));
652 return myClientHost;
655 /* TCP/IP get server host address (server calls only)
656 * Returns: server host address
659 static char *myServerAddr = NIL;
661 char *tcp_serveraddr ()
663 if (!myServerAddr) {
664 struct sockaddr_in sin;
665 int sinlen = sizeof (struct sockaddr_in);
666 myServerAddr = /* get stdin's peer name */
667 cpystr (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
668 "UNKNOWN" : ((sin.sin_family == AF_INET) ?
669 inet_ntoa (sin.sin_addr) : "NON-IPv4"));
671 return myServerAddr;
675 /* TCP/IP get server host name (server calls only)
676 * Returns: server host name
679 static char *myServerHost = NIL;
680 static long myServerPort = -1;
682 char *tcp_serverhost ()
684 if (!myServerHost) {
685 struct sockaddr_in sin;
686 int sinlen = sizeof (struct sockaddr_in);
687 /* get stdin's name */
688 if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ||
689 (sin.sin_family != AF_INET)) myServerHost = cpystr (mylocalhost ());
690 else {
691 myServerHost = tcp_name (&sin,NIL);
692 myServerPort = ntohs (sin.sin_port);
695 return myServerHost;
699 /* TCP/IP get server port number (server calls only)
700 * Returns: server port number
703 long tcp_serverport ()
705 if (!myServerHost) tcp_serverhost ();
706 return myServerPort;
709 /* TCP/IP return canonical form of host name
710 * Accepts: host name
711 * Returns: canonical form of host name
714 char *tcp_canonical (char *name)
716 char *ret,host[MAILTMPLEN];
717 struct hostent *he;
718 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
719 void *data;
720 /* look like domain literal? */
721 if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
722 (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
723 data = (*bn) (BLOCK_SENSITIVE,NIL);
724 if (tcpdebug) {
725 sprintf (host,"DNS canonicalization %.80s",name);
726 mm_log (host,TCPDEBUG);
728 /* note that Amiga requires lowercase! */
729 ret = (he = gethostbyname (lcase (strcpy (host,name)))) ?
730 (char *) he->h_name : name;
731 (*bn) (BLOCK_NONSENSITIVE,data);
732 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
733 if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
734 return cpystr (ret);
738 /* TCP/IP return name from socket
739 * Accepts: socket
740 * verbose flag
741 * Returns: cpystr name
744 char *tcp_name (struct sockaddr_in *sin,long flag)
746 char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
747 sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
748 if (allowreversedns) {
749 struct hostent *he;
750 blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
751 void *data;
752 if (tcpdebug) {
753 sprintf (tmp,"Reverse DNS resolution %s",adr);
754 mm_log (tmp,TCPDEBUG);
756 (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
757 data = (*bn) (BLOCK_SENSITIVE,NIL);
758 /* translate address to name */
759 if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
760 sizeof (struct in_addr),
761 sin->sin_family)) ?
762 (char *) he->h_name : NIL)) {
763 /* produce verbose form if needed */
764 if (flag) sprintf (ret = tmp,"%s %s",t,adr);
765 else ret = t;
767 (*bn) (BLOCK_NONSENSITIVE,data);
768 (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
769 if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
771 return cpystr (ret);
775 /* Validate name
776 * Accepts: domain name
777 * Returns: T if valid, NIL otherwise
780 char *tcp_name_valid (char *s)
782 int c;
783 char *ret,*tail;
784 /* must be non-empty and not too long */
785 if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
786 /* must be alnum, dot, or hyphen */
787 while ((c = *s++) && (s <= tail) &&
788 (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
789 ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
790 if (c) ret = NIL;
792 return ret;