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