Debug: provide for SIGALRM tracking
[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 175 )
8768d548 176 f.tcp_fastopen_ok = TRUE;
10ac8d7f
JH
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 245sigalrm_seen = FALSE;
c2a1bba0 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 252the SMTP banner. Other (than SMTP) cases of TCP connections can
9d2ec425
JH
253possibly use the data-on-syn, so support that too.
254
255This is a Linux implementation. It might be useable on FreeBSD; I have
256not checked. I think MacOS has a "connectx" call for this purpose,
257rather than using "sendto" ? */
fb05276a 258
8768d548 259if (fastopen_blob && f.tcp_fastopen_ok)
fb05276a 260 {
10ac8d7f 261 if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len,
ac0dcd3f 262 MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0)
1ccd5f67 263 /* seen for with-data, experimental TFO option, with-cookie case */
8255135b 264 /* seen for with-data, proper TFO opt, with-cookie case */
ac0dcd3f 265 {
10ac8d7f 266 DEBUG(D_transport|D_v)
ba936fb8 267 debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
4aa2e44b 268 address, (unsigned long)fastopen_blob->len);
afdb5e9c 269 /*XXX also seen on successful TFO, sigh */
06e272a3 270 tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_USED : TFO_ATTEMPTED;
ac0dcd3f 271 }
8255135b 272 else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */
1ccd5f67
JH
273 /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
274 /* apparently no visibility of the diffference at this point */
8255135b 275 /* seen for with-data, proper TFO opt, cookie-req */
1ccd5f67
JH
276 /* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */
277 /* ? older Experimental TFO option behaviour ? */
278 { /* queue unsent data */
8255135b 279 DEBUG(D_transport|D_v) debug_printf("TFO mode sendto, %s data: EINPROGRESS\n",
10ac8d7f
JH
280 fastopen_blob->len > 0 ? "with" : "no");
281 if (!fastopen_blob->data)
a2673768 282 {
06e272a3 283 tcp_out_fastopen = TFO_ATTEMPTED; /* we tried; unknown if useful yet */
ac0dcd3f 284 rc = 0;
a2673768 285 }
8255135b 286 else
10ac8d7f 287 rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0);
ac0dcd3f
JH
288 }
289 else if(errno == EOPNOTSUPP)
290 {
291 DEBUG(D_transport)
292 debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n");
293 goto legacy_connect;
294 }
fb05276a
JH
295 }
296else
297#endif
0ab63f3d
JH
298 {
299legacy_connect:
10ac8d7f 300 DEBUG(D_transport|D_v) if (fastopen_blob)
4aa2e44b
JH
301 debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
302 address, (unsigned long)fastopen_blob->len);
0ab63f3d 303 if ((rc = connect(sock, s_ptr, s_len)) >= 0)
10ac8d7f
JH
304 if ( fastopen_blob && fastopen_blob->data && fastopen_blob->len
305 && send(sock, fastopen_blob->data, fastopen_blob->len, 0) < 0)
0ab63f3d
JH
306 rc = -1;
307 }
fb05276a 308
059ec3d9 309save_errno = errno;
c2a1bba0 310ALARM_CLR(0);
059ec3d9
PH
311
312/* There is a testing facility for simulating a connection timeout, as I
313can't think of any other way of doing this. It converts a connection refused
75e0e026 314into a timeout if the timeout is set to 999999. */
059ec3d9 315
8768d548 316if (f.running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999)
059ec3d9 317 {
a39bd74d
JB
318 rc = -1;
319 save_errno = EINTR;
320 sigalrm_seen = TRUE;
059ec3d9
PH
321 }
322
323/* Success */
324
055e2cb4 325if (rc >= 0)
055e2cb4 326 return 0;
059ec3d9
PH
327
328/* A failure whose error code is "Interrupted system call" is in fact
329an externally applied timeout if the signal handler has been run. */
330
a39bd74d 331errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno;
059ec3d9
PH
332return -1;
333}
334
335
a6d4c44e
TF
336
337/*************************************************
338* Create connected socket to remote host *
339*************************************************/
340
b1f8e4f8
JH
341/* Create a socket and connect to host (name or number, ipv6 ok)
342 at one of port-range.
a6d4c44e 343
b1f8e4f8
JH
344Arguments:
345 type SOCK_DGRAM or SOCK_STREAM
346 af AF_INET6 or AF_INET for the socket type
afdb5e9c 347 hostname host name, or ip address (as text)
b1f8e4f8
JH
348 portlo,porthi the remote port range
349 timeout a timeout
4a5cbaff 350 connhost if not NULL, host_item to be filled in with connection details
b1f8e4f8 351 errstr pointer for allocated string on error
10ac8d7f 352 fastopen_blob with SOCK_STREAM, if non-null, request TCP Fast Open.
4a5cbaff 353 Additionally, optional early-data to send
b1f8e4f8
JH
354
355Return:
356 socket fd, or -1 on failure (having allocated an error string)
357*/
358int
359ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
10ac8d7f 360 int timeout, host_item * connhost, uschar ** errstr, const blob * fastopen_blob)
b1f8e4f8
JH
361{
362int namelen, port;
363host_item shost;
364host_item *h;
365int af = 0, fd, fd4 = -1, fd6 = -1;
366
367shost.next = NULL;
368shost.address = NULL;
369shost.port = portlo;
370shost.mx = -1;
371
372namelen = Ustrlen(hostname);
373
374/* Anything enclosed in [] must be an IP address. */
375
376if (hostname[0] == '[' &&
377 hostname[namelen - 1] == ']')
378 {
af483912 379 uschar * host = string_copyn(hostname+1, namelen-2);
b1f8e4f8
JH
380 if (string_is_ip_address(host, NULL) == 0)
381 {
382 *errstr = string_sprintf("malformed IP address \"%s\"", hostname);
383 return -1;
384 }
385 shost.name = shost.address = host;
386 }
387
388/* Otherwise check for an unadorned IP address */
389
390else if (string_is_ip_address(hostname, NULL) != 0)
af483912 391 shost.name = shost.address = string_copyn(hostname, namelen);
b1f8e4f8
JH
392
393/* Otherwise lookup IP address(es) from the name */
394
395else
396 {
af483912 397 shost.name = string_copyn(hostname, namelen);
1f155f8e
JH
398 if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE,
399 NULL, FALSE) != HOST_FOUND)
b1f8e4f8
JH
400 {
401 *errstr = string_sprintf("no IP address found for host %s", shost.name);
402 return -1;
403 }
404 }
405
406/* Try to connect to the server - test each IP till one works */
407
fb05276a 408for (h = &shost; h; h = h->next)
b1f8e4f8 409 {
af483912
JH
410 fd = Ustrchr(h->address, ':') != 0
411 ? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
412 : fd4 < 0 ? (fd4 = ip_socket(type, af = AF_INET )) : fd4;
b1f8e4f8
JH
413
414 if (fd < 0)
415 {
416 *errstr = string_sprintf("failed to create socket: %s", strerror(errno));
417 goto bad;
418 }
419
420 for(port = portlo; port <= porthi; port++)
10ac8d7f 421 if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
b1f8e4f8
JH
422 {
423 if (fd != fd6) close(fd6);
424 if (fd != fd4) close(fd4);
8a512ed5
JH
425 if (connhost)
426 {
b1f8e4f8
JH
427 h->port = port;
428 *connhost = *h;
429 connhost->next = NULL;
430 }
431 return fd;
432 }
433 }
434
a9764ac5
JH
435*errstr = string_sprintf("failed to connect to any address for %s: %s",
436 hostname, strerror(errno));
b1f8e4f8
JH
437
438bad:
439 close(fd4); close(fd6); return -1;
440}
441
059ec3d9 442
4a5cbaff 443/*XXX TFO? */
3e60dd41
JH
444int
445ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo)
446{
a39bd74d
JB
447int scan;
448uschar hostname[256];
449unsigned int portlow, porthigh;
450
451/* extract host and port part */
452scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
453if (scan != 3)
454 {
455 if (scan != 2)
456 {
457 *errstr = string_sprintf("invalid socket '%s'", hostport);
458 return -1;
3e60dd41 459 }
a39bd74d 460 porthigh = portlow;
3e60dd41
JH
461 }
462
a39bd74d 463return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
4a5cbaff 464 tmo, NULL, errstr, NULL);
3e60dd41
JH
465}
466
467int
468ip_unixsocket(const uschar * path, uschar ** errstr)
469{
a39bd74d
JB
470int sock;
471struct sockaddr_un server;
3e60dd41 472
a39bd74d
JB
473if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
474 {
475 *errstr = US"can't open UNIX socket.";
476 return -1;
3e60dd41
JH
477 }
478
af483912 479callout_address = string_copy(path);
a39bd74d
JB
480server.sun_family = AF_UNIX;
481Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
482server.sun_path[sizeof(server.sun_path)-1] = '\0';
483if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
484 {
485 int err = errno;
486 (void)close(sock);
487 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
488 path, strerror(err));
489 return -1;
490 }
491return sock;
3e60dd41
JH
492}
493
494int
495ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
496{
a39bd74d
JB
497return *spec == '/'
498 ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo);
3e60dd41
JH
499}
500
059ec3d9
PH
501/*************************************************
502* Set keepalive on a socket *
503*************************************************/
504
505/* Can be called for both incoming and outgoing sockets.
506
507Arguments:
508 sock the socket
509 address the remote host address, for failure logging
510 torf true for outgoing connection, false for incoming
511
512Returns: nothing
513*/
514
515void
55414b25 516ip_keepalive(int sock, const uschar *address, BOOL torf)
059ec3d9
PH
517{
518int fodder = 1;
519if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
5903c6ff 520 US (&fodder), sizeof(fodder)) != 0)
059ec3d9
PH
521 log_write(0, LOG_MAIN, "setsockopt(SO_KEEPALIVE) on connection %s %s "
522 "failed: %s", torf? "to":"from", address, strerror(errno));
523}
524
525
526
527/*************************************************
528* Receive from a socket with timeout *
529*************************************************/
530
4e71661f 531/*
059ec3d9 532Arguments:
4e71661f
JH
533 fd the file descriptor
534 timeout the timeout, seconds
535Returns: TRUE => ready for i/o
536 FALSE => timed out, or other error
059ec3d9 537*/
4e71661f
JH
538BOOL
539fd_ready(int fd, int timeout)
059ec3d9
PH
540{
541fd_set select_inset;
19050083 542time_t start_recv = time(NULL);
c528cec4 543int time_left = timeout;
059ec3d9
PH
544int rc;
545
85ff3cf9 546if (time_left <= 0)
4e71661f
JH
547 {
548 errno = ETIMEDOUT;
549 return FALSE;
550 }
059ec3d9
PH
551/* Wait until the socket is ready */
552
a39bd74d 553do
059ec3d9 554 {
f2ed27cf 555 struct timeval tv = { .tv_sec = time_left, .tv_usec = 0 };
059ec3d9 556 FD_ZERO (&select_inset);
4e71661f 557 FD_SET (fd, &select_inset);
059ec3d9 558
0f0c8159 559 /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
4e71661f 560 rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
059ec3d9
PH
561
562 /* If some interrupt arrived, just retry. We presume this to be rare,
563 but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
564 select() to exit).
565
566 Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
567 the interrupt not at all rare. Since the timeout is typically more than 2
568 minutes, the effect was to block the timeout completely. To prevent this
c528cec4
HSHR
569 happening again, we do an explicit time test and adjust the timeout
570 accordingly */
059ec3d9
PH
571
572 if (rc < 0 && errno == EINTR)
573 {
574 DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
85ff3cf9 575
c528cec4 576 /* Watch out, 'continue' jumps to the condition, not to the loops top */
85ff3cf9
HSHR
577 time_left = timeout - (time(NULL) - start_recv);
578 if (time_left > 0) continue;
059ec3d9
PH
579 }
580
059ec3d9
PH
581 if (rc <= 0)
582 {
583 errno = ETIMEDOUT;
4e71661f 584 return FALSE;
059ec3d9
PH
585 }
586
c528cec4
HSHR
587 /* Checking the FD_ISSET is not enough, if we're interrupted, the
588 select_inset may still contain the 'input'. */
059ec3d9 589 }
4bd6107d 590while (rc < 0 || !FD_ISSET(fd, &select_inset));
4e71661f
JH
591return TRUE;
592}
593
594/* The timeout is implemented using select(), and we loop to cover select()
595getting interrupted, and the possibility of select() returning with a positive
596result but no ready descriptor. Is this in fact possible?
597
598Arguments:
74f1a423 599 cctx the connection context (socket fd, possibly TLS context)
4e71661f
JH
600 buffer to read into
601 bufsize the buffer size
602 timeout the timeout
603
604Returns: > 0 => that much data read
605 <= 0 on error or EOF; errno set - zero for EOF
606*/
607
608int
74f1a423 609ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout)
4e71661f
JH
610{
611int rc;
612
74f1a423 613if (!fd_ready(cctx->sock, timeout))
4e71661f 614 return -1;
059ec3d9
PH
615
616/* The socket is ready, read from it (via TLS if it's active). On EOF (i.e.
617close down of the connection), set errno to zero; otherwise leave it alone. */
618
619#ifdef SUPPORT_TLS
74f1a423
JH
620if (cctx->tls_ctx) /* client TLS */
621 rc = tls_read(cctx->tls_ctx, buffer, buffsize);
622else if (tls_in.active.sock == cctx->sock) /* server TLS */
623 rc = tls_read(NULL, buffer, buffsize);
059ec3d9
PH
624else
625#endif
74f1a423 626 rc = recv(cctx->sock, buffer, buffsize, 0);
059ec3d9
PH
627
628if (rc > 0) return rc;
629if (rc == 0) errno = 0;
630return -1;
631}
632
633
9e4f5962
PP
634
635
13363eba
PP
636/*************************************************
637* Lookup address family of potential socket *
638*************************************************/
639
640/* Given a file-descriptor, check to see if it's a socket and, if so,
641return the address family; detects IPv4 vs IPv6. If not a socket then
642return -1.
643
644The value 0 is typically AF_UNSPEC, which should not be seen on a connected
645fd. If the return is -1, the errno will be from getsockname(); probably
646ENOTSOCK or ECONNRESET.
647
648Arguments: socket-or-not fd
649Returns: address family or -1
650*/
651
652int
653ip_get_address_family(int fd)
654{
655struct sockaddr_storage ss;
656socklen_t sslen = sizeof(ss);
657
658if (getsockname(fd, (struct sockaddr *) &ss, &sslen) < 0)
659 return -1;
660
661return (int) ss.ss_family;
662}
663
664
665
666
9e4f5962
PP
667/*************************************************
668* Lookup DSCP settings for a socket *
669*************************************************/
670
671struct dscp_name_tableentry {
672 const uschar *name;
673 int value;
674};
675/* Keep both of these tables sorted! */
676static struct dscp_name_tableentry dscp_table[] = {
677#ifdef IPTOS_DSCP_AF11
36a3ae5f
PP
678 { CUS"af11", IPTOS_DSCP_AF11 },
679 { CUS"af12", IPTOS_DSCP_AF12 },
680 { CUS"af13", IPTOS_DSCP_AF13 },
681 { CUS"af21", IPTOS_DSCP_AF21 },
682 { CUS"af22", IPTOS_DSCP_AF22 },
683 { CUS"af23", IPTOS_DSCP_AF23 },
684 { CUS"af31", IPTOS_DSCP_AF31 },
685 { CUS"af32", IPTOS_DSCP_AF32 },
686 { CUS"af33", IPTOS_DSCP_AF33 },
687 { CUS"af41", IPTOS_DSCP_AF41 },
688 { CUS"af42", IPTOS_DSCP_AF42 },
689 { CUS"af43", IPTOS_DSCP_AF43 },
690 { CUS"ef", IPTOS_DSCP_EF },
9e4f5962
PP
691#endif
692#ifdef IPTOS_LOWCOST
36a3ae5f 693 { CUS"lowcost", IPTOS_LOWCOST },
9e4f5962 694#endif
36a3ae5f 695 { CUS"lowdelay", IPTOS_LOWDELAY },
9e4f5962 696#ifdef IPTOS_MINCOST
36a3ae5f 697 { CUS"mincost", IPTOS_MINCOST },
9e4f5962 698#endif
36a3ae5f
PP
699 { CUS"reliability", IPTOS_RELIABILITY },
700 { CUS"throughput", IPTOS_THROUGHPUT }
9e4f5962
PP
701};
702static int dscp_table_size =
703 sizeof(dscp_table) / sizeof(struct dscp_name_tableentry);
704
705/* DSCP values change by protocol family, and so do the options used for
2a1b36b3
PP
706setsockopt(); this utility does all the lookups. It takes an unexpanded
707option string, expands it, strips off affix whitespace, then checks if it's
708a number. If all of what's left is a number, then that's how the option will
709be parsed and success/failure is a range check. If it's not all a number,
710then it must be a supported keyword.
9e4f5962
PP
711
712Arguments:
713 dscp_name a string, so far unvalidated
714 af address_family in use
715 level setsockopt level to use
716 optname setsockopt name to use
717 dscp_value value for dscp_name
718
719Returns: TRUE if okay to setsockopt(), else FALSE
2a1b36b3
PP
720
721*level and *optname may be set even if FALSE is returned
9e4f5962
PP
722*/
723
724BOOL
725dscp_lookup(const uschar *dscp_name, int af,
726 int *level, int *optname, int *dscp_value)
727{
2a1b36b3 728uschar *dscp_lookup, *p;
9e4f5962 729int first, last;
2a1b36b3 730long rawlong;
9e4f5962
PP
731
732if (af == AF_INET)
733 {
734 *level = IPPROTO_IP;
735 *optname = IP_TOS;
736 }
bb7b9411 737#if HAVE_IPV6 && defined(IPV6_TCLASS)
9e4f5962
PP
738else if (af == AF_INET6)
739 {
740 *level = IPPROTO_IPV6;
741 *optname = IPV6_TCLASS;
742 }
b301a50b 743#endif
9e4f5962
PP
744else
745 {
746 DEBUG(D_transport)
747 debug_printf("Unhandled address family %d in dscp_lookup()\n", af);
748 return FALSE;
749 }
750if (!dscp_name)
751 {
752 DEBUG(D_transport)
753 debug_printf("[empty DSCP]\n");
754 return FALSE;
755 }
756dscp_lookup = expand_string(US dscp_name);
757if (dscp_lookup == NULL || *dscp_lookup == '\0')
758 return FALSE;
759
2a1b36b3
PP
760p = dscp_lookup + Ustrlen(dscp_lookup) - 1;
761while (isspace(*p)) *p-- = '\0';
762while (isspace(*dscp_lookup) && dscp_lookup < p) dscp_lookup++;
763if (*dscp_lookup == '\0')
764 return FALSE;
765
766rawlong = Ustrtol(dscp_lookup, &p, 0);
767if (p != dscp_lookup && *p == '\0')
768 {
769 /* We have six bits available, which will end up shifted to fit in 0xFC mask.
770 RFC 2597 defines the values unshifted. */
771 if (rawlong < 0 || rawlong > 0x3F)
772 {
773 DEBUG(D_transport)
774 debug_printf("DSCP value %ld out of range, ignored.\n", rawlong);
775 return FALSE;
776 }
777 *dscp_value = rawlong << 2;
778 return TRUE;
779 }
780
9e4f5962
PP
781first = 0;
782last = dscp_table_size;
783while (last > first)
784 {
785 int middle = (first + last)/2;
786 int c = Ustrcmp(dscp_lookup, dscp_table[middle].name);
787 if (c == 0)
788 {
789 *dscp_value = dscp_table[middle].value;
790 return TRUE;
791 }
792 else if (c > 0)
9e4f5962 793 first = middle + 1;
9e4f5962 794 else
9e4f5962 795 last = middle;
9e4f5962
PP
796 }
797return FALSE;
798}
799
36a3ae5f
PP
800void
801dscp_list_to_stream(FILE *stream)
802{
803int i;
804for (i=0; i < dscp_table_size; ++i)
805 fprintf(stream, "%s\n", dscp_table[i].name);
806}
807
9e4f5962 808
059ec3d9 809/* End of ip.c */
8a512ed5
JH
810/* vi: aw ai sw=2
811*/