MacOS: fix build
[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
163/*************************************************
10ac8d7f
JH
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
183/*************************************************
059ec3d9
PH
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 {
323 tcp_out_fastopen = TFO_ATTEMPTED; /* we tried; unknown if useful yet */
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
JH
396{
397int namelen, port;
398host_item shost;
399host_item *h;
400int af = 0, fd, fd4 = -1, fd6 = -1;
401
402shost.next = NULL;
403shost.address = NULL;
404shost.port = portlo;
405shost.mx = -1;
406
407namelen = Ustrlen(hostname);
408
409/* Anything enclosed in [] must be an IP address. */
410
411if (hostname[0] == '[' &&
412 hostname[namelen - 1] == ']')
413 {
af483912 414 uschar * host = string_copyn(hostname+1, namelen-2);
b1f8e4f8
JH
415 if (string_is_ip_address(host, NULL) == 0)
416 {
417 *errstr = string_sprintf("malformed IP address \"%s\"", hostname);
418 return -1;
419 }
420 shost.name = shost.address = host;
421 }
422
423/* Otherwise check for an unadorned IP address */
424
425else if (string_is_ip_address(hostname, NULL) != 0)
af483912 426 shost.name = shost.address = string_copyn(hostname, namelen);
b1f8e4f8
JH
427
428/* Otherwise lookup IP address(es) from the name */
429
430else
431 {
af483912 432 shost.name = string_copyn(hostname, namelen);
1f155f8e
JH
433 if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE,
434 NULL, FALSE) != HOST_FOUND)
b1f8e4f8
JH
435 {
436 *errstr = string_sprintf("no IP address found for host %s", shost.name);
437 return -1;
438 }
439 }
440
441/* Try to connect to the server - test each IP till one works */
442
fb05276a 443for (h = &shost; h; h = h->next)
b1f8e4f8 444 {
af483912
JH
445 fd = Ustrchr(h->address, ':') != 0
446 ? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6
447 : fd4 < 0 ? (fd4 = ip_socket(type, af = AF_INET )) : fd4;
b1f8e4f8
JH
448
449 if (fd < 0)
450 {
451 *errstr = string_sprintf("failed to create socket: %s", strerror(errno));
452 goto bad;
453 }
454
455 for(port = portlo; port <= porthi; port++)
10ac8d7f 456 if (ip_connect(fd, af, h->address, port, timeout, fastopen_blob) == 0)
b1f8e4f8
JH
457 {
458 if (fd != fd6) close(fd6);
459 if (fd != fd4) close(fd4);
8a512ed5
JH
460 if (connhost)
461 {
b1f8e4f8
JH
462 h->port = port;
463 *connhost = *h;
464 connhost->next = NULL;
465 }
466 return fd;
467 }
468 }
469
a9764ac5
JH
470*errstr = string_sprintf("failed to connect to any address for %s: %s",
471 hostname, strerror(errno));
b1f8e4f8
JH
472
473bad:
474 close(fd4); close(fd6); return -1;
475}
476
059ec3d9 477
4a5cbaff 478/*XXX TFO? */
3e60dd41
JH
479int
480ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo)
481{
a39bd74d
JB
482int scan;
483uschar hostname[256];
484unsigned int portlow, porthigh;
485
486/* extract host and port part */
487scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
488if (scan != 3)
489 {
490 if (scan != 2)
491 {
492 *errstr = string_sprintf("invalid socket '%s'", hostport);
493 return -1;
3e60dd41 494 }
a39bd74d 495 porthigh = portlow;
3e60dd41
JH
496 }
497
a39bd74d 498return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
4a5cbaff 499 tmo, NULL, errstr, NULL);
3e60dd41
JH
500}
501
502int
503ip_unixsocket(const uschar * path, uschar ** errstr)
504{
a39bd74d
JB
505int sock;
506struct sockaddr_un server;
3e60dd41 507
a39bd74d
JB
508if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
509 {
510 *errstr = US"can't open UNIX socket.";
511 return -1;
3e60dd41
JH
512 }
513
af483912 514callout_address = string_copy(path);
a39bd74d
JB
515server.sun_family = AF_UNIX;
516Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
517server.sun_path[sizeof(server.sun_path)-1] = '\0';
518if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
519 {
520 int err = errno;
521 (void)close(sock);
522 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
523 path, strerror(err));
524 return -1;
525 }
526return sock;
3e60dd41
JH
527}
528
529int
530ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
531{
a39bd74d
JB
532return *spec == '/'
533 ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo);
3e60dd41
JH
534}
535
059ec3d9
PH
536/*************************************************
537* Set keepalive on a socket *
538*************************************************/
539
540/* Can be called for both incoming and outgoing sockets.
541
542Arguments:
543 sock the socket
544 address the remote host address, for failure logging
545 torf true for outgoing connection, false for incoming
546
547Returns: nothing
548*/
549
550void
55414b25 551ip_keepalive(int sock, const uschar *address, BOOL torf)
059ec3d9
PH
552{
553int fodder = 1;
554if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
5903c6ff 555 US (&fodder), sizeof(fodder)) != 0)
059ec3d9
PH
556 log_write(0, LOG_MAIN, "setsockopt(SO_KEEPALIVE) on connection %s %s "
557 "failed: %s", torf? "to":"from", address, strerror(errno));
558}
559
560
561
562/*************************************************
563* Receive from a socket with timeout *
564*************************************************/
565
4e71661f 566/*
059ec3d9 567Arguments:
4e71661f
JH
568 fd the file descriptor
569 timeout the timeout, seconds
570Returns: TRUE => ready for i/o
571 FALSE => timed out, or other error
059ec3d9 572*/
4e71661f
JH
573BOOL
574fd_ready(int fd, int timeout)
059ec3d9
PH
575{
576fd_set select_inset;
19050083 577time_t start_recv = time(NULL);
c528cec4 578int time_left = timeout;
059ec3d9
PH
579int rc;
580
85ff3cf9 581if (time_left <= 0)
4e71661f
JH
582 {
583 errno = ETIMEDOUT;
584 return FALSE;
585 }
059ec3d9
PH
586/* Wait until the socket is ready */
587
a39bd74d 588do
059ec3d9 589 {
f2ed27cf 590 struct timeval tv = { .tv_sec = time_left, .tv_usec = 0 };
059ec3d9 591 FD_ZERO (&select_inset);
4e71661f 592 FD_SET (fd, &select_inset);
059ec3d9 593
0f0c8159 594 /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
4e71661f 595 rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
059ec3d9
PH
596
597 /* If some interrupt arrived, just retry. We presume this to be rare,
598 but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes
599 select() to exit).
600
601 Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
602 the interrupt not at all rare. Since the timeout is typically more than 2
603 minutes, the effect was to block the timeout completely. To prevent this
c528cec4
HSHR
604 happening again, we do an explicit time test and adjust the timeout
605 accordingly */
059ec3d9
PH
606
607 if (rc < 0 && errno == EINTR)
608 {
609 DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
85ff3cf9 610
c528cec4 611 /* Watch out, 'continue' jumps to the condition, not to the loops top */
85ff3cf9
HSHR
612 time_left = timeout - (time(NULL) - start_recv);
613 if (time_left > 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
637 timeout the timeout
638
639Returns: > 0 => that much data read
640 <= 0 on error or EOF; errno set - zero for EOF
641*/
642
643int
74f1a423 644ip_recv(client_conn_ctx * cctx, uschar * buffer, int buffsize, int timeout)
4e71661f
JH
645{
646int rc;
647
74f1a423 648if (!fd_ready(cctx->sock, timeout))
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
654#ifdef SUPPORT_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
671/*************************************************
13363eba
PP
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
702/*************************************************
9e4f5962
PP
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{
838int i;
839for (i=0; i < dscp_table_size; ++i)
840 fprintf(stream, "%s\n", dscp_table[i].name);
841}
842
9e4f5962 843
059ec3d9 844/* End of ip.c */
8a512ed5
JH
845/* vi: aw ai sw=2
846*/