xref: /src/crypto/krb5/src/lib/apputils/net-server.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
133a9b234SCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
233a9b234SCy Schubert /* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */
333a9b234SCy Schubert /*
433a9b234SCy Schubert  * Copyright 1990,2000,2007,2008,2009,2010,2016 by the Massachusetts Institute
533a9b234SCy Schubert  * of Technology.
633a9b234SCy Schubert  *
733a9b234SCy Schubert  * Export of this software from the United States of America may
833a9b234SCy Schubert  *   require a specific license from the United States Government.
933a9b234SCy Schubert  *   It is the responsibility of any person or organization contemplating
1033a9b234SCy Schubert  *   export to obtain such a license before exporting.
1133a9b234SCy Schubert  *
1233a9b234SCy Schubert  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
1333a9b234SCy Schubert  * distribute this software and its documentation for any purpose and
1433a9b234SCy Schubert  * without fee is hereby granted, provided that the above copyright
1533a9b234SCy Schubert  * notice appear in all copies and that both that copyright notice and
1633a9b234SCy Schubert  * this permission notice appear in supporting documentation, and that
1733a9b234SCy Schubert  * the name of M.I.T. not be used in advertising or publicity pertaining
1833a9b234SCy Schubert  * to distribution of the software without specific, written prior
1933a9b234SCy Schubert  * permission.  Furthermore if you modify this software you must label
2033a9b234SCy Schubert  * your software as modified software and not distribute it in such a
2133a9b234SCy Schubert  * fashion that it might be confused with the original M.I.T. software.
2233a9b234SCy Schubert  * M.I.T. makes no representations about the suitability of
2333a9b234SCy Schubert  * this software for any purpose.  It is provided "as is" without express
2433a9b234SCy Schubert  * or implied warranty.
2533a9b234SCy Schubert  */
2633a9b234SCy Schubert 
2733a9b234SCy Schubert #include "k5-int.h"
2833a9b234SCy Schubert #include "adm_proto.h"
2933a9b234SCy Schubert #include <sys/ioctl.h>
3033a9b234SCy Schubert #include <syslog.h>
3133a9b234SCy Schubert 
3233a9b234SCy Schubert #include <stddef.h>
3333a9b234SCy Schubert #include "port-sockets.h"
3433a9b234SCy Schubert #include "socket-utils.h"
3533a9b234SCy Schubert 
3633a9b234SCy Schubert #include <gssrpc/rpc.h>
3733a9b234SCy Schubert 
3833a9b234SCy Schubert #ifdef HAVE_NETINET_IN_H
3933a9b234SCy Schubert #include <sys/types.h>
4033a9b234SCy Schubert #include <netinet/in.h>
4133a9b234SCy Schubert #include <sys/socket.h>
42d82a140dSCy Schubert #include <sys/un.h>
4333a9b234SCy Schubert #ifdef HAVE_SYS_SOCKIO_H
4433a9b234SCy Schubert /* for SIOCGIFCONF, etc. */
4533a9b234SCy Schubert #include <sys/sockio.h>
4633a9b234SCy Schubert #endif
4733a9b234SCy Schubert #include <sys/time.h>
4833a9b234SCy Schubert #if HAVE_SYS_SELECT_H
4933a9b234SCy Schubert #include <sys/select.h>
5033a9b234SCy Schubert #endif
5133a9b234SCy Schubert #include <arpa/inet.h>
5233a9b234SCy Schubert 
5333a9b234SCy Schubert #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
5433a9b234SCy Schubert #include <net/if.h>
5533a9b234SCy Schubert #endif
5633a9b234SCy Schubert 
5733a9b234SCy Schubert #ifdef HAVE_SYS_FILIO_H
5833a9b234SCy Schubert #include <sys/filio.h>          /* FIONBIO */
5933a9b234SCy Schubert #endif
6033a9b234SCy Schubert 
6133a9b234SCy Schubert #include "fake-addrinfo.h"
6233a9b234SCy Schubert #include "net-server.h"
6333a9b234SCy Schubert #include <signal.h>
6433a9b234SCy Schubert #include <netdb.h>
6533a9b234SCy Schubert 
6633a9b234SCy Schubert #include "udppktinfo.h"
6733a9b234SCy Schubert 
68d82a140dSCy Schubert /* List of systemd socket activation addresses and socket types. */
69d82a140dSCy Schubert struct sockact_list {
70d82a140dSCy Schubert     size_t nsockets;
71d82a140dSCy Schubert     struct {
72d82a140dSCy Schubert         struct sockaddr_storage addr;
73d82a140dSCy Schubert         int type;
74d82a140dSCy Schubert     } *fds;
75d82a140dSCy Schubert };
76d82a140dSCy Schubert 
77d82a140dSCy Schubert /* When systemd socket activation is used, caller-provided sockets begin at
78d82a140dSCy Schubert  * file descriptor 3. */
79d82a140dSCy Schubert const int SOCKACT_START = 3;
80d82a140dSCy Schubert 
8133a9b234SCy Schubert /* XXX */
8233a9b234SCy Schubert #define KDC5_NONET                               (-1779992062L)
8333a9b234SCy Schubert 
84d82a140dSCy Schubert static int stream_data_counter;
85d82a140dSCy Schubert static int max_stream_data_connections = 45;
8633a9b234SCy Schubert 
8733a9b234SCy Schubert static int
setreuseaddr(int sock,int value)8833a9b234SCy Schubert setreuseaddr(int sock, int value)
8933a9b234SCy Schubert {
900320e0d5SCy Schubert     int st;
910320e0d5SCy Schubert 
920320e0d5SCy Schubert     st = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
930320e0d5SCy Schubert     if (st)
940320e0d5SCy Schubert         return st;
95d82a140dSCy Schubert #if defined(SO_REUSEPORT) && defined(__APPLE__)
96d82a140dSCy Schubert     /* macOS experimentally needs this flag as well to avoid conflicts between
97d82a140dSCy Schubert      * recently exited server processes and new ones. */
980320e0d5SCy Schubert     st = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
990320e0d5SCy Schubert     if (st)
1000320e0d5SCy Schubert         return st;
1010320e0d5SCy Schubert #endif
1020320e0d5SCy Schubert     return 0;
10333a9b234SCy Schubert }
10433a9b234SCy Schubert 
10533a9b234SCy Schubert #if defined(IPV6_V6ONLY)
10633a9b234SCy Schubert static int
setv6only(int sock,int value)10733a9b234SCy Schubert setv6only(int sock, int value)
10833a9b234SCy Schubert {
10933a9b234SCy Schubert     return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
11033a9b234SCy Schubert }
11133a9b234SCy Schubert #endif
11233a9b234SCy Schubert 
11333a9b234SCy Schubert /* KDC data.  */
11433a9b234SCy Schubert 
11533a9b234SCy Schubert enum conn_type {
116d82a140dSCy Schubert     CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC,
117d82a140dSCy Schubert     CONN_UNIXSOCK_LISTENER, CONN_UNIXSOCK
118d82a140dSCy Schubert };
119d82a140dSCy Schubert 
120d82a140dSCy Schubert static const char *const conn_type_names[] = {
121d82a140dSCy Schubert     [CONN_UDP] = "UDP",
122d82a140dSCy Schubert     [CONN_TCP_LISTENER] = "TCP listener",
123d82a140dSCy Schubert     [CONN_TCP] = "TCP",
124d82a140dSCy Schubert     [CONN_RPC_LISTENER] = "RPC listener",
125d82a140dSCy Schubert     [CONN_RPC] = "RPC",
126d82a140dSCy Schubert     [CONN_UNIXSOCK_LISTENER] = "UNIX domain socket listener",
127d82a140dSCy Schubert     [CONN_UNIXSOCK] = "UNIX domain socket"
12833a9b234SCy Schubert };
12933a9b234SCy Schubert 
13033a9b234SCy Schubert enum bind_type {
131d82a140dSCy Schubert     UDP, TCP, RPC, UNX
13233a9b234SCy Schubert };
13333a9b234SCy Schubert 
13433a9b234SCy Schubert static const char *const bind_type_names[] = {
13533a9b234SCy Schubert     [UDP] = "UDP",
13633a9b234SCy Schubert     [TCP] = "TCP",
13733a9b234SCy Schubert     [RPC] = "RPC",
138d82a140dSCy Schubert     [UNX] = "UNIXSOCK",
13933a9b234SCy Schubert };
14033a9b234SCy Schubert 
14133a9b234SCy Schubert /* Per-connection info.  */
14233a9b234SCy Schubert struct connection {
14333a9b234SCy Schubert     void *handle;
14433a9b234SCy Schubert     const char *prog;
14533a9b234SCy Schubert     enum conn_type type;
14633a9b234SCy Schubert 
14733a9b234SCy Schubert     /* Connection fields (TCP or RPC) */
14833a9b234SCy Schubert     struct sockaddr_storage addr_s;
14933a9b234SCy Schubert     socklen_t addrlen;
150d82a140dSCy Schubert     char addrbuf[128];
15133a9b234SCy Schubert 
15233a9b234SCy Schubert     /* Incoming data (TCP) */
15333a9b234SCy Schubert     size_t bufsiz;
15433a9b234SCy Schubert     size_t offset;
15533a9b234SCy Schubert     char *buffer;
15633a9b234SCy Schubert     size_t msglen;
15733a9b234SCy Schubert 
15833a9b234SCy Schubert     /* Outgoing data (TCP) */
15933a9b234SCy Schubert     krb5_data *response;
16033a9b234SCy Schubert     unsigned char lenbuf[4];
16133a9b234SCy Schubert     sg_buf sgbuf[2];
16233a9b234SCy Schubert     sg_buf *sgp;
16333a9b234SCy Schubert     int sgnum;
16433a9b234SCy Schubert 
16533a9b234SCy Schubert     /* Crude denial-of-service avoidance support (TCP or RPC) */
16633a9b234SCy Schubert     time_t start_time;
16733a9b234SCy Schubert 
16833a9b234SCy Schubert     /* RPC-specific fields */
16933a9b234SCy Schubert     SVCXPRT *transp;
17033a9b234SCy Schubert     int rpc_force_close;
17133a9b234SCy Schubert };
17233a9b234SCy Schubert 
17333a9b234SCy Schubert #define SET(TYPE) struct { TYPE *data; size_t n, max; }
17433a9b234SCy Schubert 
17533a9b234SCy Schubert /* Start at the top and work down -- this should allow for deletions
17633a9b234SCy Schubert    without disrupting the iteration, since we delete by overwriting
17733a9b234SCy Schubert    the element to be removed with the last element.  */
17833a9b234SCy Schubert #define FOREACH_ELT(set,idx,vvar)                                       \
17933a9b234SCy Schubert     for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
18033a9b234SCy Schubert 
18133a9b234SCy Schubert #define GROW_SET(set, incr, tmpptr)                                     \
18233a9b234SCy Schubert     ((set.max + incr < set.max                                          \
18333a9b234SCy Schubert       || ((set.max + incr) * sizeof(set.data[0]) / sizeof(set.data[0])  \
18433a9b234SCy Schubert           != set.max + incr))                                           \
18533a9b234SCy Schubert      ? 0                         /* overflow */                         \
18633a9b234SCy Schubert      : ((tmpptr = realloc(set.data,                                     \
18733a9b234SCy Schubert                           (set.max + incr) * sizeof(set.data[0])))      \
18833a9b234SCy Schubert         ? (set.data = tmpptr, set.max += incr, 1)                       \
18933a9b234SCy Schubert         : 0))
19033a9b234SCy Schubert 
19133a9b234SCy Schubert /* 1 = success, 0 = failure */
19233a9b234SCy Schubert #define ADD(set, val, tmpptr)                           \
19333a9b234SCy Schubert     ((set.n < set.max || GROW_SET(set, 10, tmpptr))     \
19433a9b234SCy Schubert      ? (set.data[set.n++] = val, 1)                     \
19533a9b234SCy Schubert      : 0)
19633a9b234SCy Schubert 
19733a9b234SCy Schubert #define DEL(set, idx)                           \
19833a9b234SCy Schubert     (set.data[idx] = set.data[--set.n], 0)
19933a9b234SCy Schubert 
20033a9b234SCy Schubert #define FREE_SET_DATA(set)                                      \
20133a9b234SCy Schubert     (free(set.data), set.data = 0, set.max = 0, set.n = 0)
20233a9b234SCy Schubert 
20333a9b234SCy Schubert /*
20433a9b234SCy Schubert  * N.B.: The Emacs cc-mode indentation code seems to get confused if
20533a9b234SCy Schubert  * the macro argument here is one word only.  So use "unsigned short"
20633a9b234SCy Schubert  * instead of the "u_short" we were using before.
20733a9b234SCy Schubert  */
20833a9b234SCy Schubert struct rpc_svc_data {
20933a9b234SCy Schubert     u_long prognum;
21033a9b234SCy Schubert     u_long versnum;
211d82a140dSCy Schubert     void (*dispatch)(struct svc_req *, SVCXPRT *);
21233a9b234SCy Schubert };
21333a9b234SCy Schubert 
21433a9b234SCy Schubert struct bind_address {
21533a9b234SCy Schubert     char *address;
21633a9b234SCy Schubert     u_short port;
21733a9b234SCy Schubert     enum bind_type type;
21833a9b234SCy Schubert     struct rpc_svc_data rpc_svc_data;
21933a9b234SCy Schubert };
22033a9b234SCy Schubert 
22133a9b234SCy Schubert static SET(verto_ev *) events;
22233a9b234SCy Schubert static SET(struct bind_address) bind_addresses;
22333a9b234SCy Schubert 
22433a9b234SCy Schubert verto_ctx *
loop_init(verto_ev_type types)22533a9b234SCy Schubert loop_init(verto_ev_type types)
22633a9b234SCy Schubert {
22733a9b234SCy Schubert     types |= VERTO_EV_TYPE_IO;
22833a9b234SCy Schubert     types |= VERTO_EV_TYPE_SIGNAL;
22933a9b234SCy Schubert     types |= VERTO_EV_TYPE_TIMEOUT;
23033a9b234SCy Schubert     return verto_default(NULL, types);
23133a9b234SCy Schubert }
23233a9b234SCy Schubert 
23333a9b234SCy Schubert static void
do_break(verto_ctx * ctx,verto_ev * ev)23433a9b234SCy Schubert do_break(verto_ctx *ctx, verto_ev *ev)
23533a9b234SCy Schubert {
23633a9b234SCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));
23733a9b234SCy Schubert     verto_break(ctx);
23833a9b234SCy Schubert }
23933a9b234SCy Schubert 
24033a9b234SCy Schubert struct sighup_context {
24133a9b234SCy Schubert     void *handle;
24233a9b234SCy Schubert     void (*reset)(void *);
24333a9b234SCy Schubert };
24433a9b234SCy Schubert 
24533a9b234SCy Schubert static void
do_reset(verto_ctx * ctx,verto_ev * ev)24633a9b234SCy Schubert do_reset(verto_ctx *ctx, verto_ev *ev)
24733a9b234SCy Schubert {
24833a9b234SCy Schubert     struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev);
24933a9b234SCy Schubert 
25033a9b234SCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset"));
25133a9b234SCy Schubert     krb5_klog_reopen(get_context(sc->handle));
25233a9b234SCy Schubert     if (sc->reset)
25333a9b234SCy Schubert         sc->reset(sc->handle);
25433a9b234SCy Schubert }
25533a9b234SCy Schubert 
25633a9b234SCy Schubert static void
free_sighup_context(verto_ctx * ctx,verto_ev * ev)25733a9b234SCy Schubert free_sighup_context(verto_ctx *ctx, verto_ev *ev)
25833a9b234SCy Schubert {
25933a9b234SCy Schubert     free(verto_get_private(ev));
26033a9b234SCy Schubert }
26133a9b234SCy Schubert 
26233a9b234SCy Schubert krb5_error_code
loop_setup_signals(verto_ctx * ctx,void * handle,void (* reset)(void *))263d82a140dSCy Schubert loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)(void *))
26433a9b234SCy Schubert {
26533a9b234SCy Schubert     struct sighup_context *sc;
26633a9b234SCy Schubert     verto_ev *ev;
26733a9b234SCy Schubert 
26833a9b234SCy Schubert     if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT)  ||
26933a9b234SCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) ||
27033a9b234SCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) ||
27133a9b234SCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE))
27233a9b234SCy Schubert         return ENOMEM;
27333a9b234SCy Schubert 
27433a9b234SCy Schubert     ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP);
27533a9b234SCy Schubert     if (!ev)
27633a9b234SCy Schubert         return ENOMEM;
27733a9b234SCy Schubert 
27833a9b234SCy Schubert     sc = malloc(sizeof(*sc));
27933a9b234SCy Schubert     if (!sc)
28033a9b234SCy Schubert         return ENOMEM;
28133a9b234SCy Schubert 
28233a9b234SCy Schubert     sc->handle = handle;
28333a9b234SCy Schubert     sc->reset = reset;
28433a9b234SCy Schubert     verto_set_private(ev, sc, free_sighup_context);
28533a9b234SCy Schubert     return 0;
28633a9b234SCy Schubert }
28733a9b234SCy Schubert 
28833a9b234SCy Schubert /*
28933a9b234SCy Schubert  * Add a bind address to the loop.
29033a9b234SCy Schubert  *
29133a9b234SCy Schubert  * Arguments:
29233a9b234SCy Schubert  * - address
293d82a140dSCy Schubert  *      An address string, hostname, or UNIX socket path.
294d82a140dSCy Schubert  *      Pass NULL to use the wildcard address for IP sockets.
29533a9b234SCy Schubert  * - port
296d82a140dSCy Schubert  *      What port the socket should be set to (for IPv4 or IPv6).
29733a9b234SCy Schubert  * - type
29833a9b234SCy Schubert  *      bind_type for the socket.
29933a9b234SCy Schubert  * - rpc_data
30033a9b234SCy Schubert  *      For RPC addresses, the svc_register() arguments to use when TCP
30133a9b234SCy Schubert  *      connections are created.  Ignored for other types.
30233a9b234SCy Schubert  */
30333a9b234SCy Schubert static krb5_error_code
loop_add_address(const char * address,int port,enum bind_type type,struct rpc_svc_data * rpc_data)30433a9b234SCy Schubert loop_add_address(const char *address, int port, enum bind_type type,
30533a9b234SCy Schubert                  struct rpc_svc_data *rpc_data)
30633a9b234SCy Schubert {
30733a9b234SCy Schubert     struct bind_address addr, val;
30833a9b234SCy Schubert     int i;
30933a9b234SCy Schubert     void *tmp;
31033a9b234SCy Schubert     char *addr_copy = NULL;
31133a9b234SCy Schubert 
31233a9b234SCy Schubert     assert(!(type == RPC && rpc_data == NULL));
31333a9b234SCy Schubert 
31433a9b234SCy Schubert     /* Make sure a valid port number was passed. */
31533a9b234SCy Schubert     if (port < 0 || port > 65535) {
31633a9b234SCy Schubert         krb5_klog_syslog(LOG_ERR, _("Invalid port %d"), port);
31733a9b234SCy Schubert         return EINVAL;
31833a9b234SCy Schubert     }
31933a9b234SCy Schubert 
32033a9b234SCy Schubert     /* Check for conflicting addresses. */
32133a9b234SCy Schubert     FOREACH_ELT(bind_addresses, i, val) {
32233a9b234SCy Schubert         if (type != val.type || port != val.port)
32333a9b234SCy Schubert             continue;
32433a9b234SCy Schubert 
32533a9b234SCy Schubert         /* If a wildcard address is being added, make sure to remove any direct
32633a9b234SCy Schubert          * addresses. */
32733a9b234SCy Schubert         if (address == NULL && val.address != NULL) {
32833a9b234SCy Schubert             krb5_klog_syslog(LOG_DEBUG,
32933a9b234SCy Schubert                              _("Removing address %s since wildcard address"
33033a9b234SCy Schubert                                " is being added"),
33133a9b234SCy Schubert                              val.address);
33233a9b234SCy Schubert             free(val.address);
33333a9b234SCy Schubert             DEL(bind_addresses, i);
33433a9b234SCy Schubert         } else if (val.address == NULL || !strcmp(address, val.address)) {
33533a9b234SCy Schubert             krb5_klog_syslog(LOG_DEBUG,
33633a9b234SCy Schubert                              _("Address already added to server"));
33733a9b234SCy Schubert             return 0;
33833a9b234SCy Schubert         }
33933a9b234SCy Schubert     }
34033a9b234SCy Schubert 
34133a9b234SCy Schubert     /* Copy the address if it is specified. */
34233a9b234SCy Schubert     if (address != NULL) {
34333a9b234SCy Schubert         addr_copy = strdup(address);
34433a9b234SCy Schubert         if (addr_copy == NULL)
34533a9b234SCy Schubert             return ENOMEM;
34633a9b234SCy Schubert     }
34733a9b234SCy Schubert 
34833a9b234SCy Schubert     /* Add the new address to bind_addresses. */
34933a9b234SCy Schubert     memset(&addr, 0, sizeof(addr));
35033a9b234SCy Schubert     addr.address = addr_copy;
35133a9b234SCy Schubert     addr.port = port;
35233a9b234SCy Schubert     addr.type = type;
35333a9b234SCy Schubert     if (rpc_data != NULL)
35433a9b234SCy Schubert         addr.rpc_svc_data = *rpc_data;
35533a9b234SCy Schubert     if (!ADD(bind_addresses, addr, tmp)) {
35633a9b234SCy Schubert         free(addr_copy);
35733a9b234SCy Schubert         return ENOMEM;
35833a9b234SCy Schubert     }
35933a9b234SCy Schubert 
36033a9b234SCy Schubert     return 0;
36133a9b234SCy Schubert }
36233a9b234SCy Schubert 
36333a9b234SCy Schubert /*
36433a9b234SCy Schubert  * Add bind addresses to the loop.
36533a9b234SCy Schubert  *
36633a9b234SCy Schubert  * Arguments:
36733a9b234SCy Schubert  *
36833a9b234SCy Schubert  * - addresses
36933a9b234SCy Schubert  *      A string for the addresses.  Pass NULL to use the wildcard address.
3700320e0d5SCy Schubert  *      Supported delimiters can be found in ADDRESSES_DELIM.  Addresses are
37133a9b234SCy Schubert  *      parsed with k5_parse_host_name().
37233a9b234SCy Schubert  * - default_port
37333a9b234SCy Schubert  *      What port the socket should be set to if not specified in addresses.
37433a9b234SCy Schubert  * - type
37533a9b234SCy Schubert  *      bind_type for the socket.
37633a9b234SCy Schubert  * - rpc_data
37733a9b234SCy Schubert  *      For RPC addresses, the svc_register() arguments to use when TCP
37833a9b234SCy Schubert  *      connections are created.  Ignored for other types.
37933a9b234SCy Schubert  */
38033a9b234SCy Schubert static krb5_error_code
loop_add_addresses(const char * addresses,int default_port,enum bind_type type,struct rpc_svc_data * rpc_data)38133a9b234SCy Schubert loop_add_addresses(const char *addresses, int default_port,
38233a9b234SCy Schubert                    enum bind_type type, struct rpc_svc_data *rpc_data)
38333a9b234SCy Schubert {
38433a9b234SCy Schubert     krb5_error_code ret = 0;
38533a9b234SCy Schubert     char *addresses_copy = NULL, *host = NULL, *saveptr, *addr;
38633a9b234SCy Schubert     int port;
38733a9b234SCy Schubert 
38833a9b234SCy Schubert     /* If no addresses are set, add a wildcard address. */
38933a9b234SCy Schubert     if (addresses == NULL)
39033a9b234SCy Schubert         return loop_add_address(NULL, default_port, type, rpc_data);
39133a9b234SCy Schubert 
39233a9b234SCy Schubert     /* Copy the addresses string before using strtok(). */
39333a9b234SCy Schubert     addresses_copy = strdup(addresses);
39433a9b234SCy Schubert     if (addresses_copy == NULL) {
39533a9b234SCy Schubert         ret = ENOMEM;
39633a9b234SCy Schubert         goto cleanup;
39733a9b234SCy Schubert     }
39833a9b234SCy Schubert 
399d82a140dSCy Schubert     /* Loop through each address in the string and add it to the loop. */
40033a9b234SCy Schubert     addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr);
401d82a140dSCy Schubert     for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) {
402d82a140dSCy Schubert         if (type == UNX) {
403d82a140dSCy Schubert             /* Skip non-pathnames when binding UNIX domain sockets. */
404d82a140dSCy Schubert             if (*addr != '/')
405d82a140dSCy Schubert                 continue;
406d82a140dSCy Schubert             ret = loop_add_address(addr, 0, type, rpc_data);
407d82a140dSCy Schubert             if (ret)
40833a9b234SCy Schubert                 goto cleanup;
409d82a140dSCy Schubert             continue;
410d82a140dSCy Schubert         } else if (*addr == '/') {
411d82a140dSCy Schubert             /* Skip pathnames when not binding UNIX domain sockets. */
412d82a140dSCy Schubert             continue;
41333a9b234SCy Schubert         }
41433a9b234SCy Schubert 
41533a9b234SCy Schubert         /* Parse the host string. */
41633a9b234SCy Schubert         ret = k5_parse_host_string(addr, default_port, &host, &port);
41733a9b234SCy Schubert         if (ret)
41833a9b234SCy Schubert             goto cleanup;
41933a9b234SCy Schubert 
42033a9b234SCy Schubert         ret = loop_add_address(host, port, type, rpc_data);
42133a9b234SCy Schubert         if (ret)
42233a9b234SCy Schubert             goto cleanup;
42333a9b234SCy Schubert 
42433a9b234SCy Schubert         free(host);
42533a9b234SCy Schubert         host = NULL;
42633a9b234SCy Schubert     }
42733a9b234SCy Schubert 
428d82a140dSCy Schubert     ret = 0;
42933a9b234SCy Schubert cleanup:
43033a9b234SCy Schubert     free(addresses_copy);
43133a9b234SCy Schubert     free(host);
43233a9b234SCy Schubert     return ret;
43333a9b234SCy Schubert }
43433a9b234SCy Schubert 
43533a9b234SCy Schubert krb5_error_code
loop_add_udp_address(int default_port,const char * addresses)43633a9b234SCy Schubert loop_add_udp_address(int default_port, const char *addresses)
43733a9b234SCy Schubert {
43833a9b234SCy Schubert     return loop_add_addresses(addresses, default_port, UDP, NULL);
43933a9b234SCy Schubert }
44033a9b234SCy Schubert 
44133a9b234SCy Schubert krb5_error_code
loop_add_tcp_address(int default_port,const char * addresses)44233a9b234SCy Schubert loop_add_tcp_address(int default_port, const char *addresses)
44333a9b234SCy Schubert {
44433a9b234SCy Schubert     return loop_add_addresses(addresses, default_port, TCP, NULL);
44533a9b234SCy Schubert }
44633a9b234SCy Schubert 
44733a9b234SCy Schubert krb5_error_code
loop_add_rpc_service(int default_port,const char * addresses,u_long prognum,u_long versnum,void (* dispatchfn)(struct svc_req *,SVCXPRT *))44833a9b234SCy Schubert loop_add_rpc_service(int default_port, const char *addresses, u_long prognum,
449d82a140dSCy Schubert                      u_long versnum,
450d82a140dSCy Schubert                      void (*dispatchfn)(struct svc_req *, SVCXPRT *))
45133a9b234SCy Schubert {
45233a9b234SCy Schubert     struct rpc_svc_data svc;
45333a9b234SCy Schubert 
45433a9b234SCy Schubert     svc.prognum = prognum;
45533a9b234SCy Schubert     svc.versnum = versnum;
45633a9b234SCy Schubert     svc.dispatch = dispatchfn;
45733a9b234SCy Schubert     return loop_add_addresses(addresses, default_port, RPC, &svc);
45833a9b234SCy Schubert }
45933a9b234SCy Schubert 
460d82a140dSCy Schubert krb5_error_code
loop_add_unix_socket(const char * socket_paths)461d82a140dSCy Schubert loop_add_unix_socket(const char *socket_paths)
462d82a140dSCy Schubert {
463d82a140dSCy Schubert     /* There is no wildcard or default UNIX domain socket. */
464d82a140dSCy Schubert     if (socket_paths == NULL)
465d82a140dSCy Schubert         return 0;
466d82a140dSCy Schubert 
467d82a140dSCy Schubert     return loop_add_addresses(socket_paths, 0, UNX, NULL);
468d82a140dSCy Schubert }
469d82a140dSCy Schubert 
47033a9b234SCy Schubert #define USE_AF AF_INET
47133a9b234SCy Schubert #define USE_TYPE SOCK_DGRAM
47233a9b234SCy Schubert #define USE_PROTO 0
47333a9b234SCy Schubert #define SOCKET_ERRNO errno
47433a9b234SCy Schubert #include "foreachaddr.h"
47533a9b234SCy Schubert 
47633a9b234SCy Schubert static void
free_connection(struct connection * conn)47733a9b234SCy Schubert free_connection(struct connection *conn)
47833a9b234SCy Schubert {
47933a9b234SCy Schubert     if (!conn)
48033a9b234SCy Schubert         return;
48133a9b234SCy Schubert     if (conn->response)
48233a9b234SCy Schubert         krb5_free_data(get_context(conn->handle), conn->response);
48333a9b234SCy Schubert     if (conn->buffer)
48433a9b234SCy Schubert         free(conn->buffer);
48533a9b234SCy Schubert     if (conn->type == CONN_RPC_LISTENER && conn->transp != NULL)
48633a9b234SCy Schubert         svc_destroy(conn->transp);
48733a9b234SCy Schubert     free(conn);
48833a9b234SCy Schubert }
48933a9b234SCy Schubert 
49033a9b234SCy Schubert static void
remove_event_from_set(verto_ev * ev)49133a9b234SCy Schubert remove_event_from_set(verto_ev *ev)
49233a9b234SCy Schubert {
49333a9b234SCy Schubert     verto_ev *tmp;
49433a9b234SCy Schubert     int i;
49533a9b234SCy Schubert 
49633a9b234SCy Schubert     /* Remove the event from the events. */
49733a9b234SCy Schubert     FOREACH_ELT(events, i, tmp)
49833a9b234SCy Schubert         if (tmp == ev) {
49933a9b234SCy Schubert             DEL(events, i);
50033a9b234SCy Schubert             break;
50133a9b234SCy Schubert         }
50233a9b234SCy Schubert }
50333a9b234SCy Schubert 
50433a9b234SCy Schubert static void
free_socket(verto_ctx * ctx,verto_ev * ev)50533a9b234SCy Schubert free_socket(verto_ctx *ctx, verto_ev *ev)
50633a9b234SCy Schubert {
50733a9b234SCy Schubert     struct connection *conn = NULL;
50833a9b234SCy Schubert     fd_set fds;
50933a9b234SCy Schubert     int fd;
51033a9b234SCy Schubert 
51133a9b234SCy Schubert     remove_event_from_set(ev);
51233a9b234SCy Schubert 
51333a9b234SCy Schubert     fd = verto_get_fd(ev);
51433a9b234SCy Schubert     conn = verto_get_private(ev);
51533a9b234SCy Schubert 
51633a9b234SCy Schubert     /* Close the file descriptor. */
51733a9b234SCy Schubert     krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd);
51833a9b234SCy Schubert     if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->rpc_force_close))
51933a9b234SCy Schubert         close(fd);
52033a9b234SCy Schubert 
52133a9b234SCy Schubert     /* Free the connection struct. */
52233a9b234SCy Schubert     if (conn) {
52333a9b234SCy Schubert         switch (conn->type) {
52433a9b234SCy Schubert         case CONN_RPC:
52533a9b234SCy Schubert             if (conn->rpc_force_close) {
52633a9b234SCy Schubert                 FD_ZERO(&fds);
52733a9b234SCy Schubert                 FD_SET(fd, &fds);
52833a9b234SCy Schubert                 svc_getreqset(&fds);
52933a9b234SCy Schubert                 if (FD_ISSET(fd, &svc_fdset)) {
53033a9b234SCy Schubert                     krb5_klog_syslog(LOG_ERR,
53133a9b234SCy Schubert                                      _("descriptor %d closed but still "
53233a9b234SCy Schubert                                        "in svc_fdset"),
53333a9b234SCy Schubert                                      fd);
53433a9b234SCy Schubert                 }
53533a9b234SCy Schubert             }
53633a9b234SCy Schubert             /* Fall through. */
53733a9b234SCy Schubert         case CONN_TCP:
538d82a140dSCy Schubert         case CONN_UNIXSOCK:
539d82a140dSCy Schubert             stream_data_counter--;
54033a9b234SCy Schubert             break;
54133a9b234SCy Schubert         default:
54233a9b234SCy Schubert             break;
54333a9b234SCy Schubert         }
54433a9b234SCy Schubert 
54533a9b234SCy Schubert         free_connection(conn);
54633a9b234SCy Schubert     }
54733a9b234SCy Schubert }
54833a9b234SCy Schubert 
54933a9b234SCy Schubert static verto_ev *
make_event(verto_ctx * ctx,verto_ev_flag flags,verto_callback callback,int sock,struct connection * conn)55033a9b234SCy Schubert make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback,
551b0e4d68dSCy Schubert            int sock, struct connection *conn)
55233a9b234SCy Schubert {
55333a9b234SCy Schubert     verto_ev *ev;
55433a9b234SCy Schubert     void *tmp;
55533a9b234SCy Schubert 
55633a9b234SCy Schubert     ev = verto_add_io(ctx, flags, callback, sock);
55733a9b234SCy Schubert     if (!ev) {
55833a9b234SCy Schubert         com_err(conn->prog, ENOMEM, _("cannot create io event"));
55933a9b234SCy Schubert         return NULL;
56033a9b234SCy Schubert     }
56133a9b234SCy Schubert 
56233a9b234SCy Schubert     if (!ADD(events, ev, tmp)) {
56333a9b234SCy Schubert         com_err(conn->prog, ENOMEM, _("cannot save event"));
56433a9b234SCy Schubert         verto_del(ev);
56533a9b234SCy Schubert         return NULL;
56633a9b234SCy Schubert     }
56733a9b234SCy Schubert 
56833a9b234SCy Schubert     verto_set_private(ev, conn, free_socket);
56933a9b234SCy Schubert     return ev;
57033a9b234SCy Schubert }
57133a9b234SCy Schubert 
572b0e4d68dSCy Schubert static krb5_error_code
add_fd(int sock,enum conn_type conntype,verto_ev_flag flags,void * handle,const char * prog,verto_ctx * ctx,verto_callback callback,verto_ev ** ev_out)573b0e4d68dSCy Schubert add_fd(int sock, enum conn_type conntype, verto_ev_flag flags, void *handle,
574b0e4d68dSCy Schubert        const char *prog, verto_ctx *ctx, verto_callback callback,
575b0e4d68dSCy Schubert        verto_ev **ev_out)
57633a9b234SCy Schubert {
57733a9b234SCy Schubert     struct connection *newconn;
57833a9b234SCy Schubert 
579b0e4d68dSCy Schubert     *ev_out = NULL;
580b0e4d68dSCy Schubert 
58133a9b234SCy Schubert #ifndef _WIN32
58233a9b234SCy Schubert     if (sock >= FD_SETSIZE) {
583b0e4d68dSCy Schubert         com_err(prog, 0, _("file descriptor number %d too high"), sock);
584b0e4d68dSCy Schubert         return EMFILE;
58533a9b234SCy Schubert     }
58633a9b234SCy Schubert #endif
58733a9b234SCy Schubert     newconn = malloc(sizeof(*newconn));
58833a9b234SCy Schubert     if (newconn == NULL) {
589b0e4d68dSCy Schubert         com_err(prog, ENOMEM,
59033a9b234SCy Schubert                 _("cannot allocate storage for connection info"));
591b0e4d68dSCy Schubert         return ENOMEM;
59233a9b234SCy Schubert     }
59333a9b234SCy Schubert     memset(newconn, 0, sizeof(*newconn));
594b0e4d68dSCy Schubert     newconn->handle = handle;
595b0e4d68dSCy Schubert     newconn->prog = prog;
59633a9b234SCy Schubert     newconn->type = conntype;
59733a9b234SCy Schubert 
598b0e4d68dSCy Schubert     *ev_out = make_event(ctx, flags, callback, sock, newconn);
599b0e4d68dSCy Schubert     return 0;
60033a9b234SCy Schubert }
60133a9b234SCy Schubert 
60233a9b234SCy Schubert static void process_packet(verto_ctx *ctx, verto_ev *ev);
603d82a140dSCy Schubert static void accept_stream_connection(verto_ctx *ctx, verto_ev *ev);
604d82a140dSCy Schubert static void process_stream_connection_read(verto_ctx *ctx, verto_ev *ev);
605d82a140dSCy Schubert static void process_stream_connection_write(verto_ctx *ctx, verto_ev *ev);
60633a9b234SCy Schubert static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);
60733a9b234SCy Schubert static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);
60833a9b234SCy Schubert 
60933a9b234SCy Schubert /*
61033a9b234SCy Schubert  * Create a socket and bind it to addr.  Ensure the socket will work with
61133a9b234SCy Schubert  * select().  Set the socket cloexec, reuseaddr, and if applicable v6-only.
612b0e4d68dSCy Schubert  * Does not call listen().  On failure, log an error and return an error code.
61333a9b234SCy Schubert  */
614b0e4d68dSCy Schubert static krb5_error_code
create_server_socket(struct sockaddr * addr,int type,const char * prog,int * fd_out)615b0e4d68dSCy Schubert create_server_socket(struct sockaddr *addr, int type, const char *prog,
616b0e4d68dSCy Schubert                      int *fd_out)
61733a9b234SCy Schubert {
618b0e4d68dSCy Schubert     int sock, e;
619d82a140dSCy Schubert     char addrbuf[128];
620b0e4d68dSCy Schubert 
621b0e4d68dSCy Schubert     *fd_out = -1;
62233a9b234SCy Schubert 
623d82a140dSCy Schubert     if (addr->sa_family == AF_UNIX)
624d82a140dSCy Schubert         (void)unlink(sa2sun(addr)->sun_path);
62533a9b234SCy Schubert     sock = socket(addr->sa_family, type, 0);
62633a9b234SCy Schubert     if (sock == -1) {
627b0e4d68dSCy Schubert         e = errno;
628d82a140dSCy Schubert         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
629d82a140dSCy Schubert         com_err(prog, e, _("Cannot create TCP server socket on %s"), addrbuf);
630b0e4d68dSCy Schubert         return e;
63133a9b234SCy Schubert     }
63233a9b234SCy Schubert     set_cloexec_fd(sock);
63333a9b234SCy Schubert 
63433a9b234SCy Schubert #ifndef _WIN32                  /* Windows FD_SETSIZE is a count. */
63533a9b234SCy Schubert     if (sock >= FD_SETSIZE) {
63633a9b234SCy Schubert         close(sock);
637d82a140dSCy Schubert         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
638b0e4d68dSCy Schubert         com_err(prog, 0, _("TCP socket fd number %d (for %s) too high"),
639d82a140dSCy Schubert                 sock, addrbuf);
640b0e4d68dSCy Schubert         return EMFILE;
64133a9b234SCy Schubert     }
64233a9b234SCy Schubert #endif
64333a9b234SCy Schubert 
644b0e4d68dSCy Schubert     if (setreuseaddr(sock, 1) < 0)
645b0e4d68dSCy Schubert         com_err(prog, errno, _("Cannot enable SO_REUSEADDR on fd %d"), sock);
64633a9b234SCy Schubert 
64733a9b234SCy Schubert     if (addr->sa_family == AF_INET6) {
64833a9b234SCy Schubert #ifdef IPV6_V6ONLY
649b0e4d68dSCy Schubert         if (setv6only(sock, 1)) {
650b0e4d68dSCy Schubert             com_err(prog, errno, _("setsockopt(%d,IPV6_V6ONLY,1) failed"),
65133a9b234SCy Schubert                     sock);
652b0e4d68dSCy Schubert         } else {
653b0e4d68dSCy Schubert             com_err(prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"), sock);
654b0e4d68dSCy Schubert         }
65533a9b234SCy Schubert #else
65633a9b234SCy Schubert         krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support"));
65733a9b234SCy Schubert #endif /* IPV6_V6ONLY */
65833a9b234SCy Schubert     }
65933a9b234SCy Schubert 
66033a9b234SCy Schubert     if (bind(sock, addr, sa_socklen(addr)) == -1) {
661b0e4d68dSCy Schubert         e = errno;
662d82a140dSCy Schubert         k5_print_addr_port(addr, addrbuf, sizeof(addrbuf));
663d82a140dSCy Schubert         com_err(prog, e, _("Cannot bind server socket on %s"), addrbuf);
66433a9b234SCy Schubert         close(sock);
665b0e4d68dSCy Schubert         return e;
66633a9b234SCy Schubert     }
66733a9b234SCy Schubert 
668b0e4d68dSCy Schubert     *fd_out = sock;
669b0e4d68dSCy Schubert     return 0;
67033a9b234SCy Schubert }
67133a9b234SCy Schubert 
67233a9b234SCy Schubert static const int one = 1;
67333a9b234SCy Schubert 
67433a9b234SCy Schubert static int
setnbio(int sock)67533a9b234SCy Schubert setnbio(int sock)
67633a9b234SCy Schubert {
67733a9b234SCy Schubert     return ioctlsocket(sock, FIONBIO, (const void *)&one);
67833a9b234SCy Schubert }
67933a9b234SCy Schubert 
68033a9b234SCy Schubert static int
setkeepalive(int sock)68133a9b234SCy Schubert setkeepalive(int sock)
68233a9b234SCy Schubert {
68333a9b234SCy Schubert     return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
68433a9b234SCy Schubert }
68533a9b234SCy Schubert 
68633a9b234SCy Schubert static int
setnolinger(int s)68733a9b234SCy Schubert setnolinger(int s)
68833a9b234SCy Schubert {
68933a9b234SCy Schubert     static const struct linger ling = { 0, 0 };
69033a9b234SCy Schubert     return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
69133a9b234SCy Schubert }
69233a9b234SCy Schubert 
69333a9b234SCy Schubert /* An enum map to socket families for each bind_type. */
69433a9b234SCy Schubert static const int bind_socktypes[] =
69533a9b234SCy Schubert {
69633a9b234SCy Schubert     [UDP] = SOCK_DGRAM,
69733a9b234SCy Schubert     [TCP] = SOCK_STREAM,
698d82a140dSCy Schubert     [RPC] = SOCK_STREAM,
699d82a140dSCy Schubert     [UNX] = SOCK_STREAM
70033a9b234SCy Schubert };
70133a9b234SCy Schubert 
70233a9b234SCy Schubert /* An enum map containing conn_type (for struct connection) for each
70333a9b234SCy Schubert  * bind_type.  */
70433a9b234SCy Schubert static const enum conn_type bind_conn_types[] =
70533a9b234SCy Schubert {
70633a9b234SCy Schubert     [UDP] = CONN_UDP,
70733a9b234SCy Schubert     [TCP] = CONN_TCP_LISTENER,
708d82a140dSCy Schubert     [RPC] = CONN_RPC_LISTENER,
709d82a140dSCy Schubert     [UNX] = CONN_UNIXSOCK_LISTENER
71033a9b234SCy Schubert };
71133a9b234SCy Schubert 
712d82a140dSCy Schubert /* If any systemd socket activation fds are indicated by the environment, set
713d82a140dSCy Schubert  * them close-on-exec and put their addresses and socket types into *list. */
714d82a140dSCy Schubert static void
init_sockact_list(struct sockact_list * list)715d82a140dSCy Schubert init_sockact_list(struct sockact_list *list)
716d82a140dSCy Schubert {
717d82a140dSCy Schubert     const char *v;
718d82a140dSCy Schubert     char *end;
719d82a140dSCy Schubert     long lpid;
720d82a140dSCy Schubert     int fd;
721d82a140dSCy Schubert     size_t nfds, i;
722d82a140dSCy Schubert     socklen_t slen;
723d82a140dSCy Schubert 
724d82a140dSCy Schubert     list->nsockets = 0;
725d82a140dSCy Schubert     list->fds = NULL;
726d82a140dSCy Schubert 
727d82a140dSCy Schubert     /* Check if LISTEN_FDS is meant for this process. */
728d82a140dSCy Schubert     v = getenv("LISTEN_PID");
729d82a140dSCy Schubert     if (v == NULL)
730d82a140dSCy Schubert         return;
731d82a140dSCy Schubert     lpid = strtol(v, &end, 10);
732d82a140dSCy Schubert     if (end == NULL || end == v || *end != '\0' || lpid != getpid())
733d82a140dSCy Schubert         return;
734d82a140dSCy Schubert 
735d82a140dSCy Schubert     /* Get the number of activated sockets. */
736d82a140dSCy Schubert     v = getenv("LISTEN_FDS");
737d82a140dSCy Schubert     if (v == NULL)
738d82a140dSCy Schubert         return;
739d82a140dSCy Schubert     nfds = strtoul(v, &end, 10);
740d82a140dSCy Schubert     if (end == NULL || end == v || *end != '\0')
741d82a140dSCy Schubert         return;
742d82a140dSCy Schubert     if (nfds == 0 || nfds > (size_t)INT_MAX - SOCKACT_START)
743d82a140dSCy Schubert         return;
744d82a140dSCy Schubert 
745d82a140dSCy Schubert     list->fds = calloc(nfds, sizeof(*list->fds));
746d82a140dSCy Schubert     if (list->fds == NULL)
747d82a140dSCy Schubert         return;
748d82a140dSCy Schubert 
749d82a140dSCy Schubert     for (i = 0; i < nfds; i++) {
750d82a140dSCy Schubert         fd = i + SOCKACT_START;
751d82a140dSCy Schubert         set_cloexec_fd(fd);
752d82a140dSCy Schubert         slen = sizeof(list->fds[i].addr);
753d82a140dSCy Schubert         (void)getsockname(fd, ss2sa(&list->fds[i].addr), &slen);
754d82a140dSCy Schubert         slen = sizeof(list->fds[i].type);
755d82a140dSCy Schubert         (void)getsockopt(fd, SOL_SOCKET, SO_TYPE, &list->fds[i].type, &slen);
756d82a140dSCy Schubert     }
757d82a140dSCy Schubert 
758d82a140dSCy Schubert     list->nsockets = nfds;
759d82a140dSCy Schubert }
760d82a140dSCy Schubert 
761d82a140dSCy Schubert /* Release any storage used by *list. */
762d82a140dSCy Schubert static void
fini_sockact_list(struct sockact_list * list)763d82a140dSCy Schubert fini_sockact_list(struct sockact_list *list)
764d82a140dSCy Schubert {
765d82a140dSCy Schubert     free(list->fds);
766d82a140dSCy Schubert     list->fds = NULL;
767d82a140dSCy Schubert     list->nsockets = 0;
768d82a140dSCy Schubert }
769d82a140dSCy Schubert 
770d82a140dSCy Schubert /* If sa matches an address in *list, return the associated file descriptor and
771d82a140dSCy Schubert  * clear the address from *list.  Otherwise return -1. */
772d82a140dSCy Schubert static int
find_sockact(struct sockact_list * list,const struct sockaddr * sa,int type)773d82a140dSCy Schubert find_sockact(struct sockact_list *list, const struct sockaddr *sa, int type)
774d82a140dSCy Schubert {
775d82a140dSCy Schubert     size_t i;
776d82a140dSCy Schubert 
777d82a140dSCy Schubert     for (i = 0; i < list->nsockets; i++) {
778d82a140dSCy Schubert         if (list->fds[i].type == type &&
779d82a140dSCy Schubert             sa_equal(ss2sa(&list->fds[i].addr), sa)) {
780d82a140dSCy Schubert             list->fds[i].type = -1;
781d82a140dSCy Schubert             memset(&list->fds[i].addr, 0, sizeof(list->fds[i].addr));
782d82a140dSCy Schubert             return i + SOCKACT_START;
783d82a140dSCy Schubert         }
784d82a140dSCy Schubert     }
785d82a140dSCy Schubert     return -1;
786d82a140dSCy Schubert }
787d82a140dSCy Schubert 
78833a9b234SCy Schubert /*
78933a9b234SCy Schubert  * Set up a listening socket.
79033a9b234SCy Schubert  *
79133a9b234SCy Schubert  * Arguments:
79233a9b234SCy Schubert  *
79333a9b234SCy Schubert  * - ba
79433a9b234SCy Schubert  *      The bind address and port for the socket.
79533a9b234SCy Schubert  * - ai
79633a9b234SCy Schubert  *      The addrinfo struct to use for creating the socket.
79733a9b234SCy Schubert  * - ctype
79833a9b234SCy Schubert  *      The conn_type of this socket.
79933a9b234SCy Schubert  */
80033a9b234SCy Schubert static krb5_error_code
setup_socket(struct bind_address * ba,struct sockaddr * sock_address,struct sockact_list * sockacts,void * handle,const char * prog,verto_ctx * ctx,int listen_backlog,verto_callback vcb,enum conn_type ctype)801b0e4d68dSCy Schubert setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
802d82a140dSCy Schubert              struct sockact_list *sockacts, void *handle, const char *prog,
803d82a140dSCy Schubert              verto_ctx *ctx, int listen_backlog, verto_callback vcb,
804d82a140dSCy Schubert              enum conn_type ctype)
80533a9b234SCy Schubert {
80633a9b234SCy Schubert     krb5_error_code ret;
80733a9b234SCy Schubert     struct connection *conn;
808b0e4d68dSCy Schubert     verto_ev_flag flags;
80933a9b234SCy Schubert     verto_ev *ev = NULL;
81033a9b234SCy Schubert     int sock = -1;
811d82a140dSCy Schubert     char addrbuf[128];
81233a9b234SCy Schubert 
813d82a140dSCy Schubert     k5_print_addr_port(sock_address, addrbuf, sizeof(addrbuf));
81433a9b234SCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"),
815d82a140dSCy Schubert                      bind_type_names[ba->type], addrbuf);
81633a9b234SCy Schubert 
817d82a140dSCy Schubert     if (sockacts->nsockets > 0) {
818d82a140dSCy Schubert         /* Look for a systemd socket activation fd matching sock_address. */
819d82a140dSCy Schubert         sock = find_sockact(sockacts, sock_address, bind_socktypes[ba->type]);
820d82a140dSCy Schubert         if (sock == -1) {
821d82a140dSCy Schubert             /* Ignore configured addresses that don't match any caller-provided
822d82a140dSCy Schubert              * sockets. */
823d82a140dSCy Schubert             ret = 0;
824d82a140dSCy Schubert             goto cleanup;
825d82a140dSCy Schubert         }
826d82a140dSCy Schubert     } else {
827d82a140dSCy Schubert         /* We're not using socket activation; create the socket. */
828d82a140dSCy Schubert         ret = create_server_socket(sock_address, bind_socktypes[ba->type],
829d82a140dSCy Schubert                                    prog, &sock);
830b0e4d68dSCy Schubert         if (ret)
83133a9b234SCy Schubert             goto cleanup;
83233a9b234SCy Schubert 
833d82a140dSCy Schubert         /* Listen for backlogged connections on stream sockets.  (For RPC
834d82a140dSCy Schubert          * sockets this will be done by svc_register().) */
835d82a140dSCy Schubert         if ((ba->type == TCP || ba->type == UNX) &&
836d82a140dSCy Schubert             listen(sock, listen_backlog) != 0) {
83733a9b234SCy Schubert             ret = errno;
838b0e4d68dSCy Schubert             com_err(prog, errno, _("Cannot listen on %s server socket on %s"),
839d82a140dSCy Schubert                     bind_type_names[ba->type], addrbuf);
84033a9b234SCy Schubert             goto cleanup;
84133a9b234SCy Schubert         }
842d82a140dSCy Schubert     }
84333a9b234SCy Schubert 
844d82a140dSCy Schubert     /* Set non-blocking I/O for non-RPC listener sockets. */
84533a9b234SCy Schubert     if (ba->type != RPC && setnbio(sock) != 0) {
84633a9b234SCy Schubert         ret = errno;
847b0e4d68dSCy Schubert         com_err(prog, errno,
84833a9b234SCy Schubert                 _("cannot set listening %s socket on %s non-blocking"),
849d82a140dSCy Schubert                 bind_type_names[ba->type], addrbuf);
85033a9b234SCy Schubert         goto cleanup;
85133a9b234SCy Schubert     }
85233a9b234SCy Schubert 
85333a9b234SCy Schubert     /* Turn off the linger option for TCP sockets. */
85433a9b234SCy Schubert     if (ba->type == TCP && setnolinger(sock) != 0) {
85533a9b234SCy Schubert         ret = errno;
856b0e4d68dSCy Schubert         com_err(prog, errno, _("cannot set SO_LINGER on %s socket on %s"),
857d82a140dSCy Schubert                 bind_type_names[ba->type], addrbuf);
85833a9b234SCy Schubert         goto cleanup;
85933a9b234SCy Schubert     }
86033a9b234SCy Schubert 
86133a9b234SCy Schubert     /* Try to turn on pktinfo for UDP wildcard sockets. */
862b0e4d68dSCy Schubert     if (ba->type == UDP && sa_is_wildcard(sock_address)) {
86333a9b234SCy Schubert         krb5_klog_syslog(LOG_DEBUG, _("Setting pktinfo on socket %s"),
864d82a140dSCy Schubert                          addrbuf);
86533a9b234SCy Schubert         ret = set_pktinfo(sock, sock_address->sa_family);
86633a9b234SCy Schubert         if (ret) {
867b0e4d68dSCy Schubert             com_err(prog, ret,
86833a9b234SCy Schubert                     _("Cannot request packet info for UDP socket address "
869d82a140dSCy Schubert                       "%s port %d"), addrbuf, ba->port);
87033a9b234SCy Schubert             krb5_klog_syslog(LOG_INFO, _("System does not support pktinfo yet "
87133a9b234SCy Schubert                                          "binding to a wildcard address.  "
87233a9b234SCy Schubert                                          "Packets are not guaranteed to "
87333a9b234SCy Schubert                                          "return on the received address."));
87433a9b234SCy Schubert         }
87533a9b234SCy Schubert     }
87633a9b234SCy Schubert 
87733a9b234SCy Schubert     /* Add the socket to the event loop. */
878b0e4d68dSCy Schubert     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST |
879b0e4d68dSCy Schubert         VERTO_EV_FLAG_REINITIABLE;
880b0e4d68dSCy Schubert     ret = add_fd(sock, ctype, flags, handle, prog, ctx, vcb, &ev);
881b0e4d68dSCy Schubert     if (ret) {
88233a9b234SCy Schubert         krb5_klog_syslog(LOG_ERR, _("Error attempting to add verto event"));
88333a9b234SCy Schubert         goto cleanup;
88433a9b234SCy Schubert     }
88533a9b234SCy Schubert 
88633a9b234SCy Schubert     if (ba->type == RPC) {
88733a9b234SCy Schubert         conn = verto_get_private(ev);
88833a9b234SCy Schubert         conn->transp = svctcp_create(sock, 0, 0);
88933a9b234SCy Schubert         if (conn->transp == NULL) {
89033a9b234SCy Schubert             ret = errno;
89133a9b234SCy Schubert             krb5_klog_syslog(LOG_ERR, _("Cannot create RPC service: %s"),
89233a9b234SCy Schubert                              strerror(ret));
89333a9b234SCy Schubert             goto cleanup;
89433a9b234SCy Schubert         }
89533a9b234SCy Schubert 
89633a9b234SCy Schubert         ret = svc_register(conn->transp, ba->rpc_svc_data.prognum,
89733a9b234SCy Schubert                            ba->rpc_svc_data.versnum, ba->rpc_svc_data.dispatch,
89833a9b234SCy Schubert                            0);
89933a9b234SCy Schubert         if (!ret) {
90033a9b234SCy Schubert             ret = errno;
90133a9b234SCy Schubert             krb5_klog_syslog(LOG_ERR, _("Cannot register RPC service: %s"),
90233a9b234SCy Schubert                              strerror(ret));
90333a9b234SCy Schubert             goto cleanup;
90433a9b234SCy Schubert         }
90533a9b234SCy Schubert     }
90633a9b234SCy Schubert 
90733a9b234SCy Schubert     ev = NULL;
90833a9b234SCy Schubert     sock = -1;
90933a9b234SCy Schubert     ret = 0;
91033a9b234SCy Schubert 
91133a9b234SCy Schubert cleanup:
91233a9b234SCy Schubert     if (sock >= 0)
91333a9b234SCy Schubert         close(sock);
91433a9b234SCy Schubert     if (ev != NULL)
91533a9b234SCy Schubert         verto_del(ev);
91633a9b234SCy Schubert     return ret;
91733a9b234SCy Schubert }
91833a9b234SCy Schubert 
91933a9b234SCy Schubert /*
92033a9b234SCy Schubert  * Setup all the socket addresses that the net-server should listen to.
92133a9b234SCy Schubert  *
92233a9b234SCy Schubert  * This function uses getaddrinfo to figure out all the addresses. This will
92333a9b234SCy Schubert  * automatically figure out which socket families that should be used on the
92433a9b234SCy Schubert  * host making it useful even for wildcard addresses.
92533a9b234SCy Schubert  */
92633a9b234SCy Schubert static krb5_error_code
setup_addresses(verto_ctx * ctx,void * handle,const char * prog,int listen_backlog)927b0e4d68dSCy Schubert setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
928d82a140dSCy Schubert                 int listen_backlog)
92933a9b234SCy Schubert {
93033a9b234SCy Schubert     /* An bind_type enum map for the verto callback functions. */
93133a9b234SCy Schubert     static verto_callback *const verto_callbacks[] = {
93233a9b234SCy Schubert         [UDP] = &process_packet,
933d82a140dSCy Schubert         [TCP] = &accept_stream_connection,
934d82a140dSCy Schubert         [RPC] = &accept_rpc_connection,
935d82a140dSCy Schubert         [UNX] = &accept_stream_connection
93633a9b234SCy Schubert     };
93733a9b234SCy Schubert     krb5_error_code ret = 0;
93833a9b234SCy Schubert     size_t i;
93933a9b234SCy Schubert     int err, bound_any;
94033a9b234SCy Schubert     struct bind_address addr;
941d82a140dSCy Schubert     struct sockaddr_un sun;
94233a9b234SCy Schubert     struct addrinfo hints, *ai_list = NULL, *ai = NULL;
943d82a140dSCy Schubert     struct sockact_list sockacts = { 0 };
94433a9b234SCy Schubert     verto_callback vcb;
945d82a140dSCy Schubert     char addrbuf[128];
94633a9b234SCy Schubert 
94733a9b234SCy Schubert     /* Check to make sure addresses were added to the server. */
94833a9b234SCy Schubert     if (bind_addresses.n == 0) {
94933a9b234SCy Schubert         krb5_klog_syslog(LOG_ERR, _("No addresses added to the net server"));
95033a9b234SCy Schubert         return EINVAL;
95133a9b234SCy Schubert     }
95233a9b234SCy Schubert 
95333a9b234SCy Schubert     /* Ask for all address families, listener addresses, and no port name
95433a9b234SCy Schubert      * resolution. */
95533a9b234SCy Schubert     memset(&hints, 0, sizeof(struct addrinfo));
95633a9b234SCy Schubert     hints.ai_family = AF_UNSPEC;
9570320e0d5SCy Schubert     hints.ai_flags = AI_PASSIVE;
9580320e0d5SCy Schubert #ifdef AI_NUMERICSERV
9590320e0d5SCy Schubert     hints.ai_flags |= AI_NUMERICSERV;
9600320e0d5SCy Schubert #endif
96133a9b234SCy Schubert 
962d82a140dSCy Schubert     init_sockact_list(&sockacts);
963d82a140dSCy Schubert 
96433a9b234SCy Schubert     /* Add all the requested addresses. */
96533a9b234SCy Schubert     for (i = 0; i < bind_addresses.n; i++) {
96633a9b234SCy Schubert         addr = bind_addresses.data[i];
96733a9b234SCy Schubert         hints.ai_socktype = bind_socktypes[addr.type];
96833a9b234SCy Schubert 
969d82a140dSCy Schubert         if (addr.type == UNX) {
970d82a140dSCy Schubert             sun.sun_family = AF_UNIX;
971d82a140dSCy Schubert             if (strlcpy(sun.sun_path, addr.address, sizeof(sun.sun_path)) >=
972d82a140dSCy Schubert                 sizeof(sun.sun_path)) {
973d82a140dSCy Schubert                 ret = ENAMETOOLONG;
974d82a140dSCy Schubert                 krb5_klog_syslog(LOG_ERR,
975d82a140dSCy Schubert                                  _("UNIX domain socket path too long: %s"),
976d82a140dSCy Schubert                                  addr.address);
977d82a140dSCy Schubert                 goto cleanup;
978d82a140dSCy Schubert             }
979d82a140dSCy Schubert             ret = setup_socket(&addr, (struct sockaddr *)&sun, &sockacts,
980d82a140dSCy Schubert                                handle, prog, ctx, listen_backlog,
981d82a140dSCy Schubert                                verto_callbacks[addr.type],
982d82a140dSCy Schubert                                bind_conn_types[addr.type]);
983d82a140dSCy Schubert             if (ret) {
984d82a140dSCy Schubert                 krb5_klog_syslog(LOG_ERR,
985d82a140dSCy Schubert                                  _("Failed setting up a UNIX socket (for %s)"),
986d82a140dSCy Schubert                                  addr.address);
987d82a140dSCy Schubert                 goto cleanup;
988d82a140dSCy Schubert             }
989d82a140dSCy Schubert             continue;
990d82a140dSCy Schubert         }
991d82a140dSCy Schubert 
99233a9b234SCy Schubert         /* Call getaddrinfo, using a dummy port value. */
99333a9b234SCy Schubert         err = getaddrinfo(addr.address, "0", &hints, &ai_list);
99433a9b234SCy Schubert         if (err) {
99533a9b234SCy Schubert             krb5_klog_syslog(LOG_ERR,
99633a9b234SCy Schubert                              _("Failed getting address info (for %s): %s"),
99733a9b234SCy Schubert                              (addr.address == NULL) ? "<wildcard>" :
99833a9b234SCy Schubert                              addr.address, gai_strerror(err));
99933a9b234SCy Schubert             ret = EIO;
100033a9b234SCy Schubert             goto cleanup;
100133a9b234SCy Schubert         }
100233a9b234SCy Schubert 
100333a9b234SCy Schubert         /*
100433a9b234SCy Schubert          * Loop through all the sockets that getaddrinfo could find to match
100533a9b234SCy Schubert          * the requested address.  For wildcard listeners, this should usually
100633a9b234SCy Schubert          * have two results, one for each of IPv4 and IPv6, or one or the
100733a9b234SCy Schubert          * other, depending on the system.  On IPv4-only systems, getaddrinfo()
100833a9b234SCy Schubert          * may return both IPv4 and IPv6 addresses, but creating an IPv6 socket
100933a9b234SCy Schubert          * may give an EAFNOSUPPORT error, so tolerate that error as long as we
101033a9b234SCy Schubert          * can bind at least one socket.
101133a9b234SCy Schubert          */
101233a9b234SCy Schubert         bound_any = 0;
101333a9b234SCy Schubert         for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
101433a9b234SCy Schubert             /* Make sure getaddrinfo returned a socket with the same type that
101533a9b234SCy Schubert              * was requested. */
101633a9b234SCy Schubert             assert(hints.ai_socktype == ai->ai_socktype);
101733a9b234SCy Schubert 
101833a9b234SCy Schubert             /* Set the real port number. */
101933a9b234SCy Schubert             sa_setport(ai->ai_addr, addr.port);
102033a9b234SCy Schubert 
1021d82a140dSCy Schubert             ret = setup_socket(&addr, ai->ai_addr, &sockacts, handle, prog,
1022d82a140dSCy Schubert                                ctx, listen_backlog, verto_callbacks[addr.type],
102333a9b234SCy Schubert                                bind_conn_types[addr.type]);
102433a9b234SCy Schubert             if (ret) {
1025d82a140dSCy Schubert                 k5_print_addr(ai->ai_addr, addrbuf, sizeof(addrbuf));
102633a9b234SCy Schubert                 krb5_klog_syslog(LOG_ERR,
102733a9b234SCy Schubert                                  _("Failed setting up a %s socket (for %s)"),
1028d82a140dSCy Schubert                                  bind_type_names[addr.type], addrbuf);
102933a9b234SCy Schubert                 if (ret != EAFNOSUPPORT)
103033a9b234SCy Schubert                     goto cleanup;
103133a9b234SCy Schubert             } else {
103233a9b234SCy Schubert                 bound_any = 1;
103333a9b234SCy Schubert             }
103433a9b234SCy Schubert         }
103533a9b234SCy Schubert         if (!bound_any)
103633a9b234SCy Schubert             goto cleanup;
103733a9b234SCy Schubert         ret = 0;
103833a9b234SCy Schubert 
103933a9b234SCy Schubert         if (ai_list != NULL)
104033a9b234SCy Schubert             freeaddrinfo(ai_list);
104133a9b234SCy Schubert         ai_list = NULL;
104233a9b234SCy Schubert     }
104333a9b234SCy Schubert 
104433a9b234SCy Schubert cleanup:
104533a9b234SCy Schubert     if (ai_list != NULL)
104633a9b234SCy Schubert         freeaddrinfo(ai_list);
1047d82a140dSCy Schubert     fini_sockact_list(&sockacts);
104833a9b234SCy Schubert     return ret;
104933a9b234SCy Schubert }
105033a9b234SCy Schubert 
105133a9b234SCy Schubert krb5_error_code
loop_setup_network(verto_ctx * ctx,void * handle,const char * prog,int listen_backlog)105233a9b234SCy Schubert loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,
1053d82a140dSCy Schubert                    int listen_backlog)
105433a9b234SCy Schubert {
1055b0e4d68dSCy Schubert     krb5_error_code ret;
105633a9b234SCy Schubert     verto_ev *ev;
1057b0e4d68dSCy Schubert     int i;
105833a9b234SCy Schubert 
105933a9b234SCy Schubert     /* Check to make sure that at least one address was added to the loop. */
106033a9b234SCy Schubert     if (bind_addresses.n == 0)
106133a9b234SCy Schubert         return EINVAL;
106233a9b234SCy Schubert 
106333a9b234SCy Schubert     /* Close any open connections. */
106433a9b234SCy Schubert     FOREACH_ELT(events, i, ev)
106533a9b234SCy Schubert         verto_del(ev);
106633a9b234SCy Schubert     events.n = 0;
106733a9b234SCy Schubert 
106833a9b234SCy Schubert     krb5_klog_syslog(LOG_INFO, _("setting up network..."));
1069d82a140dSCy Schubert     ret = setup_addresses(ctx, handle, prog, listen_backlog);
1070b0e4d68dSCy Schubert     if (ret) {
107133a9b234SCy Schubert         com_err(prog, ret, _("Error setting up network"));
107233a9b234SCy Schubert         exit(1);
107333a9b234SCy Schubert     }
107433a9b234SCy Schubert     krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);
107533a9b234SCy Schubert     if (events.n == 0) {
107633a9b234SCy Schubert         /* If no sockets were set up, we can't continue. */
107733a9b234SCy Schubert         com_err(prog, 0, _("no sockets set up?"));
107833a9b234SCy Schubert         exit (1);
107933a9b234SCy Schubert     }
108033a9b234SCy Schubert 
108133a9b234SCy Schubert     return 0;
108233a9b234SCy Schubert }
108333a9b234SCy Schubert 
108433a9b234SCy Schubert struct udp_dispatch_state {
108533a9b234SCy Schubert     void *handle;
108633a9b234SCy Schubert     const char *prog;
108733a9b234SCy Schubert     int port_fd;
108833a9b234SCy Schubert     struct sockaddr_storage saddr;
108933a9b234SCy Schubert     struct sockaddr_storage daddr;
109033a9b234SCy Schubert     aux_addressing_info auxaddr;
109133a9b234SCy Schubert     krb5_data request;
109233a9b234SCy Schubert     char pktbuf[MAX_DGRAM_SIZE];
109333a9b234SCy Schubert };
109433a9b234SCy Schubert 
109533a9b234SCy Schubert static void
process_packet_response(void * arg,krb5_error_code code,krb5_data * response)109633a9b234SCy Schubert process_packet_response(void *arg, krb5_error_code code, krb5_data *response)
109733a9b234SCy Schubert {
109833a9b234SCy Schubert     struct udp_dispatch_state *state = arg;
109933a9b234SCy Schubert     int cc;
110033a9b234SCy Schubert 
110133a9b234SCy Schubert     if (code)
110233a9b234SCy Schubert         com_err(state->prog ? state->prog : NULL, code,
110333a9b234SCy Schubert                 _("while dispatching (udp)"));
110433a9b234SCy Schubert     if (code || response == NULL)
110533a9b234SCy Schubert         goto out;
110633a9b234SCy Schubert 
110733a9b234SCy Schubert     cc = send_to_from(state->port_fd, response->data,
1108d82a140dSCy Schubert                       (socklen_t)response->length, 0, ss2sa(&state->saddr),
1109d82a140dSCy Schubert                       ss2sa(&state->daddr), &state->auxaddr);
111033a9b234SCy Schubert     if (cc == -1) {
111133a9b234SCy Schubert         /* Note that the local address (daddr*) has no port number
111233a9b234SCy Schubert          * info associated with it. */
1113d82a140dSCy Schubert         char sbuf[128], dbuf[128];
111433a9b234SCy Schubert         int e = errno;
111533a9b234SCy Schubert 
1116d82a140dSCy Schubert         k5_print_addr_port(ss2sa(&state->saddr), sbuf, sizeof(sbuf));
1117d82a140dSCy Schubert         k5_print_addr(ss2sa(&state->daddr), dbuf, sizeof(dbuf));
1118d82a140dSCy Schubert         com_err(state->prog, e, _("while sending reply to %s from %s"),
1119d82a140dSCy Schubert                 sbuf, dbuf);
112033a9b234SCy Schubert         goto out;
112133a9b234SCy Schubert     }
112233a9b234SCy Schubert     if ((size_t)cc != response->length) {
112333a9b234SCy Schubert         com_err(state->prog, 0, _("short reply write %d vs %d\n"),
112433a9b234SCy Schubert                 response->length, cc);
112533a9b234SCy Schubert     }
112633a9b234SCy Schubert 
112733a9b234SCy Schubert out:
112833a9b234SCy Schubert     krb5_free_data(get_context(state->handle), response);
112933a9b234SCy Schubert     free(state);
113033a9b234SCy Schubert }
113133a9b234SCy Schubert 
113233a9b234SCy Schubert static void
process_packet(verto_ctx * ctx,verto_ev * ev)113333a9b234SCy Schubert process_packet(verto_ctx *ctx, verto_ev *ev)
113433a9b234SCy Schubert {
113533a9b234SCy Schubert     int cc;
113633a9b234SCy Schubert     struct connection *conn;
113733a9b234SCy Schubert     struct udp_dispatch_state *state;
1138d82a140dSCy Schubert     socklen_t slen;
113933a9b234SCy Schubert 
114033a9b234SCy Schubert     conn = verto_get_private(ev);
114133a9b234SCy Schubert 
114233a9b234SCy Schubert     state = malloc(sizeof(*state));
114333a9b234SCy Schubert     if (!state) {
114433a9b234SCy Schubert         com_err(conn->prog, ENOMEM, _("while dispatching (udp)"));
114533a9b234SCy Schubert         return;
114633a9b234SCy Schubert     }
114733a9b234SCy Schubert 
114833a9b234SCy Schubert     state->handle = conn->handle;
114933a9b234SCy Schubert     state->prog = conn->prog;
115033a9b234SCy Schubert     state->port_fd = verto_get_fd(ev);
115133a9b234SCy Schubert     assert(state->port_fd >= 0);
115233a9b234SCy Schubert 
115333a9b234SCy Schubert     memset(&state->auxaddr, 0, sizeof(state->auxaddr));
115433a9b234SCy Schubert     cc = recv_from_to(state->port_fd, state->pktbuf, sizeof(state->pktbuf), 0,
1155d82a140dSCy Schubert                       &state->saddr, &state->daddr, &state->auxaddr);
115633a9b234SCy Schubert     if (cc == -1) {
115733a9b234SCy Schubert         if (errno != EINTR && errno != EAGAIN
115833a9b234SCy Schubert             /*
115933a9b234SCy Schubert              * This is how Linux indicates that a previous transmission was
116033a9b234SCy Schubert              * refused, e.g., if the client timed out before getting the
116133a9b234SCy Schubert              * response packet.
116233a9b234SCy Schubert              */
116333a9b234SCy Schubert             && errno != ECONNREFUSED
116433a9b234SCy Schubert         )
116533a9b234SCy Schubert             com_err(conn->prog, errno, _("while receiving from network"));
116633a9b234SCy Schubert         free(state);
116733a9b234SCy Schubert         return;
116833a9b234SCy Schubert     }
116933a9b234SCy Schubert     if (!cc) { /* zero-length packet? */
117033a9b234SCy Schubert         free(state);
117133a9b234SCy Schubert         return;
117233a9b234SCy Schubert     }
117333a9b234SCy Schubert 
1174d82a140dSCy Schubert     if (state->daddr.ss_family == AF_UNSPEC && conn->type == CONN_UDP) {
117533a9b234SCy Schubert         /*
117633a9b234SCy Schubert          * An address couldn't be obtained, so the PKTINFO option probably
117733a9b234SCy Schubert          * isn't available.  If the socket is bound to a specific address, then
117833a9b234SCy Schubert          * try to get the address here.
117933a9b234SCy Schubert          */
1180d82a140dSCy Schubert         slen = sizeof(state->daddr);
1181d82a140dSCy Schubert         (void)getsockname(state->port_fd, ss2sa(&state->daddr), &slen);
118233a9b234SCy Schubert     }
118333a9b234SCy Schubert 
118433a9b234SCy Schubert     state->request.length = cc;
118533a9b234SCy Schubert     state->request.data = state->pktbuf;
1186b0e4d68dSCy Schubert 
1187d82a140dSCy Schubert     dispatch(state->handle, ss2sa(&state->daddr), ss2sa(&state->saddr),
118833a9b234SCy Schubert              &state->request, 0, ctx, process_packet_response, state);
118933a9b234SCy Schubert }
119033a9b234SCy Schubert 
119133a9b234SCy Schubert static int
kill_lru_stream_connection(void * handle,verto_ev * newev)1192d82a140dSCy Schubert kill_lru_stream_connection(void *handle, verto_ev *newev)
119333a9b234SCy Schubert {
119433a9b234SCy Schubert     struct connection *c = NULL, *oldest_c = NULL;
119533a9b234SCy Schubert     verto_ev *ev, *oldest_ev = NULL;
119633a9b234SCy Schubert     int i, fd = -1;
119733a9b234SCy Schubert 
119833a9b234SCy Schubert     krb5_klog_syslog(LOG_INFO, _("too many connections"));
119933a9b234SCy Schubert 
120033a9b234SCy Schubert     FOREACH_ELT (events, i, ev) {
120133a9b234SCy Schubert         if (ev == newev)
120233a9b234SCy Schubert             continue;
120333a9b234SCy Schubert 
120433a9b234SCy Schubert         c = verto_get_private(ev);
120533a9b234SCy Schubert         if (!c)
120633a9b234SCy Schubert             continue;
1207d82a140dSCy Schubert         if (c->type != CONN_TCP && c->type != CONN_RPC &&
1208d82a140dSCy Schubert             c->type != CONN_UNIXSOCK)
120933a9b234SCy Schubert             continue;
121033a9b234SCy Schubert         if (oldest_c == NULL
121133a9b234SCy Schubert             || oldest_c->start_time > c->start_time) {
121233a9b234SCy Schubert             oldest_ev = ev;
121333a9b234SCy Schubert             oldest_c = c;
121433a9b234SCy Schubert         }
121533a9b234SCy Schubert     }
121633a9b234SCy Schubert     if (oldest_c != NULL) {
121733a9b234SCy Schubert         krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
1218d82a140dSCy Schubert                          conn_type_names[oldest_c->type],
121933a9b234SCy Schubert                          verto_get_fd(oldest_ev), oldest_c->addrbuf);
122033a9b234SCy Schubert         if (oldest_c->type == CONN_RPC)
122133a9b234SCy Schubert             oldest_c->rpc_force_close = 1;
122233a9b234SCy Schubert         verto_del(oldest_ev);
122333a9b234SCy Schubert     }
122433a9b234SCy Schubert     return fd;
122533a9b234SCy Schubert }
122633a9b234SCy Schubert 
122733a9b234SCy Schubert static void
accept_stream_connection(verto_ctx * ctx,verto_ev * ev)1228d82a140dSCy Schubert accept_stream_connection(verto_ctx *ctx, verto_ev *ev)
122933a9b234SCy Schubert {
123033a9b234SCy Schubert     int s;
1231d82a140dSCy Schubert     struct sockaddr_storage addr;
1232d82a140dSCy Schubert     socklen_t addrlen = sizeof(addr);
123333a9b234SCy Schubert     struct connection *newconn, *conn;
1234d82a140dSCy Schubert     enum conn_type ctype;
1235b0e4d68dSCy Schubert     verto_ev_flag flags;
123633a9b234SCy Schubert     verto_ev *newev;
123733a9b234SCy Schubert 
123833a9b234SCy Schubert     conn = verto_get_private(ev);
1239d82a140dSCy Schubert     s = accept(verto_get_fd(ev), ss2sa(&addr), &addrlen);
124033a9b234SCy Schubert     if (s < 0)
124133a9b234SCy Schubert         return;
124233a9b234SCy Schubert     set_cloexec_fd(s);
124333a9b234SCy Schubert #ifndef _WIN32
124433a9b234SCy Schubert     if (s >= FD_SETSIZE) {
124533a9b234SCy Schubert         close(s);
124633a9b234SCy Schubert         return;
124733a9b234SCy Schubert     }
124833a9b234SCy Schubert #endif
1249d82a140dSCy Schubert     setnbio(s);
1250d82a140dSCy Schubert     setnolinger(s);
1251d82a140dSCy Schubert     if (addr.ss_family != AF_UNIX)
1252d82a140dSCy Schubert         setkeepalive(s);
125333a9b234SCy Schubert 
1254b0e4d68dSCy Schubert     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1255d82a140dSCy Schubert     ctype = (conn->type == CONN_TCP_LISTENER) ? CONN_TCP : CONN_UNIXSOCK;
1256d82a140dSCy Schubert     if (add_fd(s, ctype, flags, conn->handle, conn->prog, ctx,
1257d82a140dSCy Schubert                process_stream_connection_read, &newev) != 0) {
125833a9b234SCy Schubert         close(s);
125933a9b234SCy Schubert         return;
126033a9b234SCy Schubert     }
126133a9b234SCy Schubert     newconn = verto_get_private(newev);
126233a9b234SCy Schubert 
1263d82a140dSCy Schubert     if (addr.ss_family == AF_UNIX) {
1264d82a140dSCy Schubert         /* accept() doesn't fill in sun_path as the client socket isn't bound.
1265d82a140dSCy Schubert          * For logging purposes we will use the target address. */
1266d82a140dSCy Schubert         addrlen = sizeof(addr);
1267d82a140dSCy Schubert         if (getsockname(s, ss2sa(&addr), &addrlen) < 0) {
1268d82a140dSCy Schubert             com_err(conn->prog, errno, _("Failed to get address for %d"), s);
1269d82a140dSCy Schubert             close(s);
1270d82a140dSCy Schubert             return;
127133a9b234SCy Schubert         }
127233a9b234SCy Schubert     }
127333a9b234SCy Schubert 
1274d82a140dSCy Schubert     k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,
1275d82a140dSCy Schubert                        sizeof(newconn->addrbuf));
1276d82a140dSCy Schubert     newconn->addr_s = addr;
127733a9b234SCy Schubert     newconn->addrlen = addrlen;
127833a9b234SCy Schubert     newconn->bufsiz = 1024 * 1024;
127933a9b234SCy Schubert     newconn->buffer = malloc(newconn->bufsiz);
128033a9b234SCy Schubert     newconn->start_time = time(0);
128133a9b234SCy Schubert 
1282d82a140dSCy Schubert     if (++stream_data_counter > max_stream_data_connections)
1283d82a140dSCy Schubert         kill_lru_stream_connection(conn->handle, newev);
128433a9b234SCy Schubert 
128533a9b234SCy Schubert     if (newconn->buffer == 0) {
128633a9b234SCy Schubert         com_err(conn->prog, errno,
128733a9b234SCy Schubert                 _("allocating buffer for new TCP session from %s"),
128833a9b234SCy Schubert                 newconn->addrbuf);
128933a9b234SCy Schubert         verto_del(newev);
129033a9b234SCy Schubert         return;
129133a9b234SCy Schubert     }
129233a9b234SCy Schubert     newconn->offset = 0;
129333a9b234SCy Schubert     SG_SET(&newconn->sgbuf[0], newconn->lenbuf, 4);
129433a9b234SCy Schubert     SG_SET(&newconn->sgbuf[1], 0, 0);
129533a9b234SCy Schubert }
129633a9b234SCy Schubert 
129733a9b234SCy Schubert struct tcp_dispatch_state {
129833a9b234SCy Schubert     struct sockaddr_storage local_saddr;
129933a9b234SCy Schubert     struct connection *conn;
130033a9b234SCy Schubert     krb5_data request;
130133a9b234SCy Schubert     verto_ctx *ctx;
130233a9b234SCy Schubert     int sock;
130333a9b234SCy Schubert };
130433a9b234SCy Schubert 
130533a9b234SCy Schubert static void
process_stream_response(void * arg,krb5_error_code code,krb5_data * response)1306d82a140dSCy Schubert process_stream_response(void *arg, krb5_error_code code, krb5_data *response)
130733a9b234SCy Schubert {
130833a9b234SCy Schubert     struct tcp_dispatch_state *state = arg;
130933a9b234SCy Schubert     verto_ev *ev;
131033a9b234SCy Schubert 
131133a9b234SCy Schubert     assert(state);
131233a9b234SCy Schubert     state->conn->response = response;
131333a9b234SCy Schubert 
131433a9b234SCy Schubert     if (code)
131533a9b234SCy Schubert         com_err(state->conn->prog, code, _("while dispatching (tcp)"));
131633a9b234SCy Schubert     if (code || !response)
131733a9b234SCy Schubert         goto kill_tcp_connection;
131833a9b234SCy Schubert 
131933a9b234SCy Schubert     /* Queue outgoing response. */
132033a9b234SCy Schubert     store_32_be(response->length, state->conn->lenbuf);
132133a9b234SCy Schubert     SG_SET(&state->conn->sgbuf[1], response->data, response->length);
132233a9b234SCy Schubert     state->conn->sgp = state->conn->sgbuf;
132333a9b234SCy Schubert     state->conn->sgnum = 2;
132433a9b234SCy Schubert 
132533a9b234SCy Schubert     ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,
1326d82a140dSCy Schubert                     process_stream_connection_write, state->sock, state->conn);
132733a9b234SCy Schubert     if (ev) {
132833a9b234SCy Schubert         free(state);
132933a9b234SCy Schubert         return;
133033a9b234SCy Schubert     }
133133a9b234SCy Schubert 
133233a9b234SCy Schubert kill_tcp_connection:
1333d82a140dSCy Schubert     stream_data_counter--;
133433a9b234SCy Schubert     free_connection(state->conn);
133533a9b234SCy Schubert     close(state->sock);
133633a9b234SCy Schubert     free(state);
133733a9b234SCy Schubert }
133833a9b234SCy Schubert 
133933a9b234SCy Schubert /* Creates the tcp_dispatch_state and deletes the verto event. */
134033a9b234SCy Schubert static struct tcp_dispatch_state *
prepare_for_dispatch(verto_ctx * ctx,verto_ev * ev)134133a9b234SCy Schubert prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)
134233a9b234SCy Schubert {
134333a9b234SCy Schubert     struct tcp_dispatch_state *state;
134433a9b234SCy Schubert 
134533a9b234SCy Schubert     state = malloc(sizeof(*state));
134633a9b234SCy Schubert     if (!state) {
134733a9b234SCy Schubert         krb5_klog_syslog(LOG_ERR, _("error allocating tcp dispatch private!"));
134833a9b234SCy Schubert         return NULL;
134933a9b234SCy Schubert     }
135033a9b234SCy Schubert     state->conn = verto_get_private(ev);
135133a9b234SCy Schubert     state->sock = verto_get_fd(ev);
135233a9b234SCy Schubert     state->ctx = ctx;
135333a9b234SCy Schubert     verto_set_private(ev, NULL, NULL); /* Don't close the fd or free conn! */
135433a9b234SCy Schubert     remove_event_from_set(ev); /* Remove it from the set. */
135533a9b234SCy Schubert     verto_del(ev);
135633a9b234SCy Schubert     return state;
135733a9b234SCy Schubert }
135833a9b234SCy Schubert 
135933a9b234SCy Schubert static void
process_stream_connection_read(verto_ctx * ctx,verto_ev * ev)1360d82a140dSCy Schubert process_stream_connection_read(verto_ctx *ctx, verto_ev *ev)
136133a9b234SCy Schubert {
136233a9b234SCy Schubert     struct tcp_dispatch_state *state = NULL;
136333a9b234SCy Schubert     struct connection *conn = NULL;
136433a9b234SCy Schubert     ssize_t nread;
136533a9b234SCy Schubert     size_t len;
136633a9b234SCy Schubert 
136733a9b234SCy Schubert     conn = verto_get_private(ev);
136833a9b234SCy Schubert 
136933a9b234SCy Schubert     /*
137033a9b234SCy Schubert      * Read message length and data into one big buffer, already allocated
137133a9b234SCy Schubert      * at connect time.  If we have a complete message, we stop reading, so
137233a9b234SCy Schubert      * we should only be here if there is no data in the buffer, or only an
137333a9b234SCy Schubert      * incomplete message.
137433a9b234SCy Schubert      */
137533a9b234SCy Schubert     if (conn->offset < 4) {
137633a9b234SCy Schubert         krb5_data *response = NULL;
137733a9b234SCy Schubert 
137833a9b234SCy Schubert         /* msglen has not been computed.  XXX Doing at least two reads
137933a9b234SCy Schubert          * here, letting the kernel worry about buffering. */
138033a9b234SCy Schubert         len = 4 - conn->offset;
138133a9b234SCy Schubert         nread = SOCKET_READ(verto_get_fd(ev),
138233a9b234SCy Schubert                             conn->buffer + conn->offset, len);
138333a9b234SCy Schubert         if (nread < 0) /* error */
138433a9b234SCy Schubert             goto kill_tcp_connection;
138533a9b234SCy Schubert         if (nread == 0) /* eof */
138633a9b234SCy Schubert             goto kill_tcp_connection;
138733a9b234SCy Schubert         conn->offset += nread;
138833a9b234SCy Schubert         if (conn->offset == 4) {
138933a9b234SCy Schubert             unsigned char *p = (unsigned char *)conn->buffer;
139033a9b234SCy Schubert             conn->msglen = load_32_be(p);
139133a9b234SCy Schubert             if (conn->msglen > conn->bufsiz - 4) {
139233a9b234SCy Schubert                 krb5_error_code err;
139333a9b234SCy Schubert                 /* Message too big. */
139433a9b234SCy Schubert                 krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, "
139533a9b234SCy Schubert                                             "cap is %lu"), conn->addrbuf,
139633a9b234SCy Schubert                                  (unsigned long) conn->msglen,
139733a9b234SCy Schubert                                  (unsigned long) conn->bufsiz - 4);
139833a9b234SCy Schubert                 /* XXX Should return an error.  */
139933a9b234SCy Schubert                 err = make_toolong_error (conn->handle,
140033a9b234SCy Schubert                                           &response);
140133a9b234SCy Schubert                 if (err) {
140233a9b234SCy Schubert                     krb5_klog_syslog(LOG_ERR, _("error constructing "
140333a9b234SCy Schubert                                                 "KRB_ERR_FIELD_TOOLONG error! %s"),
140433a9b234SCy Schubert                                      error_message(err));
140533a9b234SCy Schubert                     goto kill_tcp_connection;
140633a9b234SCy Schubert                 }
140733a9b234SCy Schubert 
140833a9b234SCy Schubert                 state = prepare_for_dispatch(ctx, ev);
140933a9b234SCy Schubert                 if (!state) {
141033a9b234SCy Schubert                     krb5_free_data(get_context(conn->handle), response);
141133a9b234SCy Schubert                     goto kill_tcp_connection;
141233a9b234SCy Schubert                 }
1413d82a140dSCy Schubert                 process_stream_response(state, 0, response);
141433a9b234SCy Schubert             }
141533a9b234SCy Schubert         }
141633a9b234SCy Schubert     } else {
141733a9b234SCy Schubert         /* msglen known. */
141833a9b234SCy Schubert         socklen_t local_saddrlen = sizeof(struct sockaddr_storage);
141933a9b234SCy Schubert 
142033a9b234SCy Schubert         len = conn->msglen - (conn->offset - 4);
142133a9b234SCy Schubert         nread = SOCKET_READ(verto_get_fd(ev),
142233a9b234SCy Schubert                             conn->buffer + conn->offset, len);
142333a9b234SCy Schubert         if (nread < 0) /* error */
142433a9b234SCy Schubert             goto kill_tcp_connection;
142533a9b234SCy Schubert         if (nread == 0) /* eof */
142633a9b234SCy Schubert             goto kill_tcp_connection;
142733a9b234SCy Schubert         conn->offset += nread;
142833a9b234SCy Schubert         if (conn->offset < conn->msglen + 4)
142933a9b234SCy Schubert             return;
143033a9b234SCy Schubert 
143133a9b234SCy Schubert         /* Have a complete message, and exactly one message. */
143233a9b234SCy Schubert         state = prepare_for_dispatch(ctx, ev);
143333a9b234SCy Schubert         if (!state)
143433a9b234SCy Schubert             goto kill_tcp_connection;
143533a9b234SCy Schubert 
143633a9b234SCy Schubert         state->request.length = conn->msglen;
143733a9b234SCy Schubert         state->request.data = conn->buffer + 4;
143833a9b234SCy Schubert 
143933a9b234SCy Schubert         if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr),
1440b0e4d68dSCy Schubert                         &local_saddrlen) < 0) {
1441b0e4d68dSCy Schubert             krb5_klog_syslog(LOG_ERR, _("getsockname failed: %s"),
1442b0e4d68dSCy Schubert                              error_message(errno));
1443b0e4d68dSCy Schubert             goto kill_tcp_connection;
1444b0e4d68dSCy Schubert         }
1445d82a140dSCy Schubert         dispatch(state->conn->handle, ss2sa(&state->local_saddr),
1446d82a140dSCy Schubert                  ss2sa(&conn->addr_s), &state->request, 1, ctx,
1447d82a140dSCy Schubert                  process_stream_response, state);
144833a9b234SCy Schubert     }
144933a9b234SCy Schubert 
145033a9b234SCy Schubert     return;
145133a9b234SCy Schubert 
145233a9b234SCy Schubert kill_tcp_connection:
145333a9b234SCy Schubert     verto_del(ev);
145433a9b234SCy Schubert }
145533a9b234SCy Schubert 
145633a9b234SCy Schubert static void
process_stream_connection_write(verto_ctx * ctx,verto_ev * ev)1457d82a140dSCy Schubert process_stream_connection_write(verto_ctx *ctx, verto_ev *ev)
145833a9b234SCy Schubert {
145933a9b234SCy Schubert     struct connection *conn;
146033a9b234SCy Schubert     SOCKET_WRITEV_TEMP tmp;
146133a9b234SCy Schubert     ssize_t nwrote;
146233a9b234SCy Schubert     int sock;
146333a9b234SCy Schubert 
146433a9b234SCy Schubert     conn = verto_get_private(ev);
146533a9b234SCy Schubert     sock = verto_get_fd(ev);
146633a9b234SCy Schubert 
146733a9b234SCy Schubert     nwrote = SOCKET_WRITEV(sock, conn->sgp,
146833a9b234SCy Schubert                            conn->sgnum, tmp);
146933a9b234SCy Schubert     if (nwrote > 0) { /* non-error and non-eof */
147033a9b234SCy Schubert         while (nwrote) {
147133a9b234SCy Schubert             sg_buf *sgp = conn->sgp;
147233a9b234SCy Schubert             if ((size_t)nwrote < SG_LEN(sgp)) {
147333a9b234SCy Schubert                 SG_ADVANCE(sgp, (size_t)nwrote);
147433a9b234SCy Schubert                 nwrote = 0;
147533a9b234SCy Schubert             } else {
147633a9b234SCy Schubert                 nwrote -= SG_LEN(sgp);
147733a9b234SCy Schubert                 conn->sgp++;
147833a9b234SCy Schubert                 conn->sgnum--;
147933a9b234SCy Schubert                 if (conn->sgnum == 0 && nwrote != 0)
148033a9b234SCy Schubert                     abort();
148133a9b234SCy Schubert             }
148233a9b234SCy Schubert         }
148333a9b234SCy Schubert 
148433a9b234SCy Schubert         /* If we still have more data to send, just return so that
148533a9b234SCy Schubert          * the main loop can call this function again when the socket
148633a9b234SCy Schubert          * is ready for more writing. */
148733a9b234SCy Schubert         if (conn->sgnum > 0)
148833a9b234SCy Schubert             return;
148933a9b234SCy Schubert     }
149033a9b234SCy Schubert 
149133a9b234SCy Schubert     /* Finished sending.  We should go back to reading, though if we
149233a9b234SCy Schubert      * sent a FIELD_TOOLONG error in reply to a length with the high
149333a9b234SCy Schubert      * bit set, RFC 4120 says we have to close the TCP stream. */
149433a9b234SCy Schubert     verto_del(ev);
149533a9b234SCy Schubert }
149633a9b234SCy Schubert 
149733a9b234SCy Schubert void
loop_free(verto_ctx * ctx)149833a9b234SCy Schubert loop_free(verto_ctx *ctx)
149933a9b234SCy Schubert {
150033a9b234SCy Schubert     int i;
150133a9b234SCy Schubert     struct bind_address val;
150233a9b234SCy Schubert 
150333a9b234SCy Schubert     verto_free(ctx);
150433a9b234SCy Schubert 
150533a9b234SCy Schubert     /* Free each addresses added to the loop. */
150633a9b234SCy Schubert     FOREACH_ELT(bind_addresses, i, val)
150733a9b234SCy Schubert         free(val.address);
150833a9b234SCy Schubert     FREE_SET_DATA(bind_addresses);
150933a9b234SCy Schubert     FREE_SET_DATA(events);
151033a9b234SCy Schubert }
151133a9b234SCy Schubert 
151233a9b234SCy Schubert static int
have_event_for_fd(int fd)151333a9b234SCy Schubert have_event_for_fd(int fd)
151433a9b234SCy Schubert {
151533a9b234SCy Schubert     verto_ev *ev;
151633a9b234SCy Schubert     int i;
151733a9b234SCy Schubert 
151833a9b234SCy Schubert     FOREACH_ELT(events, i, ev) {
151933a9b234SCy Schubert         if (verto_get_fd(ev) == fd)
152033a9b234SCy Schubert             return 1;
152133a9b234SCy Schubert     }
152233a9b234SCy Schubert 
152333a9b234SCy Schubert     return 0;
152433a9b234SCy Schubert }
152533a9b234SCy Schubert 
152633a9b234SCy Schubert static void
accept_rpc_connection(verto_ctx * ctx,verto_ev * ev)152733a9b234SCy Schubert accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
152833a9b234SCy Schubert {
1529b0e4d68dSCy Schubert     verto_ev_flag flags;
153033a9b234SCy Schubert     struct connection *conn;
153133a9b234SCy Schubert     fd_set fds;
15320320e0d5SCy Schubert     int s;
153333a9b234SCy Schubert 
153433a9b234SCy Schubert     conn = verto_get_private(ev);
153533a9b234SCy Schubert 
153633a9b234SCy Schubert     /* Service the woken RPC listener descriptor. */
153733a9b234SCy Schubert     FD_ZERO(&fds);
153833a9b234SCy Schubert     FD_SET(verto_get_fd(ev), &fds);
153933a9b234SCy Schubert     svc_getreqset(&fds);
154033a9b234SCy Schubert 
154133a9b234SCy Schubert     /* Scan svc_fdset for any new connections. */
154233a9b234SCy Schubert     for (s = 0; s < FD_SETSIZE; s++) {
1543d82a140dSCy Schubert         struct sockaddr_storage addr;
1544d82a140dSCy Schubert         socklen_t addrlen = sizeof(addr);
154533a9b234SCy Schubert         struct connection *newconn;
154633a9b234SCy Schubert         verto_ev *newev;
154733a9b234SCy Schubert 
154833a9b234SCy Schubert         /* If we already have this fd, continue. */
154933a9b234SCy Schubert         if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s))
155033a9b234SCy Schubert             continue;
155133a9b234SCy Schubert 
1552b0e4d68dSCy Schubert         flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1553b0e4d68dSCy Schubert         if (add_fd(s, CONN_RPC, flags, conn->handle, conn->prog, ctx,
1554b0e4d68dSCy Schubert                    process_rpc_connection, &newev) != 0)
155533a9b234SCy Schubert             continue;
155633a9b234SCy Schubert         newconn = verto_get_private(newev);
155733a9b234SCy Schubert 
155833a9b234SCy Schubert         set_cloexec_fd(s);
155933a9b234SCy Schubert 
1560d82a140dSCy Schubert         if (getpeername(s, ss2sa(&addr), &addrlen) != 0) {
1561d82a140dSCy Schubert             strlcpy(newconn->addrbuf, "<unknown>", sizeof(newconn->addrbuf));
156233a9b234SCy Schubert         } else {
1563d82a140dSCy Schubert             k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,
1564d82a140dSCy Schubert                                sizeof(newconn->addrbuf));
156533a9b234SCy Schubert         }
156633a9b234SCy Schubert 
1567d82a140dSCy Schubert         newconn->addr_s = addr;
156833a9b234SCy Schubert         newconn->addrlen = addrlen;
156933a9b234SCy Schubert         newconn->start_time = time(0);
157033a9b234SCy Schubert 
1571d82a140dSCy Schubert         if (++stream_data_counter > max_stream_data_connections)
1572d82a140dSCy Schubert             kill_lru_stream_connection(newconn->handle, newev);
157333a9b234SCy Schubert     }
157433a9b234SCy Schubert }
157533a9b234SCy Schubert 
157633a9b234SCy Schubert static void
process_rpc_connection(verto_ctx * ctx,verto_ev * ev)157733a9b234SCy Schubert process_rpc_connection(verto_ctx *ctx, verto_ev *ev)
157833a9b234SCy Schubert {
157933a9b234SCy Schubert     fd_set fds;
158033a9b234SCy Schubert 
158133a9b234SCy Schubert     FD_ZERO(&fds);
158233a9b234SCy Schubert     FD_SET(verto_get_fd(ev), &fds);
158333a9b234SCy Schubert     svc_getreqset(&fds);
158433a9b234SCy Schubert 
158533a9b234SCy Schubert     if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))
158633a9b234SCy Schubert         verto_del(ev);
158733a9b234SCy Schubert }
158833a9b234SCy Schubert 
158933a9b234SCy Schubert #endif /* INET */
1590