select() -> poll(). Bug 2831
[exim.git] / src / src / ip.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 - 2021 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* Functions for doing things with sockets. With the advent of IPv6 this has
10 got messier, so that it's worth pulling out the code into separate functions
11 that other parts of Exim can call, especially as there are now several
12 different places in the code where sockets are used. */
13
14
15 #include "exim.h"
16
17
18 #if defined(TCP_FASTOPEN)
19 # if defined(MSG_FASTOPEN) || defined(EXIM_TFO_CONNECTX) || defined(EXIM_TFO_FREEBSD)
20 #  define EXIM_SUPPORT_TFO
21 # endif
22 #endif
23
24 /*************************************************
25 *             Create a socket                    *
26 *************************************************/
27
28 /* Socket creation happens in a number of places so it's packaged here for
29 convenience.
30
31 Arguments:
32   type       SOCK_DGRAM or SOCK_STREAM
33   af         AF_INET or AF_INET6
34
35 Returns:     socket number or -1 on failure
36 */
37
38 int
39 ip_socket(int type, int af)
40 {
41 int sock = socket(af, type, 0);
42 if (sock < 0)
43   log_write(0, LOG_MAIN, "IPv%c socket creation failed: %s",
44     (af == AF_INET6)? '6':'4', strerror(errno));
45 return sock;
46 }
47
48
49
50
51 #if HAVE_IPV6
52 /*************************************************
53 *      Convert printing address to numeric       *
54 *************************************************/
55
56 /* This function converts the textual form of an IP address into a numeric form
57 in an appropriate structure in an IPv6 environment. The getaddrinfo() function
58 can (apparently) handle more complicated addresses (e.g. those containing
59 scopes) than inet_pton() in some environments. We use hints to tell it that the
60 input must be a numeric address.
61
62 However, apparently some operating systems (or libraries) don't support
63 getaddrinfo(), so there is a build-time option to revert to inet_pton() (which
64 does not support scopes).
65
66 Arguments:
67   address     textual form of the address
68   addr        where to copy back the answer
69
70 Returns:      nothing - failure provokes a panic-die
71 */
72
73 static void
74 ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr)
75 {
76 #ifdef IPV6_USE_INET_PTON
77
78   if (inet_pton(AF_INET6, CCS address, &saddr->sin6_addr) != 1)
79     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an "
80       "IP address", address);
81   saddr->sin6_family = AF_INET6;
82
83 #else
84
85   int rc;
86   struct addrinfo hints, *res;
87   memset(&hints, 0, sizeof(hints));
88   hints.ai_family = AF_INET6;
89   hints.ai_socktype = SOCK_STREAM;
90   hints.ai_flags = AI_NUMERICHOST;
91   if ((rc = getaddrinfo(CCS address, NULL, &hints, &res)) != 0 || res == NULL)
92     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an "
93       "IP address: %s", address,
94       (rc == 0)? "NULL result returned" : gai_strerror(rc));
95   memcpy(saddr, res->ai_addr, res->ai_addrlen);
96   freeaddrinfo(res);
97
98 #endif
99 }
100 #endif  /* HAVE_IPV6 */
101
102
103 /*************************************************
104 *         Bind socket to interface and port      *
105 *************************************************/
106
107 int
108 ip_addr(void * sin_, int af, const uschar * address, int port)
109 {
110 union sockaddr_46 * sin = sin_;
111 memset(sin, 0, sizeof(*sin));
112
113 /* Setup code when using an IPv6 socket. The wildcard address is ":", to
114 ensure an IPv6 socket is used. */
115
116 #if HAVE_IPV6
117 if (af == AF_INET6)
118   {
119   if (address[0] == ':' && address[1] == 0)
120     {
121     sin->v6.sin6_family = AF_INET6;
122     sin->v6.sin6_addr = in6addr_any;
123     }
124   else
125     ip_addrinfo(address, &sin->v6);  /* Panic-dies on error */
126   sin->v6.sin6_port = htons(port);
127   return sizeof(sin->v6);
128   }
129 else
130 #endif    /* HAVE_IPV6 */
131
132 /* Setup code when using IPv4 socket. The wildcard address is "". */
133
134   {
135   sin->v4.sin_family = AF_INET;
136   sin->v4.sin_port = htons(port);
137   sin->v4.sin_addr.s_addr = address[0] == 0
138     ? (S_ADDR_TYPE)INADDR_ANY
139     : (S_ADDR_TYPE)inet_addr(CS address);
140   return sizeof(sin->v4);
141   }
142 }
143
144
145
146 /* This function binds a socket to a local interface address and port. For a
147 wildcard IPv6 bind, the address is ":".
148
149 Arguments:
150   sock           the socket
151   af             AF_INET or AF_INET6 - the socket type
152   address        the IP address, in text form
153   port           the IP port (host order)
154
155 Returns:         the result of bind()
156 */
157
158 int
159 ip_bind(int sock, int af, uschar *address, int port)
160 {
161 union sockaddr_46 sin;
162 int s_len = ip_addr(&sin, af, address, port);
163 return bind(sock, (struct sockaddr *)&sin, s_len);
164 }
165
166
167
168 /*************************************************
169 *        Connect socket to remote host           *
170 *************************************************/
171
172 /* This function connects a socket to a remote address and port. The socket may
173 or may not have previously been bound to a local interface. The socket is not
174 closed, even in cases of error. It is expected that the calling function, which
175 created the socket, will be the one that closes it.
176
177 Arguments:
178   sock        the socket
179   af          AF_INET6 or AF_INET for the socket type
180   address     the remote address, in text form
181   port        the remote port
182   timeout     a timeout (zero for indefinite timeout)
183   fastopen_blob    non-null iff TCP_FASTOPEN can be used; may indicate early-data to
184                 be sent in SYN segment.  Any such data must be idempotent.
185
186 Returns:      0 on success; -1 on failure, with errno set
187 */
188
189 int
190 ip_connect(int sock, int af, const uschar *address, int port, int timeout,
191   const blob * fastopen_blob)
192 {
193 struct sockaddr_in s_in4;
194 struct sockaddr *s_ptr;
195 int s_len, rc, save_errno;
196
197 /* For an IPv6 address, use an IPv6 sockaddr structure. */
198
199 #if HAVE_IPV6
200 struct sockaddr_in6 s_in6;
201 if (af == AF_INET6)
202   {
203   memset(&s_in6, 0, sizeof(s_in6));
204   ip_addrinfo(address, &s_in6);   /* Panic-dies on error */
205   s_in6.sin6_port = htons(port);
206   s_ptr = (struct sockaddr *)&s_in6;
207   s_len = sizeof(s_in6);
208   }
209 else
210 #endif    /* HAVE_IPV6 */
211
212 /* For an IPv4 address, use an IPv4 sockaddr structure, even on a system with
213 IPv6 support. */
214
215   {
216   memset(&s_in4, 0, sizeof(s_in4));
217   s_in4.sin_family = AF_INET;
218   s_in4.sin_port = htons(port);
219   s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CCS address);
220   s_ptr = (struct sockaddr *)&s_in4;
221   s_len = sizeof(s_in4);
222   }
223
224 /* If no connection timeout is set, just call connect() without setting a
225 timer, thereby allowing the inbuilt OS timeout to operate. */
226
227 callout_address = string_sprintf("[%s]:%d", address, port);
228 sigalrm_seen = FALSE;
229 if (timeout > 0) ALARM(timeout);
230
231 #ifdef EXIM_SUPPORT_TFO
232 /* TCP Fast Open, if the system has a cookie from a previous call to
233 this peer, can send data in the SYN packet.  The peer can send data
234 before it gets our ACK of its SYN,ACK - the latter is useful for
235 the SMTP banner.  Other (than SMTP) cases of TCP connections can
236 possibly use the data-on-syn, so support that too. */
237
238 if (fastopen_blob && f.tcp_fastopen_ok)
239   {
240 # ifdef MSG_FASTOPEN
241   /* This is a Linux implementation. */
242
243   if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len,
244                     MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0)
245         /* seen for with-data, experimental TFO option, with-cookie case */
246         /* seen for with-data, proper TFO opt, with-cookie case */
247     {
248     DEBUG(D_transport|D_v)
249       debug_printf(" TFO mode connection attempt to %s, %lu data\n",
250         address, (unsigned long)fastopen_blob->len);
251     /*XXX also seen on successful TFO, sigh */
252     tcp_out_fastopen = fastopen_blob->len > 0 ?  TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
253     }
254   else switch (errno)
255     {
256     case EINPROGRESS:   /* expected if we had no cookie for peer */
257         /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
258         /*  apparently no visibility of the diffference at this point */
259         /* seen for with-data, proper TFO opt, cookie-req */
260         /*   with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */
261         /* ? older Experimental TFO option behaviour ? */
262       DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n",
263         fastopen_blob->len > 0 ? "with"  : "no");
264       if (!fastopen_blob->data)
265         {
266         tcp_out_fastopen = TFO_ATTEMPTED_NODATA;                /* we tried; unknown if useful yet */
267         rc = 0;
268         }
269       else                                      /* queue unsent data */
270         rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
271       break;
272
273     case EOPNOTSUPP:
274       DEBUG(D_transport)
275         debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
276       goto legacy_connect;
277
278     case EPIPE:
279       DEBUG(D_transport)
280         debug_printf("Tried TCP Fast Open but kernel too old to support it\n");
281       goto legacy_connect;
282     }
283
284 # elif defined(EXIM_TFO_FREEBSD)
285   /* Re: https://people.freebsd.org/~pkelsey/tfo-tools/tfo-client.c */
286
287   if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &on, sizeof(on)) < 0)
288     {
289     DEBUG(D_transport)
290       debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
291     goto legacy_connect;
292     }
293   if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, 0,
294                     s_ptr, s_len)) >= 0)
295     {
296     DEBUG(D_transport|D_v)
297       debug_printf(" TFO mode connection attempt to %s, %lu data\n",
298         address, (unsigned long)fastopen_blob->len);
299     tcp_out_fastopen = fastopen_blob->len > 0 ?  TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
300     }
301
302 # elif defined(EXIM_TFO_CONNECTX)
303   /* MacOS */
304   sa_endpoints_t ends = {
305     .sae_srcif = 0, .sae_srcaddr = NULL, .sae_srcaddrlen = 0,
306     .sae_dstaddr = s_ptr, .sae_dstaddrlen = s_len };
307   struct iovec iov = {
308     .iov_base = fastopen_blob->data, .iov_len = fastopen_blob->len };
309   size_t len;
310
311   if ((rc = connectx(sock, &ends, SAE_ASSOCID_ANY,
312              CONNECT_DATA_IDEMPOTENT, &iov, 1, &len, NULL)) == 0)
313     {
314     DEBUG(D_transport|D_v)
315       debug_printf(" TFO mode connection attempt to %s, %lu data\n",
316         address, (unsigned long)fastopen_blob->len);
317     tcp_out_fastopen = fastopen_blob->len > 0 ?  TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
318
319     if (len != fastopen_blob->len)
320       DEBUG(D_transport|D_v)
321         debug_printf(" only queued %lu data!\n", (unsigned long)len);
322     }
323   else if (errno == EINPROGRESS)
324     {
325     DEBUG(D_transport|D_v) debug_printf(" TFO mode connectx, %s data: EINPROGRESS\n",
326       fastopen_blob->len > 0 ? "with"  : "no");
327     if (!fastopen_blob->data)
328       {
329       tcp_out_fastopen = TFO_ATTEMPTED_NODATA;          /* we tried; unknown if useful yet */
330       rc = 0;
331       }
332     else        /* assume that no data was queued; block in send */
333       rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
334     }
335 # endif
336   }
337 else
338 #endif  /*EXIM_SUPPORT_TFO*/
339   {
340 #if defined(EXIM_SUPPORT_TFO) && !defined(EXIM_TFO_CONNECTX)
341 legacy_connect:
342 #endif
343
344   DEBUG(D_transport|D_v) if (fastopen_blob)
345     debug_printf(" non-TFO mode connection attempt to %s, %lu data\n",
346       address, (unsigned long)fastopen_blob->len);
347   if ((rc = connect(sock, s_ptr, s_len)) >= 0)
348     if (  fastopen_blob && fastopen_blob->data && fastopen_blob->len
349        && send(sock, fastopen_blob->data, fastopen_blob->len, 0) < 0)
350         rc = -1;
351   }
352
353 save_errno = errno;
354 ALARM_CLR(0);
355
356 /* There is a testing facility for simulating a connection timeout, as I
357 can't think of any other way of doing this. It converts a connection refused
358 into a timeout if the timeout is set to 999999. */
359
360 if (f.running_in_test_harness  && save_errno == ECONNREFUSED && timeout == 999999)
361   {
362   rc = -1;
363   save_errno = EINTR;
364   sigalrm_seen = TRUE;
365   }
366
367 /* Success */
368
369 if (rc >= 0)
370   return 0;
371
372 /* A failure whose error code is "Interrupted system call" is in fact
373 an externally applied timeout if the signal handler has been run. */
374
375 errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno;
376 return -1;
377 }
378
379
380
381 /*************************************************
382 *    Create connected socket to remote host      *
383 *************************************************/
384
385 /* Create a socket and connect to host (name or number, ipv6 ok)
386    at one of port-range.
387
388 Arguments:
389   type          SOCK_DGRAM or SOCK_STREAM
390   af            AF_INET6 or AF_INET for the socket type
391   hostname      host name, or ip address (as text)
392   portlo,porthi the remote port range
393   timeout       a timeout
394   connhost      if not NULL, host_item to be filled in with connection details
395   errstr        pointer for allocated string on error
396   fastopen_blob with SOCK_STREAM, if non-null, request TCP Fast Open.
397                 Additionally, optional idempotent early-data to send
398
399 Return:
400   socket fd, or -1 on failure (having allocated an error string)
401 */
402 int
403 ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
404       int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen_blob)
405 {
406 int namelen;
407 host_item shost;
408 int af = 0, fd, fd4 = -1, fd6 = -1;
409
410 shost.next = NULL;
411 shost.address = NULL;
412 shost.port = portlo;
413 shost.mx = -1;
414
415 namelen = Ustrlen(hostname);
416
417 /* Anything enclosed in [] must be an IP address. */
418
419 if (hostname[0] == '[' &&
420     hostname[namelen - 1] == ']')
421   {
422   uschar * host = string_copyn(hostname+1, namelen-2);
423   if (string_is_ip_address(host, NULL) == 0)
424     {
425     *errstr = string_sprintf("malformed IP address \"%s\"", hostname);
426     return -1;
427     }
428   shost.name = shost.address = host;
429   }
430
431 /* Otherwise check for an unadorned IP address */
432
433 else if (string_is_ip_address(hostname, NULL) != 0)
434   shost.name = shost.address = string_copyn(hostname, namelen);
435
436 /* Otherwise lookup IP address(es) from the name */
437
438 else
439   {
440   shost.name = string_copyn(hostname, namelen);
441   if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE,
442       NULL, FALSE) != HOST_FOUND)
443     {
444     *errstr = string_sprintf("no IP address found for host %s", shost.name);
445     return -1;
446     }
447   }
448
449 /* Try to connect to the server - test each IP till one works */
450
451 for (host_item * h = &shost; h; h = h->next)
452   {
453   fd = Ustrchr(h->address, ':') != 0
454     ? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
455     : fd4 < 0 ? (fd4 = ip_socket(type, af = AF_INET )) : fd4;
456
457   if (fd < 0)
458     {
459     *errstr = string_sprintf("failed to create socket: %s", strerror(errno));
460     goto bad;
461     }
462
463   for (int port = portlo; port <= porthi; port++)
464     if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
465       {
466       if (fd6 >= 0 && fd != fd6) close(fd6);
467       if (fd4 >= 0 && fd != fd4) close(fd4);
468       if (connhost)
469         {
470         h->port = port;
471         *connhost = *h;
472         connhost->next = NULL;
473         }
474       return fd;
475       }
476   }
477
478 *errstr = string_sprintf("failed to connect to any address for %s: %s",
479   hostname, strerror(errno));
480
481 bad:
482   close(fd4); close(fd6); return -1;
483 }
484
485
486 /*XXX TFO? */
487 int
488 ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo,
489   host_item * connhost)
490 {
491 int scan;
492 uschar hostname[256];
493 unsigned int portlow, porthigh;
494
495 /* extract host and port part */
496 scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
497 if (scan != 3)
498   {
499   if (scan != 2)
500     {
501     *errstr = string_sprintf("invalid socket '%s'", hostport);
502     return -1;
503     }
504   porthigh = portlow;
505   }
506
507 return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
508                           tmo, connhost, errstr, NULL);
509 }
510
511 int
512 ip_unixsocket(const uschar * path, uschar ** errstr)
513 {
514 int sock;
515 struct sockaddr_un server;
516
517 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
518   {
519   *errstr = US"can't open UNIX socket.";
520   return -1;
521   }
522
523 callout_address = string_copy(path);
524 server.sun_family = AF_UNIX;
525 Ustrncpy(US server.sun_path, path, sizeof(server.sun_path)-1);
526 server.sun_path[sizeof(server.sun_path)-1] = '\0';
527 if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
528   {
529   int err = errno;
530   (void)close(sock);
531   *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
532                 path, strerror(err));
533   return -1;
534   }
535 return sock;
536 }
537
538 /* spec is either an absolute path (with a leading /), or
539 a host (name or IP) and port (whitespace-separated).
540 The port can be a range, dash-separated, or a single number.
541
542 For a TCP socket, optionally fill in a  host_item.
543 */
544 int
545 ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo,
546   host_item * connhost)
547 {
548 return *spec == '/'
549   ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo, connhost);
550 }
551
552 /*************************************************
553 *         Set keepalive on a socket              *
554 *************************************************/
555
556 /* Can be called for both incoming and outgoing sockets.
557
558 Arguments:
559   sock       the socket
560   address    the remote host address, for failure logging
561   torf       true for outgoing connection, false for incoming
562
563 Returns:     nothing
564 */
565
566 void
567 ip_keepalive(int sock, const uschar *address, BOOL torf)
568 {
569 int fodder = 1;
570 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
571     US (&fodder), sizeof(fodder)) != 0)
572   log_write(0, LOG_MAIN, "setsockopt(SO_KEEPALIVE) on connection %s %s "
573     "failed: %s", torf? "to":"from", address, strerror(errno));
574 }
575
576
577
578 /*************************************************
579 *         Receive from a socket with timeout     *
580 *************************************************/
581
582 /*
583 Arguments:
584   fd          the file descriptor
585   timelimit   the timeout endpoint, seconds-since-epoch
586 Returns:      TRUE => ready for i/o
587               FALSE => timed out, or other error
588 */
589 BOOL
590 fd_ready(int fd, time_t timelimit)
591 {
592 int rc, time_left = timelimit - time(NULL);
593
594 if (time_left <= 0)
595   {
596   errno = ETIMEDOUT;
597   return FALSE;
598   }
599 /* Wait until the socket is ready */
600
601 do
602   {
603   /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
604   rc = poll_one_fd(fd, POLLIN, time_left * 1000);
605
606   /* If some interrupt arrived, just retry. We presume this to be rare,
607   but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
608   select() to exit).
609
610   Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
611   the interrupt not at all rare. Since the timeout is typically more than 2
612   minutes, the effect was to block the timeout completely. To prevent this
613   happening again, we do an explicit time test and adjust the timeout
614   accordingly */
615
616   if (rc < 0 && errno == EINTR)
617     {
618     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
619
620     /* Watch out, 'continue' jumps to the condition, not to the loops top */
621     if ((time_left = timelimit - time(NULL)) > 0) continue;
622     }
623
624   if (rc <= 0)
625     {
626     errno = ETIMEDOUT;
627     return FALSE;
628     }
629
630   /* Checking the FD_ISSET is not enough, if we're interrupted, the
631   select_inset may still contain the 'input'. */
632   }
633 while (rc < 0);
634 return TRUE;
635 }
636
637 /* The timeout is implemented using select(), and we loop to cover select()
638 getting interrupted, and the possibility of select() returning with a positive
639 result but no ready descriptor. Is this in fact possible?
640
641 Arguments:
642   cctx        the connection context (socket fd, possibly TLS context)
643   buffer      to read into
644   bufsize     the buffer size
645   timelimit   the timeout endpoint, seconds-since-epoch
646
647 Returns:      > 0 => that much data read
648               <= 0 on error or EOF; errno set - zero for EOF
649 */
650
651 int
652 ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, time_t timelimit)
653 {
654 int rc;
655
656 if (!fd_ready(cctx->sock, timelimit))
657   return -1;
658
659 /* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
660 close down of the connection), set errno to zero; otherwise leave it alone. */
661
662 #ifndef DISABLE_TLS
663 if (cctx->tls_ctx)                                      /* client TLS */
664   rc = tls_read(cctx->tls_ctx, buffer, buffsize);
665 else if (tls_in.active.sock == cctx->sock)              /* server TLS */
666   rc = tls_read(NULL, buffer, buffsize);
667 else
668 #endif
669   rc = recv(cctx->sock, buffer, buffsize, 0);
670
671 if (rc > 0) return rc;
672 if (rc == 0) errno = 0;
673 return -1;
674 }
675
676
677
678
679 /*************************************************
680 *    Lookup address family of potential socket   *
681 *************************************************/
682
683 /* Given a file-descriptor, check to see if it's a socket and, if so,
684 return the address family; detects IPv4 vs IPv6.  If not a socket then
685 return -1.
686
687 The value 0 is typically AF_UNSPEC, which should not be seen on a connected
688 fd.  If the return is -1, the errno will be from getsockname(); probably
689 ENOTSOCK or ECONNRESET.
690
691 Arguments:     socket-or-not fd
692 Returns:       address family or -1
693 */
694
695 int
696 ip_get_address_family(int fd)
697 {
698 struct sockaddr_storage ss;
699 socklen_t sslen = sizeof(ss);
700
701 if (getsockname(fd, (struct sockaddr *) &ss, &sslen) < 0)
702   return -1;
703
704 return (int) ss.ss_family;
705 }
706
707
708
709
710 /*************************************************
711 *       Lookup DSCP settings for a socket        *
712 *************************************************/
713
714 struct dscp_name_tableentry {
715   const uschar *name;
716   int value;
717 };
718 /* Keep both of these tables sorted! */
719 static struct dscp_name_tableentry dscp_table[] = {
720 #ifdef IPTOS_DSCP_AF11
721     { CUS"af11", IPTOS_DSCP_AF11 },
722     { CUS"af12", IPTOS_DSCP_AF12 },
723     { CUS"af13", IPTOS_DSCP_AF13 },
724     { CUS"af21", IPTOS_DSCP_AF21 },
725     { CUS"af22", IPTOS_DSCP_AF22 },
726     { CUS"af23", IPTOS_DSCP_AF23 },
727     { CUS"af31", IPTOS_DSCP_AF31 },
728     { CUS"af32", IPTOS_DSCP_AF32 },
729     { CUS"af33", IPTOS_DSCP_AF33 },
730     { CUS"af41", IPTOS_DSCP_AF41 },
731     { CUS"af42", IPTOS_DSCP_AF42 },
732     { CUS"af43", IPTOS_DSCP_AF43 },
733     { CUS"ef", IPTOS_DSCP_EF },
734 #endif
735 #ifdef IPTOS_LOWCOST
736     { CUS"lowcost", IPTOS_LOWCOST },
737 #endif
738     { CUS"lowdelay", IPTOS_LOWDELAY },
739 #ifdef IPTOS_MINCOST
740     { CUS"mincost", IPTOS_MINCOST },
741 #endif
742     { CUS"reliability", IPTOS_RELIABILITY },
743     { CUS"throughput", IPTOS_THROUGHPUT }
744 };
745 static int dscp_table_size =
746   sizeof(dscp_table) / sizeof(struct dscp_name_tableentry);
747
748 /* DSCP values change by protocol family, and so do the options used for
749 setsockopt(); this utility does all the lookups.  It takes an unexpanded
750 option string, expands it, strips off affix whitespace, then checks if it's
751 a number.  If all of what's left is a number, then that's how the option will
752 be parsed and success/failure is a range check.  If it's not all a number,
753 then it must be a supported keyword.
754
755 Arguments:
756   dscp_name   a string, so far unvalidated
757   af          address_family in use
758   level       setsockopt level to use
759   optname     setsockopt name to use
760   dscp_value  value for dscp_name
761
762 Returns: TRUE if okay to setsockopt(), else FALSE
763
764 *level and *optname may be set even if FALSE is returned
765 */
766
767 BOOL
768 dscp_lookup(const uschar *dscp_name, int af,
769     int *level, int *optname, int *dscp_value)
770 {
771 uschar *dscp_lookup, *p;
772 int first, last;
773 long rawlong;
774
775 if (af == AF_INET)
776   {
777   *level = IPPROTO_IP;
778   *optname = IP_TOS;
779   }
780 #if HAVE_IPV6 && defined(IPV6_TCLASS)
781 else if (af == AF_INET6)
782   {
783   *level = IPPROTO_IPV6;
784   *optname = IPV6_TCLASS;
785   }
786 #endif
787 else
788   {
789   DEBUG(D_transport)
790     debug_printf("Unhandled address family %d in dscp_lookup()\n", af);
791   return FALSE;
792   }
793 if (!dscp_name)
794   {
795   DEBUG(D_transport)
796     debug_printf("[empty DSCP]\n");
797   return FALSE;
798   }
799 dscp_lookup = expand_string(US dscp_name);
800 if (dscp_lookup == NULL || *dscp_lookup == '\0')
801   return FALSE;
802
803 p = dscp_lookup + Ustrlen(dscp_lookup) - 1;
804 while (isspace(*p)) *p-- = '\0';
805 while (isspace(*dscp_lookup) && dscp_lookup < p) dscp_lookup++;
806 if (*dscp_lookup == '\0')
807   return FALSE;
808
809 rawlong = Ustrtol(dscp_lookup, &p, 0);
810 if (p != dscp_lookup && *p == '\0')
811   {
812   /* We have six bits available, which will end up shifted to fit in 0xFC mask.
813   RFC 2597 defines the values unshifted. */
814   if (rawlong < 0 || rawlong > 0x3F)
815     {
816     DEBUG(D_transport)
817       debug_printf("DSCP value %ld out of range, ignored.\n", rawlong);
818     return FALSE;
819     }
820   *dscp_value = rawlong << 2;
821   return TRUE;
822   }
823
824 first = 0;
825 last = dscp_table_size;
826 while (last > first)
827   {
828   int middle = (first + last)/2;
829   int c = Ustrcmp(dscp_lookup, dscp_table[middle].name);
830   if (c == 0)
831     {
832     *dscp_value = dscp_table[middle].value;
833     return TRUE;
834     }
835   else if (c > 0)
836     first = middle + 1;
837   else
838     last = middle;
839   }
840 return FALSE;
841 }
842
843 void
844 dscp_list_to_stream(FILE *stream)
845 {
846 for (int i = 0; i < dscp_table_size; ++i)
847   fprintf(stream, "%s\n", dscp_table[i].name);
848 }
849
850
851 /* End of ip.c */
852 /* vi: aw ai sw=2
853 */