diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 | 
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 | 
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/apputils/net-server.c | |
Diffstat (limited to 'src/lib/apputils/net-server.c')
| -rw-r--r-- | src/lib/apputils/net-server.c | 1580 | 
1 files changed, 1580 insertions, 0 deletions
diff --git a/src/lib/apputils/net-server.c b/src/lib/apputils/net-server.c new file mode 100644 index 000000000000..29ec84a15b22 --- /dev/null +++ b/src/lib/apputils/net-server.c @@ -0,0 +1,1580 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */ +/* + * Copyright 1990,2000,2007,2008,2009,2010,2016 by the Massachusetts Institute + * of Technology. + * + * Export of this software from the United States of America may + *   require a specific license from the United States Government. + *   It is the responsibility of any person or organization contemplating + *   export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission.  Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose.  It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "adm_proto.h" +#include <sys/ioctl.h> +#include <syslog.h> + +#include <stddef.h> +#include "port-sockets.h" +#include "socket-utils.h" + +#include <gssrpc/rpc.h> + +#ifdef HAVE_NETINET_IN_H +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_SOCKIO_H +/* for SIOCGIFCONF, etc. */ +#include <sys/sockio.h> +#endif +#include <sys/time.h> +#if HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <arpa/inet.h> + +#ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */ +#include <net/if.h> +#endif + +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h>          /* FIONBIO */ +#endif + +#include "fake-addrinfo.h" +#include "net-server.h" +#include <signal.h> +#include <netdb.h> + +#include "udppktinfo.h" + +/* XXX */ +#define KDC5_NONET                               (-1779992062L) + +static int tcp_or_rpc_data_counter; +static int max_tcp_or_rpc_data_connections = 45; + +static int +setreuseaddr(int sock, int value) +{ +    return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); +} + +#if defined(IPV6_V6ONLY) +static int +setv6only(int sock, int value) +{ +    return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)); +} +#endif + +static const char * +paddr(struct sockaddr *sa) +{ +    static char buf[100]; +    char portbuf[10]; +    if (getnameinfo(sa, sa_socklen(sa), +                    buf, sizeof(buf), portbuf, sizeof(portbuf), +                    NI_NUMERICHOST|NI_NUMERICSERV)) +        strlcpy(buf, "<unprintable>", sizeof(buf)); +    else { +        unsigned int len = sizeof(buf) - strlen(buf); +        char *p = buf + strlen(buf); +        if (len > 2+strlen(portbuf)) { +            *p++ = '.'; +            len--; +            strncpy(p, portbuf, len); +        } +    } +    return buf; +} + +/* Return true if sa is an IPv4 or IPv6 wildcard address. */ +static int +is_wildcard(struct sockaddr *sa) +{ +    if (sa->sa_family == AF_INET6) +        return IN6_IS_ADDR_UNSPECIFIED(&sa2sin6(sa)->sin6_addr); +    else if (sa->sa_family == AF_INET) +        return sa2sin(sa)->sin_addr.s_addr == INADDR_ANY; +    return 0; +} + +/* KDC data.  */ + +enum conn_type { +    CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC +}; + +enum bind_type { +    UDP, TCP, RPC +}; + +static const char *const bind_type_names[] = { +    [UDP] = "UDP", +    [TCP] = "TCP", +    [RPC] = "RPC", +}; + +/* Per-connection info.  */ +struct connection { +    void *handle; +    const char *prog; +    enum conn_type type; + +    /* Connection fields (TCP or RPC) */ +    struct sockaddr_storage addr_s; +    socklen_t addrlen; +    char addrbuf[56]; +    krb5_fulladdr faddr; +    krb5_address kaddr; + +    /* Incoming data (TCP) */ +    size_t bufsiz; +    size_t offset; +    char *buffer; +    size_t msglen; + +    /* Outgoing data (TCP) */ +    krb5_data *response; +    unsigned char lenbuf[4]; +    sg_buf sgbuf[2]; +    sg_buf *sgp; +    int sgnum; + +    /* Crude denial-of-service avoidance support (TCP or RPC) */ +    time_t start_time; + +    /* RPC-specific fields */ +    SVCXPRT *transp; +    int rpc_force_close; +}; + +#define SET(TYPE) struct { TYPE *data; size_t n, max; } + +/* Start at the top and work down -- this should allow for deletions +   without disrupting the iteration, since we delete by overwriting +   the element to be removed with the last element.  */ +#define FOREACH_ELT(set,idx,vvar)                                       \ +    for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--) + +#define GROW_SET(set, incr, tmpptr)                                     \ +    ((set.max + incr < set.max                                          \ +      || ((set.max + incr) * sizeof(set.data[0]) / sizeof(set.data[0])  \ +          != set.max + incr))                                           \ +     ? 0                         /* overflow */                         \ +     : ((tmpptr = realloc(set.data,                                     \ +                          (set.max + incr) * sizeof(set.data[0])))      \ +        ? (set.data = tmpptr, set.max += incr, 1)                       \ +        : 0)) + +/* 1 = success, 0 = failure */ +#define ADD(set, val, tmpptr)                           \ +    ((set.n < set.max || GROW_SET(set, 10, tmpptr))     \ +     ? (set.data[set.n++] = val, 1)                     \ +     : 0) + +#define DEL(set, idx)                           \ +    (set.data[idx] = set.data[--set.n], 0) + +#define FREE_SET_DATA(set)                                      \ +    (free(set.data), set.data = 0, set.max = 0, set.n = 0) + +/* + * N.B.: The Emacs cc-mode indentation code seems to get confused if + * the macro argument here is one word only.  So use "unsigned short" + * instead of the "u_short" we were using before. + */ +struct rpc_svc_data { +    u_long prognum; +    u_long versnum; +    void (*dispatch)(); +}; + +struct bind_address { +    char *address; +    u_short port; +    enum bind_type type; +    struct rpc_svc_data rpc_svc_data; +}; + +static SET(verto_ev *) events; +static SET(struct bind_address) bind_addresses; + +verto_ctx * +loop_init(verto_ev_type types) +{ +    types |= VERTO_EV_TYPE_IO; +    types |= VERTO_EV_TYPE_SIGNAL; +    types |= VERTO_EV_TYPE_TIMEOUT; +    return verto_default(NULL, types); +} + +static void +do_break(verto_ctx *ctx, verto_ev *ev) +{ +    krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit")); +    verto_break(ctx); +} + +struct sighup_context { +    void *handle; +    void (*reset)(void *); +}; + +static void +do_reset(verto_ctx *ctx, verto_ev *ev) +{ +    struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev); + +    krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset")); +    krb5_klog_reopen(get_context(sc->handle)); +    if (sc->reset) +        sc->reset(sc->handle); +} + +static void +free_sighup_context(verto_ctx *ctx, verto_ev *ev) +{ +    free(verto_get_private(ev)); +} + +krb5_error_code +loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)()) +{ +    struct sighup_context *sc; +    verto_ev *ev; + +    if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT)  || +        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) || +        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) || +        !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE)) +        return ENOMEM; + +    ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP); +    if (!ev) +        return ENOMEM; + +    sc = malloc(sizeof(*sc)); +    if (!sc) +        return ENOMEM; + +    sc->handle = handle; +    sc->reset = reset; +    verto_set_private(ev, sc, free_sighup_context); +    return 0; +} + +/* + * Add a bind address to the loop. + * + * Arguments: + * - address + *      A string for the address (or hostname).  Pass NULL to use the wildcard + *      address.  The address is parsed with k5_parse_host_string(). + * - port + *      What port the socket should be set to. + * - type + *      bind_type for the socket. + * - rpc_data + *      For RPC addresses, the svc_register() arguments to use when TCP + *      connections are created.  Ignored for other types. + */ +static krb5_error_code +loop_add_address(const char *address, int port, enum bind_type type, +                 struct rpc_svc_data *rpc_data) +{ +    struct bind_address addr, val; +    int i; +    void *tmp; +    char *addr_copy = NULL; + +    assert(!(type == RPC && rpc_data == NULL)); + +    /* Make sure a valid port number was passed. */ +    if (port < 0 || port > 65535) { +        krb5_klog_syslog(LOG_ERR, _("Invalid port %d"), port); +        return EINVAL; +    } + +    /* Check for conflicting addresses. */ +    FOREACH_ELT(bind_addresses, i, val) { +        if (type != val.type || port != val.port) +            continue; + +        /* If a wildcard address is being added, make sure to remove any direct +         * addresses. */ +        if (address == NULL && val.address != NULL) { +            krb5_klog_syslog(LOG_DEBUG, +                             _("Removing address %s since wildcard address" +                               " is being added"), +                             val.address); +            free(val.address); +            DEL(bind_addresses, i); +        } else if (val.address == NULL || !strcmp(address, val.address)) { +            krb5_klog_syslog(LOG_DEBUG, +                             _("Address already added to server")); +            return 0; +        } +    } + +    /* Copy the address if it is specified. */ +    if (address != NULL) { +        addr_copy = strdup(address); +        if (addr_copy == NULL) +            return ENOMEM; +    } + +    /* Add the new address to bind_addresses. */ +    memset(&addr, 0, sizeof(addr)); +    addr.address = addr_copy; +    addr.port = port; +    addr.type = type; +    if (rpc_data != NULL) +        addr.rpc_svc_data = *rpc_data; +    if (!ADD(bind_addresses, addr, tmp)) { +        free(addr_copy); +        return ENOMEM; +    } + +    return 0; +} + +/* + * Add bind addresses to the loop. + * + * Arguments: + * + * - addresses + *      A string for the addresses.  Pass NULL to use the wildcard address. + *      Supported delimeters can be found in ADDRESSES_DELIM.  Addresses are + *      parsed with k5_parse_host_name(). + * - default_port + *      What port the socket should be set to if not specified in addresses. + * - type + *      bind_type for the socket. + * - rpc_data + *      For RPC addresses, the svc_register() arguments to use when TCP + *      connections are created.  Ignored for other types. + */ +static krb5_error_code +loop_add_addresses(const char *addresses, int default_port, +                   enum bind_type type, struct rpc_svc_data *rpc_data) +{ +    krb5_error_code ret = 0; +    char *addresses_copy = NULL, *host = NULL, *saveptr, *addr; +    int port; + +    /* If no addresses are set, add a wildcard address. */ +    if (addresses == NULL) +        return loop_add_address(NULL, default_port, type, rpc_data); + +    /* Copy the addresses string before using strtok(). */ +    addresses_copy = strdup(addresses); +    if (addresses_copy == NULL) { +        ret = ENOMEM; +        goto cleanup; +    } + +    /* Start tokenizing the addresses string.  If we get NULL the string +     * contained no addresses, so add a wildcard address. */ +    addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr); +    if (addr == NULL) { +        ret = loop_add_address(NULL, default_port, type, rpc_data); +        goto cleanup; +    } + +    /* Loop through each address and add it to the loop. */ +    for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) { +        /* Parse the host string. */ +        ret = k5_parse_host_string(addr, default_port, &host, &port); +        if (ret) +            goto cleanup; + +        ret = loop_add_address(host, port, type, rpc_data); +        if (ret) +            goto cleanup; + +        free(host); +        host = NULL; +    } + +cleanup: +    free(addresses_copy); +    free(host); +    return ret; +} + +krb5_error_code +loop_add_udp_address(int default_port, const char *addresses) +{ +    return loop_add_addresses(addresses, default_port, UDP, NULL); +} + +krb5_error_code +loop_add_tcp_address(int default_port, const char *addresses) +{ +    return loop_add_addresses(addresses, default_port, TCP, NULL); +} + +krb5_error_code +loop_add_rpc_service(int default_port, const char *addresses, u_long prognum, +                     u_long versnum, void (*dispatchfn)()) +{ +    struct rpc_svc_data svc; + +    svc.prognum = prognum; +    svc.versnum = versnum; +    svc.dispatch = dispatchfn; +    return loop_add_addresses(addresses, default_port, RPC, &svc); +} + +#define USE_AF AF_INET +#define USE_TYPE SOCK_DGRAM +#define USE_PROTO 0 +#define SOCKET_ERRNO errno +#include "foreachaddr.h" + +struct socksetup { +    verto_ctx *ctx; +    void *handle; +    const char *prog; +    krb5_error_code retval; +    int listen_backlog; +}; + +static void +free_connection(struct connection *conn) +{ +    if (!conn) +        return; +    if (conn->response) +        krb5_free_data(get_context(conn->handle), conn->response); +    if (conn->buffer) +        free(conn->buffer); +    if (conn->type == CONN_RPC_LISTENER && conn->transp != NULL) +        svc_destroy(conn->transp); +    free(conn); +} + +static void +remove_event_from_set(verto_ev *ev) +{ +    verto_ev *tmp; +    int i; + +    /* Remove the event from the events. */ +    FOREACH_ELT(events, i, tmp) +        if (tmp == ev) { +            DEL(events, i); +            break; +        } +} + +static void +free_socket(verto_ctx *ctx, verto_ev *ev) +{ +    struct connection *conn = NULL; +    fd_set fds; +    int fd; + +    remove_event_from_set(ev); + +    fd = verto_get_fd(ev); +    conn = verto_get_private(ev); + +    /* Close the file descriptor. */ +    krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd); +    if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->rpc_force_close)) +        close(fd); + +    /* Free the connection struct. */ +    if (conn) { +        switch (conn->type) { +        case CONN_RPC: +            if (conn->rpc_force_close) { +                FD_ZERO(&fds); +                FD_SET(fd, &fds); +                svc_getreqset(&fds); +                if (FD_ISSET(fd, &svc_fdset)) { +                    krb5_klog_syslog(LOG_ERR, +                                     _("descriptor %d closed but still " +                                       "in svc_fdset"), +                                     fd); +                } +            } +            /* Fall through. */ +        case CONN_TCP: +            tcp_or_rpc_data_counter--; +            break; +        default: +            break; +        } + +        free_connection(conn); +    } +} + +static verto_ev * +make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback, +           int sock, struct connection *conn, int addevent) +{ +    verto_ev *ev; +    void *tmp; + +    ev = verto_add_io(ctx, flags, callback, sock); +    if (!ev) { +        com_err(conn->prog, ENOMEM, _("cannot create io event")); +        return NULL; +    } + +    if (addevent) { +        if (!ADD(events, ev, tmp)) { +            com_err(conn->prog, ENOMEM, _("cannot save event")); +            verto_del(ev); +            return NULL; +        } +    } + +    verto_set_private(ev, conn, free_socket); +    return ev; +} + +static verto_ev * +add_fd(struct socksetup *data, int sock, enum conn_type conntype, +       verto_ev_flag flags, verto_callback callback, int addevent) +{ +    struct connection *newconn; + +#ifndef _WIN32 +    if (sock >= FD_SETSIZE) { +        data->retval = EMFILE;  /* XXX */ +        com_err(data->prog, 0, +                _("file descriptor number %d too high"), sock); +        return 0; +    } +#endif +    newconn = malloc(sizeof(*newconn)); +    if (newconn == NULL) { +        data->retval = ENOMEM; +        com_err(data->prog, ENOMEM, +                _("cannot allocate storage for connection info")); +        return 0; +    } +    memset(newconn, 0, sizeof(*newconn)); +    newconn->handle = data->handle; +    newconn->prog = data->prog; +    newconn->type = conntype; + +    return make_event(data->ctx, flags, callback, sock, newconn, addevent); +} + +static void process_packet(verto_ctx *ctx, verto_ev *ev); +static void accept_tcp_connection(verto_ctx *ctx, verto_ev *ev); +static void process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev); +static void process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev); +static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev); +static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev); + +static verto_ev * +add_tcp_read_fd(struct socksetup *data, int sock) +{ +    return add_fd(data, sock, CONN_TCP, +                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST, +                  process_tcp_connection_read, 1); +} + +/* + * Create a socket and bind it to addr.  Ensure the socket will work with + * select().  Set the socket cloexec, reuseaddr, and if applicable v6-only. + * Does not call listen().  Returns -1 on failure after logging an error. + */ +static int +create_server_socket(struct socksetup *data, struct sockaddr *addr, int type) +{ +    int sock; + +    sock = socket(addr->sa_family, type, 0); +    if (sock == -1) { +        data->retval = errno; +        com_err(data->prog, errno, _("Cannot create TCP server socket on %s"), +                paddr(addr)); +        return -1; +    } +    set_cloexec_fd(sock); + +#ifndef _WIN32                  /* Windows FD_SETSIZE is a count. */ +    if (sock >= FD_SETSIZE) { +        close(sock); +        com_err(data->prog, 0, _("TCP socket fd number %d (for %s) too high"), +                sock, paddr(addr)); +        return -1; +    } +#endif + +    if (setreuseaddr(sock, 1) < 0) { +        com_err(data->prog, errno, +                _("Cannot enable SO_REUSEADDR on fd %d"), sock); +    } + +    if (addr->sa_family == AF_INET6) { +#ifdef IPV6_V6ONLY +        if (setv6only(sock, 1)) +            com_err(data->prog, errno, +                    _("setsockopt(%d,IPV6_V6ONLY,1) failed"), sock); +        else +            com_err(data->prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"), +                    sock); +#else +        krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support")); +#endif /* IPV6_V6ONLY */ +    } + +    if (bind(sock, addr, sa_socklen(addr)) == -1) { +        data->retval = errno; +        com_err(data->prog, errno, _("Cannot bind server socket on %s"), +                paddr(addr)); +        close(sock); +        return -1; +    } + +    return sock; +} + +static verto_ev * +add_rpc_data_fd(struct socksetup *data, int sock) +{ +    return add_fd(data, sock, CONN_RPC, +                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST, +                  process_rpc_connection, 1); +} + +static const int one = 1; + +static int +setnbio(int sock) +{ +    return ioctlsocket(sock, FIONBIO, (const void *)&one); +} + +static int +setkeepalive(int sock) +{ +    return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)); +} + +static int +setnolinger(int s) +{ +    static const struct linger ling = { 0, 0 }; +    return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); +} + +/* An enum map to socket families for each bind_type. */ +static const int bind_socktypes[] = +{ +    [UDP] = SOCK_DGRAM, +    [TCP] = SOCK_STREAM, +    [RPC] = SOCK_STREAM +}; + +/* An enum map containing conn_type (for struct connection) for each + * bind_type.  */ +static const enum conn_type bind_conn_types[] = +{ +    [UDP] = CONN_UDP, +    [TCP] = CONN_TCP_LISTENER, +    [RPC] = CONN_RPC_LISTENER +}; + +/* + * Set up a listening socket. + * + * Arguments: + * + * - ba + *      The bind address and port for the socket. + * - ai + *      The addrinfo struct to use for creating the socket. + * - ctype + *      The conn_type of this socket. + */ +static krb5_error_code +setup_socket(struct socksetup *data, struct bind_address *ba, +             struct sockaddr *sock_address, verto_callback vcb, +             enum conn_type ctype) +{ +    krb5_error_code ret; +    struct connection *conn; +    verto_ev *ev = NULL; +    int sock = -1; + +    krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"), +                     bind_type_names[ba->type], paddr(sock_address)); + +    /* Create the socket. */ +    sock = create_server_socket(data, sock_address, bind_socktypes[ba->type]); +    if (sock == -1) { +        ret = data->retval; +        goto cleanup; +    } + +    /* Listen for backlogged connections on TCP sockets.  (For RPC sockets this +     * will be done by svc_register().) */ +    if (ba->type == TCP && listen(sock, data->listen_backlog) != 0) { +        ret = errno; +        com_err(data->prog, errno, +                _("Cannot listen on %s server socket on %s"), +                bind_type_names[ba->type], paddr(sock_address)); +        goto cleanup; +    } + +    /* Set non-blocking I/O for UDP and TCP listener sockets. */ +    if (ba->type != RPC && setnbio(sock) != 0) { +        ret = errno; +        com_err(data->prog, errno, +                _("cannot set listening %s socket on %s non-blocking"), +                bind_type_names[ba->type], paddr(sock_address)); +        goto cleanup; +    } + +    /* Turn off the linger option for TCP sockets. */ +    if (ba->type == TCP && setnolinger(sock) != 0) { +        ret = errno; +        com_err(data->prog, errno, +                _("cannot set SO_LINGER on %s socket on %s"), +                bind_type_names[ba->type], paddr(sock_address)); +        goto cleanup; +    } + +    /* Try to turn on pktinfo for UDP wildcard sockets. */ +    if (ba->type == UDP && is_wildcard(sock_address)) { +        krb5_klog_syslog(LOG_DEBUG, _("Setting pktinfo on socket %s"), +                         paddr(sock_address)); +        ret = set_pktinfo(sock, sock_address->sa_family); +        if (ret) { +            com_err(data->prog, ret, +                    _("Cannot request packet info for UDP socket address " +                      "%s port %d"), paddr(sock_address), ba->port); +            krb5_klog_syslog(LOG_INFO, _("System does not support pktinfo yet " +                                         "binding to a wildcard address.  " +                                         "Packets are not guaranteed to " +                                         "return on the received address.")); +        } +    } + +    /* Add the socket to the event loop. */ +    ev = add_fd(data, sock, ctype, +                VERTO_EV_FLAG_IO_READ | +                VERTO_EV_FLAG_PERSIST | +                VERTO_EV_FLAG_REINITIABLE, vcb, 1); +    if (ev == NULL) { +        krb5_klog_syslog(LOG_ERR, _("Error attempting to add verto event")); +        ret = data->retval; +        goto cleanup; +    } + +    if (ba->type == RPC) { +        conn = verto_get_private(ev); +        conn->transp = svctcp_create(sock, 0, 0); +        if (conn->transp == NULL) { +            ret = errno; +            krb5_klog_syslog(LOG_ERR, _("Cannot create RPC service: %s"), +                             strerror(ret)); +            goto cleanup; +        } + +        ret = svc_register(conn->transp, ba->rpc_svc_data.prognum, +                           ba->rpc_svc_data.versnum, ba->rpc_svc_data.dispatch, +                           0); +        if (!ret) { +            ret = errno; +            krb5_klog_syslog(LOG_ERR, _("Cannot register RPC service: %s"), +                             strerror(ret)); +            goto cleanup; +        } +    } + +    ev = NULL; +    sock = -1; +    ret = 0; + +cleanup: +    if (sock >= 0) +        close(sock); +    if (ev != NULL) +        verto_del(ev); +    return ret; +} + +/* + * Setup all the socket addresses that the net-server should listen to. + * + * This function uses getaddrinfo to figure out all the addresses. This will + * automatically figure out which socket families that should be used on the + * host making it useful even for wildcard addresses. + * + * Arguments: + * - data + *      A pointer to the socksetup data. + */ +static krb5_error_code +setup_addresses(struct socksetup *data) +{ +    /* An bind_type enum map for the verto callback functions. */ +    static verto_callback *const verto_callbacks[] = { +        [UDP] = &process_packet, +        [TCP] = &accept_tcp_connection, +        [RPC] = &accept_rpc_connection +    }; +    krb5_error_code ret = 0; +    size_t i; +    int err, bound_any; +    struct bind_address addr; +    struct addrinfo hints, *ai_list = NULL, *ai = NULL; +    verto_callback vcb; + +    /* Check to make sure addresses were added to the server. */ +    if (bind_addresses.n == 0) { +        krb5_klog_syslog(LOG_ERR, _("No addresses added to the net server")); +        return EINVAL; +    } + +    /* Ask for all address families, listener addresses, and no port name +     * resolution. */ +    memset(&hints, 0, sizeof(struct addrinfo)); +    hints.ai_family = AF_UNSPEC; +    hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + +    /* Add all the requested addresses. */ +    for (i = 0; i < bind_addresses.n; i++) { +        addr = bind_addresses.data[i]; +        hints.ai_socktype = bind_socktypes[addr.type]; + +        /* Call getaddrinfo, using a dummy port value. */ +        err = getaddrinfo(addr.address, "0", &hints, &ai_list); +        if (err) { +            krb5_klog_syslog(LOG_ERR, +                             _("Failed getting address info (for %s): %s"), +                             (addr.address == NULL) ? "<wildcard>" : +                             addr.address, gai_strerror(err)); +            ret = EIO; +            goto cleanup; +        } + +        /* +         * Loop through all the sockets that getaddrinfo could find to match +         * the requested address.  For wildcard listeners, this should usually +         * have two results, one for each of IPv4 and IPv6, or one or the +         * other, depending on the system.  On IPv4-only systems, getaddrinfo() +         * may return both IPv4 and IPv6 addresses, but creating an IPv6 socket +         * may give an EAFNOSUPPORT error, so tolerate that error as long as we +         * can bind at least one socket. +         */ +        bound_any = 0; +        for (ai = ai_list; ai != NULL; ai = ai->ai_next) { +            /* Make sure getaddrinfo returned a socket with the same type that +             * was requested. */ +            assert(hints.ai_socktype == ai->ai_socktype); + +            /* Set the real port number. */ +            sa_setport(ai->ai_addr, addr.port); + +            ret = setup_socket(data, &addr, ai->ai_addr, +                               verto_callbacks[addr.type], +                               bind_conn_types[addr.type]); +            if (ret) { +                krb5_klog_syslog(LOG_ERR, +                                 _("Failed setting up a %s socket (for %s)"), +                                 bind_type_names[addr.type], +                                 paddr(ai->ai_addr)); +                if (ret != EAFNOSUPPORT) +                    goto cleanup; +            } else { +                bound_any = 1; +            } +        } +        if (!bound_any) +            goto cleanup; +        ret = 0; + +        if (ai_list != NULL) +            freeaddrinfo(ai_list); +        ai_list = NULL; +    } + +cleanup: +    if (ai_list != NULL) +        freeaddrinfo(ai_list); +    return ret; +} + +krb5_error_code +loop_setup_network(verto_ctx *ctx, void *handle, const char *prog, +                   int tcp_listen_backlog) +{ +    struct socksetup setup_data; +    verto_ev *ev; +    int i, ret; + +    /* Check to make sure that at least one address was added to the loop. */ +    if (bind_addresses.n == 0) +        return EINVAL; + +    /* Close any open connections. */ +    FOREACH_ELT(events, i, ev) +        verto_del(ev); +    events.n = 0; + +    setup_data.ctx = ctx; +    setup_data.handle = handle; +    setup_data.prog = prog; +    setup_data.retval = 0; +    setup_data.listen_backlog = tcp_listen_backlog; + +    krb5_klog_syslog(LOG_INFO, _("setting up network...")); +    ret = setup_addresses(&setup_data); +    if (ret != 0) { +        com_err(prog, ret, _("Error setting up network")); +        exit(1); +    } +    krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n); +    if (events.n == 0) { +        /* If no sockets were set up, we can't continue. */ +        com_err(prog, 0, _("no sockets set up?")); +        exit (1); +    } + +    return 0; +} + +void +init_addr(krb5_fulladdr *faddr, struct sockaddr *sa) +{ +    switch (sa->sa_family) { +    case AF_INET: +        faddr->address->addrtype = ADDRTYPE_INET; +        faddr->address->length = 4; +        faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr; +        faddr->port = ntohs(sa2sin(sa)->sin_port); +        break; +    case AF_INET6: +        if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) { +            faddr->address->addrtype = ADDRTYPE_INET; +            faddr->address->length = 4; +            faddr->address->contents = 12 + (krb5_octet *) &sa2sin6(sa)->sin6_addr; +        } else { +            faddr->address->addrtype = ADDRTYPE_INET6; +            faddr->address->length = 16; +            faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr; +        } +        faddr->port = ntohs(sa2sin6(sa)->sin6_port); +        break; +    default: +        faddr->address->addrtype = -1; +        faddr->address->length = 0; +        faddr->address->contents = 0; +        faddr->port = 0; +        break; +    } +} + +struct udp_dispatch_state { +    void *handle; +    const char *prog; +    int port_fd; +    krb5_address addr; +    krb5_fulladdr faddr; +    socklen_t saddr_len; +    socklen_t daddr_len; +    struct sockaddr_storage saddr; +    struct sockaddr_storage daddr; +    aux_addressing_info auxaddr; +    krb5_data request; +    char pktbuf[MAX_DGRAM_SIZE]; +}; + +static void +process_packet_response(void *arg, krb5_error_code code, krb5_data *response) +{ +    struct udp_dispatch_state *state = arg; +    int cc; + +    if (code) +        com_err(state->prog ? state->prog : NULL, code, +                _("while dispatching (udp)")); +    if (code || response == NULL) +        goto out; + +    cc = send_to_from(state->port_fd, response->data, +                      (socklen_t) response->length, 0, +                      (struct sockaddr *)&state->saddr, state->saddr_len, +                      (struct sockaddr *)&state->daddr, state->daddr_len, +                      &state->auxaddr); +    if (cc == -1) { +        /* Note that the local address (daddr*) has no port number +         * info associated with it. */ +        char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV]; +        char daddrbuf[NI_MAXHOST]; +        int e = errno; + +        if (getnameinfo((struct sockaddr *)&state->daddr, state->daddr_len, +                        daddrbuf, sizeof(daddrbuf), 0, 0, +                        NI_NUMERICHOST) != 0) { +            strlcpy(daddrbuf, "?", sizeof(daddrbuf)); +        } + +        if (getnameinfo((struct sockaddr *)&state->saddr, state->saddr_len, +                        saddrbuf, sizeof(saddrbuf), sportbuf, sizeof(sportbuf), +                        NI_NUMERICHOST|NI_NUMERICSERV) != 0) { +            strlcpy(saddrbuf, "?", sizeof(saddrbuf)); +            strlcpy(sportbuf, "?", sizeof(sportbuf)); +        } + +        com_err(state->prog, e, _("while sending reply to %s/%s from %s"), +                saddrbuf, sportbuf, daddrbuf); +        goto out; +    } +    if ((size_t)cc != response->length) { +        com_err(state->prog, 0, _("short reply write %d vs %d\n"), +                response->length, cc); +    } + +out: +    krb5_free_data(get_context(state->handle), response); +    free(state); +} + +static void +process_packet(verto_ctx *ctx, verto_ev *ev) +{ +    int cc; +    struct connection *conn; +    struct udp_dispatch_state *state; + +    conn = verto_get_private(ev); + +    state = malloc(sizeof(*state)); +    if (!state) { +        com_err(conn->prog, ENOMEM, _("while dispatching (udp)")); +        return; +    } + +    state->handle = conn->handle; +    state->prog = conn->prog; +    state->port_fd = verto_get_fd(ev); +    assert(state->port_fd >= 0); + +    state->saddr_len = sizeof(state->saddr); +    state->daddr_len = sizeof(state->daddr); +    memset(&state->auxaddr, 0, sizeof(state->auxaddr)); +    cc = recv_from_to(state->port_fd, state->pktbuf, sizeof(state->pktbuf), 0, +                      (struct sockaddr *)&state->saddr, &state->saddr_len, +                      (struct sockaddr *)&state->daddr, &state->daddr_len, +                      &state->auxaddr); +    if (cc == -1) { +        if (errno != EINTR && errno != EAGAIN +            /* +             * This is how Linux indicates that a previous transmission was +             * refused, e.g., if the client timed out before getting the +             * response packet. +             */ +            && errno != ECONNREFUSED +        ) +            com_err(conn->prog, errno, _("while receiving from network")); +        free(state); +        return; +    } +    if (!cc) { /* zero-length packet? */ +        free(state); +        return; +    } + +#if 0 +    if (state->daddr_len > 0) { +        char addrbuf[100]; +        if (getnameinfo(ss2sa(&state->daddr), state->daddr_len, +                        addrbuf, sizeof(addrbuf), +                        0, 0, NI_NUMERICHOST)) +            strlcpy(addrbuf, "?", sizeof(addrbuf)); +        com_err(conn->prog, 0, _("pktinfo says local addr is %s"), addrbuf); +    } +#endif + +    if (state->daddr_len == 0 && conn->type == CONN_UDP) { +        /* +         * An address couldn't be obtained, so the PKTINFO option probably +         * isn't available.  If the socket is bound to a specific address, then +         * try to get the address here. +         */ +        state->daddr_len = sizeof(state->daddr); +        if (getsockname(state->port_fd, (struct sockaddr *)&state->daddr, +                        &state->daddr_len) != 0) +            state->daddr_len = 0; +        /* On failure, keep going anyways. */ +    } + +    state->request.length = cc; +    state->request.data = state->pktbuf; +    state->faddr.address = &state->addr; +    init_addr(&state->faddr, ss2sa(&state->saddr)); +    /* This address is in net order. */ +    dispatch(state->handle, ss2sa(&state->daddr), &state->faddr, +             &state->request, 0, ctx, process_packet_response, state); +} + +static int +kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev) +{ +    struct connection *c = NULL, *oldest_c = NULL; +    verto_ev *ev, *oldest_ev = NULL; +    int i, fd = -1; + +    krb5_klog_syslog(LOG_INFO, _("too many connections")); + +    FOREACH_ELT (events, i, ev) { +        if (ev == newev) +            continue; + +        c = verto_get_private(ev); +        if (!c) +            continue; +        if (c->type != CONN_TCP && c->type != CONN_RPC) +            continue; +#if 0 +        krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", +                         verto_get_fd(oldest_ev), +                         c->start_time); +#endif +        if (oldest_c == NULL +            || oldest_c->start_time > c->start_time) { +            oldest_ev = ev; +            oldest_c = c; +        } +    } +    if (oldest_c != NULL) { +        krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"), +                         c->type == CONN_RPC ? "rpc" : "tcp", +                         verto_get_fd(oldest_ev), oldest_c->addrbuf); +        if (oldest_c->type == CONN_RPC) +            oldest_c->rpc_force_close = 1; +        verto_del(oldest_ev); +    } +    return fd; +} + +static void +accept_tcp_connection(verto_ctx *ctx, verto_ev *ev) +{ +    int s; +    struct sockaddr_storage addr_s; +    struct sockaddr *addr = (struct sockaddr *)&addr_s; +    socklen_t addrlen = sizeof(addr_s); +    struct socksetup sockdata; +    struct connection *newconn, *conn; +    char tmpbuf[10]; +    verto_ev *newev; + +    conn = verto_get_private(ev); +    s = accept(verto_get_fd(ev), addr, &addrlen); +    if (s < 0) +        return; +    set_cloexec_fd(s); +#ifndef _WIN32 +    if (s >= FD_SETSIZE) { +        close(s); +        return; +    } +#endif +    setnbio(s), setnolinger(s), setkeepalive(s); + +    sockdata.ctx = ctx; +    sockdata.handle = conn->handle; +    sockdata.prog = conn->prog; +    sockdata.retval = 0; + +    newev = add_tcp_read_fd(&sockdata, s); +    if (newev == NULL) { +        close(s); +        return; +    } +    newconn = verto_get_private(newev); + +    if (getnameinfo((struct sockaddr *)&addr_s, addrlen, +                    newconn->addrbuf, sizeof(newconn->addrbuf), +                    tmpbuf, sizeof(tmpbuf), +                    NI_NUMERICHOST | NI_NUMERICSERV)) +        strlcpy(newconn->addrbuf, "???", sizeof(newconn->addrbuf)); +    else { +        char *p, *end; +        p = newconn->addrbuf; +        end = p + sizeof(newconn->addrbuf); +        p += strlen(p); +        if ((size_t)(end - p) > 2 + strlen(tmpbuf)) { +            *p++ = '.'; +            strlcpy(p, tmpbuf, end - p); +        } +    } +#if 0 +    krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s", +                     s, newconn->addrbuf); +#endif + +    newconn->addr_s = addr_s; +    newconn->addrlen = addrlen; +    newconn->bufsiz = 1024 * 1024; +    newconn->buffer = malloc(newconn->bufsiz); +    newconn->start_time = time(0); + +    if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections) +        kill_lru_tcp_or_rpc_connection(conn->handle, newev); + +    if (newconn->buffer == 0) { +        com_err(conn->prog, errno, +                _("allocating buffer for new TCP session from %s"), +                newconn->addrbuf); +        verto_del(newev); +        return; +    } +    newconn->offset = 0; +    newconn->faddr.address = &newconn->kaddr; +    init_addr(&newconn->faddr, ss2sa(&newconn->addr_s)); +    SG_SET(&newconn->sgbuf[0], newconn->lenbuf, 4); +    SG_SET(&newconn->sgbuf[1], 0, 0); +} + +struct tcp_dispatch_state { +    struct sockaddr_storage local_saddr; +    struct connection *conn; +    krb5_data request; +    verto_ctx *ctx; +    int sock; +}; + +static void +process_tcp_response(void *arg, krb5_error_code code, krb5_data *response) +{ +    struct tcp_dispatch_state *state = arg; +    verto_ev *ev; + +    assert(state); +    state->conn->response = response; + +    if (code) +        com_err(state->conn->prog, code, _("while dispatching (tcp)")); +    if (code || !response) +        goto kill_tcp_connection; + +    /* Queue outgoing response. */ +    store_32_be(response->length, state->conn->lenbuf); +    SG_SET(&state->conn->sgbuf[1], response->data, response->length); +    state->conn->sgp = state->conn->sgbuf; +    state->conn->sgnum = 2; + +    ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST, +                    process_tcp_connection_write, state->sock, state->conn, 1); +    if (ev) { +        free(state); +        return; +    } + +kill_tcp_connection: +    tcp_or_rpc_data_counter--; +    free_connection(state->conn); +    close(state->sock); +    free(state); +} + +/* Creates the tcp_dispatch_state and deletes the verto event. */ +static struct tcp_dispatch_state * +prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev) +{ +    struct tcp_dispatch_state *state; + +    state = malloc(sizeof(*state)); +    if (!state) { +        krb5_klog_syslog(LOG_ERR, _("error allocating tcp dispatch private!")); +        return NULL; +    } +    state->conn = verto_get_private(ev); +    state->sock = verto_get_fd(ev); +    state->ctx = ctx; +    verto_set_private(ev, NULL, NULL); /* Don't close the fd or free conn! */ +    remove_event_from_set(ev); /* Remove it from the set. */ +    verto_del(ev); +    return state; +} + +static void +process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev) +{ +    struct tcp_dispatch_state *state = NULL; +    struct connection *conn = NULL; +    ssize_t nread; +    size_t len; + +    conn = verto_get_private(ev); + +    /* +     * Read message length and data into one big buffer, already allocated +     * at connect time.  If we have a complete message, we stop reading, so +     * we should only be here if there is no data in the buffer, or only an +     * incomplete message. +     */ +    if (conn->offset < 4) { +        krb5_data *response = NULL; + +        /* msglen has not been computed.  XXX Doing at least two reads +         * here, letting the kernel worry about buffering. */ +        len = 4 - conn->offset; +        nread = SOCKET_READ(verto_get_fd(ev), +                            conn->buffer + conn->offset, len); +        if (nread < 0) /* error */ +            goto kill_tcp_connection; +        if (nread == 0) /* eof */ +            goto kill_tcp_connection; +        conn->offset += nread; +        if (conn->offset == 4) { +            unsigned char *p = (unsigned char *)conn->buffer; +            conn->msglen = load_32_be(p); +            if (conn->msglen > conn->bufsiz - 4) { +                krb5_error_code err; +                /* Message too big. */ +                krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, " +                                            "cap is %lu"), conn->addrbuf, +                                 (unsigned long) conn->msglen, +                                 (unsigned long) conn->bufsiz - 4); +                /* XXX Should return an error.  */ +                err = make_toolong_error (conn->handle, +                                          &response); +                if (err) { +                    krb5_klog_syslog(LOG_ERR, _("error constructing " +                                                "KRB_ERR_FIELD_TOOLONG error! %s"), +                                     error_message(err)); +                    goto kill_tcp_connection; +                } + +                state = prepare_for_dispatch(ctx, ev); +                if (!state) { +                    krb5_free_data(get_context(conn->handle), response); +                    goto kill_tcp_connection; +                } +                process_tcp_response(state, 0, response); +            } +        } +    } else { +        /* msglen known. */ +        socklen_t local_saddrlen = sizeof(struct sockaddr_storage); +        struct sockaddr *local_saddrp = NULL; + +        len = conn->msglen - (conn->offset - 4); +        nread = SOCKET_READ(verto_get_fd(ev), +                            conn->buffer + conn->offset, len); +        if (nread < 0) /* error */ +            goto kill_tcp_connection; +        if (nread == 0) /* eof */ +            goto kill_tcp_connection; +        conn->offset += nread; +        if (conn->offset < conn->msglen + 4) +            return; + +        /* Have a complete message, and exactly one message. */ +        state = prepare_for_dispatch(ctx, ev); +        if (!state) +            goto kill_tcp_connection; + +        state->request.length = conn->msglen; +        state->request.data = conn->buffer + 4; + +        if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr), +                        &local_saddrlen) == 0) +            local_saddrp = ss2sa(&state->local_saddr); + +        dispatch(state->conn->handle, local_saddrp, &conn->faddr, +                 &state->request, 1, ctx, process_tcp_response, state); +    } + +    return; + +kill_tcp_connection: +    verto_del(ev); +} + +static void +process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev) +{ +    struct connection *conn; +    SOCKET_WRITEV_TEMP tmp; +    ssize_t nwrote; +    int sock; + +    conn = verto_get_private(ev); +    sock = verto_get_fd(ev); + +    nwrote = SOCKET_WRITEV(sock, conn->sgp, +                           conn->sgnum, tmp); +    if (nwrote > 0) { /* non-error and non-eof */ +        while (nwrote) { +            sg_buf *sgp = conn->sgp; +            if ((size_t)nwrote < SG_LEN(sgp)) { +                SG_ADVANCE(sgp, (size_t)nwrote); +                nwrote = 0; +            } else { +                nwrote -= SG_LEN(sgp); +                conn->sgp++; +                conn->sgnum--; +                if (conn->sgnum == 0 && nwrote != 0) +                    abort(); +            } +        } + +        /* If we still have more data to send, just return so that +         * the main loop can call this function again when the socket +         * is ready for more writing. */ +        if (conn->sgnum > 0) +            return; +    } + +    /* Finished sending.  We should go back to reading, though if we +     * sent a FIELD_TOOLONG error in reply to a length with the high +     * bit set, RFC 4120 says we have to close the TCP stream. */ +    verto_del(ev); +} + +void +loop_free(verto_ctx *ctx) +{ +    int i; +    struct bind_address val; + +    verto_free(ctx); + +    /* Free each addresses added to the loop. */ +    FOREACH_ELT(bind_addresses, i, val) +        free(val.address); +    FREE_SET_DATA(bind_addresses); +    FREE_SET_DATA(events); +} + +static int +have_event_for_fd(int fd) +{ +    verto_ev *ev; +    int i; + +    FOREACH_ELT(events, i, ev) { +        if (verto_get_fd(ev) == fd) +            return 1; +    } + +    return 0; +} + +static void +accept_rpc_connection(verto_ctx *ctx, verto_ev *ev) +{ +    struct socksetup sockdata; +    struct connection *conn; +    fd_set fds; +    register int s; + +    conn = verto_get_private(ev); + +    sockdata.ctx = ctx; +    sockdata.handle = conn->handle; +    sockdata.prog = conn->prog; +    sockdata.retval = 0; + +    /* Service the woken RPC listener descriptor. */ +    FD_ZERO(&fds); +    FD_SET(verto_get_fd(ev), &fds); +    svc_getreqset(&fds); + +    /* Scan svc_fdset for any new connections. */ +    for (s = 0; s < FD_SETSIZE; s++) { +        struct sockaddr_storage addr_s; +        struct sockaddr *addr = (struct sockaddr *) &addr_s; +        socklen_t addrlen = sizeof(addr_s); +        struct connection *newconn; +        char tmpbuf[10]; +        verto_ev *newev; + +        /* If we already have this fd, continue. */ +        if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s)) +            continue; + +        newev = add_rpc_data_fd(&sockdata, s); +        if (newev == NULL) +            continue; +        newconn = verto_get_private(newev); + +        set_cloexec_fd(s); +#if 0 +        setnbio(s), setnolinger(s), setkeepalive(s); +#endif + +        if (getpeername(s, addr, &addrlen) || +            getnameinfo(addr, addrlen, +                        newconn->addrbuf, +                        sizeof(newconn->addrbuf), +                        tmpbuf, sizeof(tmpbuf), +                        NI_NUMERICHOST | NI_NUMERICSERV)) { +            strlcpy(newconn->addrbuf, "???", +                    sizeof(newconn->addrbuf)); +        } else { +            char *p, *end; +            p = newconn->addrbuf; +            end = p + sizeof(newconn->addrbuf); +            p += strlen(p); +            if ((size_t)(end - p) > 2 + strlen(tmpbuf)) { +                *p++ = '.'; +                strlcpy(p, tmpbuf, end - p); +            } +        } +#if 0 +        krb5_klog_syslog(LOG_INFO, _("accepted RPC connection on socket %d " +                                     "from %s"), s, newconn->addrbuf); +#endif + +        newconn->addr_s = addr_s; +        newconn->addrlen = addrlen; +        newconn->start_time = time(0); + +        if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections) +            kill_lru_tcp_or_rpc_connection(newconn->handle, newev); + +        newconn->faddr.address = &newconn->kaddr; +        init_addr(&newconn->faddr, ss2sa(&newconn->addr_s)); +    } +} + +static void +process_rpc_connection(verto_ctx *ctx, verto_ev *ev) +{ +    fd_set fds; + +    FD_ZERO(&fds); +    FD_SET(verto_get_fd(ev), &fds); +    svc_getreqset(&fds); + +    if (!FD_ISSET(verto_get_fd(ev), &svc_fdset)) +        verto_del(ev); +} + +#endif /* INET */  | 
