96a6c29f20e5082d32833664fd6559b4e27eace0
[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 */
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 fd_set select_inset;
593 int time_left = timelimit - time(NULL);
594 int rc;
595
596 if (time_left <= 0)
597   {
598   errno = ETIMEDOUT;
599   return FALSE;
600   }
601 /* Wait until the socket is ready */
602
603 do
604   {
605   struct timeval tv = { .tv_sec = time_left, .tv_usec = 0 };
606   FD_ZERO (&select_inset);
607   FD_SET (fd, &select_inset);
608
609   /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
610   rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
611
612   /* If some interrupt arrived, just retry. We presume this to be rare,
613   but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
614   select() to exit).
615
616   Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
617   the interrupt not at all rare. Since the timeout is typically more than 2
618   minutes, the effect was to block the timeout completely. To prevent this
619   happening again, we do an explicit time test and adjust the timeout
620   accordingly */
621
622   if (rc < 0 && errno == EINTR)
623     {
624     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
625
626     /* Watch out, 'continue' jumps to the condition, not to the loops top */
627     if ((time_left = timelimit - time(NULL)) > 0) continue;
628     }
629
630   if (rc <= 0)
631     {
632     errno = ETIMEDOUT;
633     return FALSE;
634     }
635
636   /* Checking the FD_ISSET is not enough, if we're interrupted, the
637   select_inset may still contain the 'input'. */
638   }
639 while (rc < 0 || !FD_ISSET(fd, &select_inset));
640 return TRUE;
641 }
642
643 /* The timeout is implemented using select(), and we loop to cover select()
644 getting interrupted, and the possibility of select() returning with a positive
645 result but no ready descriptor. Is this in fact possible?
646
647 Arguments:
648   cctx        the connection context (socket fd, possibly TLS context)
649   buffer      to read into
650   bufsize     the buffer size
651   timelimit   the timeout endpoint, seconds-since-epoch
652
653 Returns:      > 0 => that much data read
654               <= 0 on error or EOF; errno set - zero for EOF
655 */
656
657 int
658 ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, time_t timelimit)
659 {
660 int rc;
661
662 if (!fd_ready(cctx->sock, timelimit))
663   return -1;
664
665 /* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
666 close down of the connection), set errno to zero; otherwise leave it alone. */
667
668 #ifndef DISABLE_TLS
669 if (cctx->tls_ctx)                                      /* client TLS */
670   rc = tls_read(cctx->tls_ctx, buffer, buffsize);
671 else if (tls_in.active.sock == cctx->sock)              /* server TLS */
672   rc = tls_read(NULL, buffer, buffsize);
673 else
674 #endif
675   rc = recv(cctx->sock, buffer, buffsize, 0);
676
677 if (rc > 0) return rc;
678 if (rc == 0) errno = 0;
679 return -1;
680 }
681
682
683
684
685 /*************************************************
686 *    Lookup address family of potential socket   *
687 *************************************************/
688
689 /* Given a file-descriptor, check to see if it's a socket and, if so,
690 return the address family; detects IPv4 vs IPv6.  If not a socket then
691 return -1.
692
693 The value 0 is typically AF_UNSPEC, which should not be seen on a connected
694 fd.  If the return is -1, the errno will be from getsockname(); probably
695 ENOTSOCK or ECONNRESET.
696
697 Arguments:     socket-or-not fd
698 Returns:       address family or -1
699 */
700
701 int
702 ip_get_address_family(int fd)
703 {
704 struct sockaddr_storage ss;
705 socklen_t sslen = sizeof(ss);
706
707 if (getsockname(fd, (struct sockaddr *) &ss, &sslen) < 0)
708   return -1;
709
710 return (int) ss.ss_family;
711 }
712
713
714
715
716 /*************************************************
717 *       Lookup DSCP settings for a socket        *
718 *************************************************/
719
720 struct dscp_name_tableentry {
721   const uschar *name;
722   int value;
723 };
724 /* Keep both of these tables sorted! */
725 static struct dscp_name_tableentry dscp_table[] = {
726 #ifdef IPTOS_DSCP_AF11
727     { CUS"af11", IPTOS_DSCP_AF11 },
728     { CUS"af12", IPTOS_DSCP_AF12 },
729     { CUS"af13", IPTOS_DSCP_AF13 },
730     { CUS"af21", IPTOS_DSCP_AF21 },
731     { CUS"af22", IPTOS_DSCP_AF22 },
732     { CUS"af23", IPTOS_DSCP_AF23 },
733     { CUS"af31", IPTOS_DSCP_AF31 },
734     { CUS"af32", IPTOS_DSCP_AF32 },
735     { CUS"af33", IPTOS_DSCP_AF33 },
736     { CUS"af41", IPTOS_DSCP_AF41 },
737     { CUS"af42", IPTOS_DSCP_AF42 },
738     { CUS"af43", IPTOS_DSCP_AF43 },
739     { CUS"ef", IPTOS_DSCP_EF },
740 #endif
741 #ifdef IPTOS_LOWCOST
742     { CUS"lowcost", IPTOS_LOWCOST },
743 #endif
744     { CUS"lowdelay", IPTOS_LOWDELAY },
745 #ifdef IPTOS_MINCOST
746     { CUS"mincost", IPTOS_MINCOST },
747 #endif
748     { CUS"reliability", IPTOS_RELIABILITY },
749     { CUS"throughput", IPTOS_THROUGHPUT }
750 };
751 static int dscp_table_size =
752   sizeof(dscp_table) / sizeof(struct dscp_name_tableentry);
753
754 /* DSCP values change by protocol family, and so do the options used for
755 setsockopt(); this utility does all the lookups.  It takes an unexpanded
756 option string, expands it, strips off affix whitespace, then checks if it's
757 a number.  If all of what's left is a number, then that's how the option will
758 be parsed and success/failure is a range check.  If it's not all a number,
759 then it must be a supported keyword.
760
761 Arguments:
762   dscp_name   a string, so far unvalidated
763   af          address_family in use
764   level       setsockopt level to use
765   optname     setsockopt name to use
766   dscp_value  value for dscp_name
767
768 Returns: TRUE if okay to setsockopt(), else FALSE
769
770 *level and *optname may be set even if FALSE is returned
771 */
772
773 BOOL
774 dscp_lookup(const uschar *dscp_name, int af,
775     int *level, int *optname, int *dscp_value)
776 {
777 uschar *dscp_lookup, *p;
778 int first, last;
779 long rawlong;
780
781 if (af == AF_INET)
782   {
783   *level = IPPROTO_IP;
784   *optname = IP_TOS;
785   }
786 #if HAVE_IPV6 && defined(IPV6_TCLASS)
787 else if (af == AF_INET6)
788   {
789   *level = IPPROTO_IPV6;
790   *optname = IPV6_TCLASS;
791   }
792 #endif
793 else
794   {
795   DEBUG(D_transport)
796     debug_printf("Unhandled address family %d in dscp_lookup()\n", af);
797   return FALSE;
798   }
799 if (!dscp_name)
800   {
801   DEBUG(D_transport)
802     debug_printf("[empty DSCP]\n");
803   return FALSE;
804   }
805 dscp_lookup = expand_string(US dscp_name);
806 if (dscp_lookup == NULL || *dscp_lookup == '\0')
807   return FALSE;
808
809 p = dscp_lookup + Ustrlen(dscp_lookup) - 1;
810 while (isspace(*p)) *p-- = '\0';
811 while (isspace(*dscp_lookup) && dscp_lookup < p) dscp_lookup++;
812 if (*dscp_lookup == '\0')
813   return FALSE;
814
815 rawlong = Ustrtol(dscp_lookup, &p, 0);
816 if (p != dscp_lookup && *p == '\0')
817   {
818   /* We have six bits available, which will end up shifted to fit in 0xFC mask.
819   RFC 2597 defines the values unshifted. */
820   if (rawlong < 0 || rawlong > 0x3F)
821     {
822     DEBUG(D_transport)
823       debug_printf("DSCP value %ld out of range, ignored.\n", rawlong);
824     return FALSE;
825     }
826   *dscp_value = rawlong << 2;
827   return TRUE;
828   }
829
830 first = 0;
831 last = dscp_table_size;
832 while (last > first)
833   {
834   int middle = (first + last)/2;
835   int c = Ustrcmp(dscp_lookup, dscp_table[middle].name);
836   if (c == 0)
837     {
838     *dscp_value = dscp_table[middle].value;
839     return TRUE;
840     }
841   else if (c > 0)
842     first = middle + 1;
843   else
844     last = middle;
845   }
846 return FALSE;
847 }
848
849 void
850 dscp_list_to_stream(FILE *stream)
851 {
852 for (int i = 0; i < dscp_table_size; ++i)
853   fprintf(stream, "%s\n", dscp_table[i].name);
854 }
855
856
857 /* End of ip.c */
858 /* vi: aw ai sw=2
859 */