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