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
89int     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
185int     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