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