xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_listen.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: inet_listen.c,v 1.3 2022/10/08 16:12:50 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	inet_listen 3
6 /* SUMMARY
7 /*	start TCP listener
8 /* SYNOPSIS
9 /*	#include <listen.h>
10 /*
11 /*	int	inet_windowsize;
12 /*
13 /*	int	inet_listen(addr, backlog, block_mode)
14 /*	const char *addr;
15 /*	int	backlog;
16 /*	int	block_mode;
17 /*
18 /*	int	inet_accept(fd)
19 /*	int	fd;
20 /* DESCRIPTION
21 /*	The \fBinet_listen\fR routine starts a TCP listener
22 /*	on the specified address, with the specified backlog, and returns
23 /*	the resulting file descriptor.
24 /*
25 /*	inet_accept() accepts a connection and sanitizes error results.
26 /*
27 /*	Specify an inet_windowsize value > 0 to override the TCP
28 /*	window size that the server advertises to the client.
29 /*
30 /*	Arguments:
31 /* .IP addr
32 /*	The communication endpoint to listen on. The syntax is "host:port".
33 /*	Host and port may be specified in symbolic form or numerically.
34 /*	A null host field means listen on all network interfaces.
35 /* .IP backlog
36 /*	This argument is passed on to the \fIlisten(2)\fR routine.
37 /* .IP block_mode
38 /*	Either NON_BLOCKING for a non-blocking socket, or BLOCKING for
39 /*	blocking mode.
40 /* .IP fd
41 /*	File descriptor returned by inet_listen().
42 /* DIAGNOSTICS
43 /*	Fatal errors: inet_listen() aborts upon any system call failure.
44 /*	inet_accept() leaves all error handling up to the caller.
45 /* LICENSE
46 /* .ad
47 /* .fi
48 /*	The Secure Mailer license must be distributed with this software.
49 /* AUTHOR(S)
50 /*	Wietse Venema
51 /*	IBM T.J. Watson Research
52 /*	P.O. Box 704
53 /*	Yorktown Heights, NY 10598, USA
54 /*
55 /*	Wietse Venema
56 /*	Google, Inc.
57 /*	111 8th Avenue
58 /*	New York, NY 10011, USA
59 /*--*/
60 
61 /* System libraries. */
62 
63 #include <sys_defs.h>
64 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #include <netdb.h>
68 #ifndef MAXHOSTNAMELEN
69 #include <sys/param.h>
70 #endif
71 #include <errno.h>
72 #include <string.h>
73 #include <unistd.h>
74 
75 /* Utility library. */
76 
77 #include "mymalloc.h"
78 #include "msg.h"
79 #include "host_port.h"
80 #include "iostuff.h"
81 #include "listen.h"
82 #include "sane_accept.h"
83 #include "myaddrinfo.h"
84 #include "sock_addr.h"
85 #include "inet_proto.h"
86 
87 /* inet_listen - create TCP listener */
88 
inet_listen(const char * addr,int backlog,int block_mode)89 int     inet_listen(const char *addr, int backlog, int block_mode)
90 {
91     struct addrinfo *res;
92     struct addrinfo *res0;
93     int     aierr;
94     int     sock;
95     int     on = 1;
96     char   *buf;
97     char   *host;
98     char   *port;
99     const char *parse_err;
100     MAI_HOSTADDR_STR hostaddr;
101     MAI_SERVPORT_STR portnum;
102     const INET_PROTO_INFO *proto_info;
103 
104     /*
105      * Translate address information to internal form.
106      */
107     buf = mystrdup(addr);
108     if ((parse_err = host_port(buf, &host, "", &port, (char *) 0)) != 0)
109 	msg_fatal("%s: %s", addr, parse_err);
110     if (*host == 0)
111 	host = 0;
112     if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0)
113 	msg_fatal("%s: %s", addr, MAI_STRERROR(aierr));
114     myfree(buf);
115     /* No early returns or res0 leaks. */
116 
117     proto_info = inet_proto_info();
118     for (res = res0; /* see below */ ; res = res->ai_next) {
119 
120 	/*
121 	 * No usable address found.
122 	 */
123 	if (res == 0)
124 	    msg_fatal("%s: host found but no usable address", addr);
125 
126 	/*
127 	 * Safety net.
128 	 */
129 	if (strchr((char *) proto_info->sa_family_list, res->ai_family) != 0)
130 	    break;
131 
132 	msg_info("skipping address family %d for %s", res->ai_family, addr);
133     }
134 
135     /*
136      * Show what address we're trying.
137      */
138     if (msg_verbose) {
139 	SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
140 			     &hostaddr, &portnum, 0);
141 	msg_info("trying... [%s]:%s", hostaddr.buf, portnum.buf);
142     }
143 
144     /*
145      * Create a listener socket.
146      */
147     if ((sock = socket(res->ai_family, res->ai_socktype, 0)) < 0)
148 	msg_fatal("socket: %m");
149 #ifdef HAS_IPV6
150 #if defined(IPV6_V6ONLY) && !defined(BROKEN_AI_PASSIVE_NULL_HOST)
151     if (res->ai_family == AF_INET6
152 	&& setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
153 		      (void *) &on, sizeof(on)) < 0)
154 	msg_fatal("setsockopt(IPV6_V6ONLY): %m");
155 #endif
156 #endif
157     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
158 		   (void *) &on, sizeof(on)) < 0)
159 	msg_fatal("setsockopt(SO_REUSEADDR): %m");
160 #if defined(SO_REUSEPORT_LB)
161     if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT_LB,
162 		   (void *) &on, sizeof(on)) < 0)
163 	msg_fatal("setsockopt(SO_REUSEPORT_LB): %m");
164 #elif defined(SO_REUSEPORT)
165     if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
166 		   (void *) &on, sizeof(on)) < 0)
167 	msg_fatal("setsockopt(SO_REUSEPORT): %m");
168 #endif
169     if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
170 	SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
171 			     &hostaddr, &portnum, 0);
172 	msg_fatal("bind %s port %s: %m", hostaddr.buf, portnum.buf);
173     }
174     freeaddrinfo(res0);
175     non_blocking(sock, block_mode);
176     if (inet_windowsize > 0)
177 	set_inet_windowsize(sock, inet_windowsize);
178     if (listen(sock, backlog) < 0)
179 	msg_fatal("listen: %m");
180     return (sock);
181 }
182 
183 /* inet_accept - accept connection */
184 
inet_accept(int fd)185 int     inet_accept(int fd)
186 {
187     struct sockaddr_storage ss;
188     SOCKADDR_SIZE ss_len = sizeof(ss);
189 
190     return (sane_accept(fd, (struct sockaddr *) &ss, &ss_len));
191 }
192