1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
7 * Program: Amiga TCP/IP routines
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
,
40 static char *tcp_getline_work (TCPSTREAM
*stream
,unsigned long *size
,
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
)
55 switch ((int) function
) {
57 tmoh
= (tcptimeout_t
) value
;
62 ttmo_open
= (long) value
;
64 ret
= (void *) ttmo_open
;
67 ttmo_read
= (long) value
;
69 ret
= (void *) ttmo_read
;
71 case SET_WRITETIMEOUT
:
72 ttmo_write
= (long) value
;
73 case GET_WRITETIMEOUT
:
74 ret
= (void *) ttmo_write
;
76 case SET_ALLOWREVERSEDNS
:
77 allowreversedns
= (long) value
;
78 case GET_ALLOWREVERSEDNS
:
79 ret
= (void *) allowreversedns
;
82 tcpdebug
= (long) value
;
84 ret
= (void *) tcpdebug
;
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
;
103 int silent
= (port
& NET_SILENT
) ? T
: NIL
;
104 int *ctrp
= (port
& NET_NOOPENTIMEOUT
) ? NIL
: &ctr
;
106 struct sockaddr_in sin
;
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
);
113 port
&= 0xffff; /* erase flags */
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
);
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 */
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
;
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
);
171 if (sock
>= 0) { /* won */
172 stream
= (TCPSTREAM
*) memset (fs_get (sizeof (TCPSTREAM
)),0,
174 stream
->port
= port
; /* port number */
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 */
188 * Accepts: Internet socket address block
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
,
201 struct protoent
*pt
= getprotobyname ("tcp");
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
));
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
);
214 else if (sock
>= FD_SETSIZE
) {/* unselectable sockets are useless */
215 sprintf (tmp
,"Unable to create selectable TCP socket (%d >= %d)",
217 (*bn
) (BLOCK_NONSENSITIVE
,data
);
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 */
237 sprintf (tmp
,"Can't connect to %.80s,%lu: %s",hst
,port
,strerror (errno
));
238 close (sock
); /* flush socket */
242 if (ctr
) { /* want open timeout */
243 now
= time (0); /* open timeout */
244 ti
= ttmo_open
? now
+ ttmo_open
: 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 */
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
,
271 return sock
; /* return the socket */
274 /* TCP/IP authenticated open
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 */
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
;
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
;
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
);
314 mail_free_stringlist (&stl
);/* either way, done with list */
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
,
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 */
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 */
351 ret
[*size
= --n
] = '\0'; /* tie off string with null */
353 else *contd
= LONGT
; /* continuation needed */
357 /* TCP/IP receive buffer
358 * Accepts: TCP/IP stream
360 * buffer to read into
361 * Returns: T if success, NIL otherwise
364 long tcp_getbuffer (TCPSTREAM
*stream
,unsigned long size
,char *s
)
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 */
374 size
-= n
; /* update # of bytes to do */
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);
387 time_t ti
= ttmo_read
? now
+ ttmo_read
: 0;
388 if (tcpdebug
) mm_log ("Reading TCP buffer",TCPDEBUG
);
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) &&
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 */
418 /* TCP/IP receive data
419 * Accepts: TCP/IP stream
420 * Returns: T if success, NIL otherwise
423 long tcp_getdata (TCPSTREAM
*stream
)
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 */
435 time_t ti
= ttmo_read
? now
+ ttmo_read
: 0;
436 if (tcpdebug
) mm_log ("Reading TCP data",TCPDEBUG
);
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) &&
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
);
464 /* TCP/IP send string as record
465 * Accepts: TCP/IP stream
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
480 * Returns: T if success else NIL
483 long tcp_sout (TCPSTREAM
*stream
,char *string
,unsigned long size
)
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 */
495 time_t ti
= ttmo_write
? now
+ ttmo_write
: 0;
496 if (tcpdebug
) mm_log ("Writing to TCP",TCPDEBUG
);
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 */
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 */
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
);
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 ()
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"));
636 /* TCP/IP get client host name (server calls only)
637 * Returns: client host name
640 static char *myClientHost
= NIL
;
642 char *tcp_clienthost ()
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"));
655 /* TCP/IP get server host address (server calls only)
656 * Returns: server host address
659 static char *myServerAddr
= NIL
;
661 char *tcp_serveraddr ()
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"));
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 ()
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 ());
691 myServerHost
= tcp_name (&sin
,NIL
);
692 myServerPort
= ntohs (sin
.sin_port
);
699 /* TCP/IP get server port number (server calls only)
700 * Returns: server port number
703 long tcp_serverport ()
705 if (!myServerHost
) tcp_serverhost ();
709 /* TCP/IP return canonical form of host name
711 * Returns: canonical form of host name
714 char *tcp_canonical (char *name
)
716 char *ret
,host
[MAILTMPLEN
];
718 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
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
);
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
);
738 /* TCP/IP return name from socket
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
) {
750 blocknotify_t bn
= (blocknotify_t
)mail_parameters(NIL
,GET_BLOCKNOTIFY
,NIL
);
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
),
762 (char *) he
->h_name
: NIL
)) {
763 /* produce verbose form if needed */
764 if (flag
) sprintf (ret
= tmp
,"%s %s",t
,adr
);
767 (*bn
) (BLOCK_NONSENSITIVE
,data
);
768 (*bn
) (BLOCK_NONE
,NIL
); /* alarms OK now */
769 if (tcpdebug
) mm_log ("Reverse DNS resolution done",TCPDEBUG
);
776 * Accepts: domain name
777 * Returns: T if valid, NIL otherwise
780 char *tcp_name_valid (char *s
)
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
== '.')));