1/*	$NetBSD$	*/
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
56/* System libraries. */
57
58#include <sys_defs.h>
59#include <sys/socket.h>
60#include <netinet/in.h>
61#include <arpa/inet.h>
62#include <netdb.h>
63#ifndef MAXHOSTNAMELEN
64#include <sys/param.h>
65#endif
66#include <errno.h>
67#include <string.h>
68#include <unistd.h>
69
70/* Utility library. */
71
72#include "mymalloc.h"
73#include "msg.h"
74#include "host_port.h"
75#include "iostuff.h"
76#include "listen.h"
77#include "sane_accept.h"
78#include "myaddrinfo.h"
79#include "sock_addr.h"
80#include "inet_proto.h"
81
82/* inet_listen - create TCP listener */
83
84int     inet_listen(const char *addr, int backlog, int block_mode)
85{
86    struct addrinfo *res;
87    struct addrinfo *res0;
88    int     aierr;
89    int     sock;
90    int     on = 1;
91    char   *buf;
92    char   *host;
93    char   *port;
94    const char *parse_err;
95    MAI_HOSTADDR_STR hostaddr;
96    MAI_SERVPORT_STR portnum;
97    INET_PROTO_INFO *proto_info;
98
99    /*
100     * Translate address information to internal form.
101     */
102    buf = mystrdup(addr);
103    if ((parse_err = host_port(buf, &host, "", &port, (char *) 0)) != 0)
104	msg_fatal("%s: %s", addr, parse_err);
105    if (*host == 0)
106	host = 0;
107    if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0)
108	msg_fatal("%s: %s", addr, MAI_STRERROR(aierr));
109    myfree(buf);
110    /* No early returns or res0 leaks. */
111
112    proto_info = inet_proto_info();
113    for (res = res0; /* see below */ ; res = res->ai_next) {
114
115	/*
116	 * No usable address found.
117	 */
118	if (res == 0)
119	    msg_fatal("%s: host found but no usable address", addr);
120
121	/*
122	 * Safety net.
123	 */
124	if (strchr((char *) proto_info->sa_family_list, res->ai_family) != 0)
125	    break;
126
127	msg_info("skipping address family %d for %s", res->ai_family, addr);
128    }
129
130    /*
131     * Show what address we're trying.
132     */
133    if (msg_verbose) {
134	SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
135			     &hostaddr, &portnum, 0);
136	msg_info("trying... [%s]:%s", hostaddr.buf, portnum.buf);
137    }
138
139    /*
140     * Create a listener socket.
141     */
142    if ((sock = socket(res->ai_family, res->ai_socktype, 0)) < 0)
143	msg_fatal("socket: %m");
144#ifdef HAS_IPV6
145# if defined(IPV6_V6ONLY) && !defined(BROKEN_AI_PASSIVE_NULL_HOST)
146    if (res->ai_family == AF_INET6
147	&& setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
148		      (char *) &on, sizeof(on)) < 0)
149	msg_fatal("setsockopt(IPV6_V6ONLY): %m");
150# endif
151#endif
152    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
153		   (char *) &on, sizeof(on)) < 0)
154	msg_fatal("setsockopt(SO_REUSEADDR): %m");
155    if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
156	SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
157			     &hostaddr, &portnum, 0);
158	msg_fatal("bind %s port %s: %m", hostaddr.buf, portnum.buf);
159    }
160    freeaddrinfo(res0);
161    non_blocking(sock, block_mode);
162    if (inet_windowsize > 0)
163	set_inet_windowsize(sock, inet_windowsize);
164    if (listen(sock, backlog) < 0)
165	msg_fatal("listen: %m");
166    return (sock);
167}
168
169/* inet_accept - accept connection */
170
171int     inet_accept(int fd)
172{
173    struct sockaddr_storage ss;
174    SOCKADDR_SIZE ss_len = sizeof(ss);
175
176    return (sane_accept(fd, (struct sockaddr *) & ss, &ss_len));
177}
178