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