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