Fix initialization and pointer bugs in new code
[krb5.git] / src / lib / apputils / net-server.c
index 9d3daea40e1181fa45cc8c57ff8ee9040060b2db..11a7c122fffa3bf03168c76c036be5c154b91841 100644 (file)
@@ -1,7 +1,6 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */
 /*
- * lib/apputils/net-server.c
- *
  * Copyright 1990,2000,2007,2008,2009,2010 by the Massachusetts Institute of Technology.
  *
  * Export of this software from the United States of America may
@@ -22,9 +21,6 @@
  * 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.
- *
- *
- * Network code for Kerberos v5 servers (kdc, kadmind).
  */
 
 #include "k5-int.h"
 
 #include "fake-addrinfo.h"
 #include "net-server.h"
+#ifdef INTERNAL_VERTO
+#include "verto-k5ev.h"
+#endif
+
+#include <signal.h>
 
 /* XXX */
 #define KDC5_NONET                               (-1779992062L)
 
-volatile int signal_requests_exit = 0, signal_requests_reset = 0;
-
-static void closedown_network_sockets(void);
+static int tcp_or_rpc_data_counter;
+static int max_tcp_or_rpc_data_connections = 45;
 
 /* Misc utility routines.  */
 static void
@@ -191,40 +191,40 @@ enum conn_type {
 
 /* Per-connection info.  */
 struct connection {
-    int fd;
+    void *handle;
+    const char *prog;
     enum conn_type type;
-    void (*service)(void *handle, struct connection *, const char *, int);
-    union {
-        /* Type-specific information.  */
-        struct {
-            /* connection */
-            struct sockaddr_storage addr_s;
-            socklen_t addrlen;
-            char addrbuf[56];
-            krb5_fulladdr faddr;
-            krb5_address kaddr;
-            /* incoming */
-            size_t bufsiz;
-            size_t offset;
-            char *buffer;
-            size_t msglen;
-            /* outgoing */
-            krb5_data *response;
-            unsigned char lenbuf[4];
-            sg_buf sgbuf[2];
-            sg_buf *sgp;
-            int sgnum;
-            /* crude denial-of-service avoidance support */
-            time_t start_time;
-        } tcp;
-        struct {
-            SVCXPRT *transp;
-        } rpc;
-    } u;
+
+    /* 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;
 };
 
 \f
-#define SET(TYPE) struct { TYPE *data; int n, max; }
+#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
@@ -233,13 +233,12 @@ struct connection {
     for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
 
 #define GROW_SET(set, incr, tmpptr)                                     \
-    (((int)(set.max + incr) < set.max                                   \
-      || (((size_t)((int)(set.max + incr) * sizeof(set.data[0]))        \
-           / sizeof(set.data[0]))                                       \
-          != (size_t)(set.max + incr)))                                 \
-     ? 0                          /* overflow */                        \
+    ((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,                                     \
-                          (int)(set.max + incr) * sizeof(set.data[0]))) \
+                          (set.max + incr) * sizeof(set.data[0])))      \
         ? (set.data = tmpptr, set.max += incr, 1)                       \
         : 0))
 
@@ -255,36 +254,92 @@ struct connection {
 #define FREE_SET_DATA(set)                                      \
     (free(set.data), set.data = 0, set.max = 0, set.n = 0)
 
-
-/* Set<struct connection *> connections; */
-static SET(struct connection *) connections;
-#define n_sockets       connections.n
-#define conns           connections.data
-
-/* Set<u_short> udp_port_data, tcp_port_data; */
 /*
  * 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.
  */
-static SET(unsigned short) udp_port_data, tcp_port_data;
-
 struct rpc_svc_data {
     u_short port;
     u_long prognum;
     u_long versnum;
     void (*dispatch)();
 };
-
+static SET(unsigned short) udp_port_data, tcp_port_data;
 static SET(struct rpc_svc_data) rpc_svc_data;
+static SET(verto_ev *) events;
 
-#include "cm.h"
+verto_ctx *
+loop_init(verto_ev_type types)
+{
+    types |= VERTO_EV_TYPE_IO;
+    types |= VERTO_EV_TYPE_SIGNAL;
+    types |= VERTO_EV_TYPE_TIMEOUT;
 
-static struct select_state sstate;
-static fd_set rpc_listenfds;
+#ifdef INTERNAL_VERTO
+    return verto_default_k5ev();
+#else
+    return verto_default(NULL, types);
+#endif
+}
+
+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)();
+};
+
+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();
+}
+
+static void
+free_sighup_context(verto_ctx *ctx, verto_ev *ev)
+{
+    free(verto_get_private(ev));
+}
 
 krb5_error_code
-add_udp_port(int port)
+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;
+}
+
+krb5_error_code
+loop_add_udp_port(int port)
 {
     int i;
     void *tmp;
@@ -303,7 +358,7 @@ add_udp_port(int port)
 }
 
 krb5_error_code
-add_tcp_port(int port)
+loop_add_tcp_port(int port)
 {
     int i;
     void *tmp;
@@ -322,7 +377,8 @@ add_tcp_port(int port)
 }
 
 krb5_error_code
-add_rpc_service(int port, u_long prognum, u_long versnum, void (*dispatchfn)())
+loop_add_rpc_service(int port, u_long prognum,
+                     u_long versnum, void (*dispatchfn)())
 {
     int i;
     void *tmp;
@@ -352,6 +408,8 @@ add_rpc_service(int port, u_long prognum, u_long versnum, void (*dispatchfn)())
 #include "foreachaddr.h"
 
 struct socksetup {
+    verto_ctx *ctx;
+    void *handle;
     const char *prog;
     krb5_error_code retval;
     int udp_flags;
@@ -359,18 +417,114 @@ struct socksetup {
 #define UDP_DO_IPV6 2
 };
 
-static struct connection *
+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,
-       void (*service)(void *handle, struct connection *, const char *, int))
+       verto_ev_flag flags, verto_callback callback, int addevent)
 {
     struct connection *newconn;
-    void *tmp;
 
 #ifndef _WIN32
     if (sock >= FD_SETSIZE) {
         data->retval = EMFILE;  /* XXX */
         com_err(data->prog, 0,
-                "file descriptor number %d too high", sock);
+                _("file descriptor number %d too high"), sock);
         return 0;
     }
 #endif
@@ -378,65 +532,50 @@ add_fd(struct socksetup *data, int sock, enum conn_type conntype,
     if (newconn == NULL) {
         data->retval = ENOMEM;
         com_err(data->prog, ENOMEM,
-                "cannot allocate storage for connection info");
-        return 0;
-    }
-    if (!ADD(connections, newconn, tmp)) {
-        data->retval = ENOMEM;
-        com_err(data->prog, ENOMEM, "cannot save socket info");
-        free(newconn);
+                _("cannot allocate storage for connection info"));
         return 0;
     }
-
     memset(newconn, 0, sizeof(*newconn));
+    newconn->handle = data->handle;
+    newconn->prog = data->prog;
     newconn->type = conntype;
-    newconn->fd = sock;
-    newconn->service = service;
-    return newconn;
+
+    return make_event(data->ctx, flags, callback, sock, newconn, addevent);
 }
 
-static void process_packet(void *handle, struct connection *, const char *,
-                           int);
-static void accept_tcp_connection(void *handle, struct connection *,
-                                  const char *, int);
-static void process_tcp_connection(void *handle, struct connection *,
-                                   const char *, int);
-static void accept_rpc_connection(void *handle, struct connection *,
-                                  const char *, int);
-static void process_rpc_connection(void *handle, struct connection *,
-                                   const char *, int);
-
-static struct connection *
+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_udp_fd(struct socksetup *data, int sock, int pktinfo)
 {
     return add_fd(data, sock, pktinfo ? CONN_UDP_PKTINFO : CONN_UDP,
-                  process_packet);
+                  VERTO_EV_FLAG_IO_READ |
+                  VERTO_EV_FLAG_PERSIST |
+                  VERTO_EV_FLAG_REINITIABLE,
+                  process_packet, 1);
 }
 
-static struct connection *
+static verto_ev *
 add_tcp_listener_fd(struct socksetup *data, int sock)
 {
-    return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection);
-}
-
-static struct connection *
-add_tcp_data_fd(struct socksetup *data, int sock)
-{
-    return add_fd(data, sock, CONN_TCP, process_tcp_connection);
+    return add_fd(data, sock, CONN_TCP_LISTENER,
+                  VERTO_EV_FLAG_IO_READ |
+                  VERTO_EV_FLAG_PERSIST |
+                  VERTO_EV_FLAG_REINITIABLE,
+                  accept_tcp_connection, 1);
 }
 
-static void
-delete_fd(struct connection *xconn)
+static verto_ev *
+add_tcp_read_fd(struct socksetup *data, int sock)
 {
-    struct connection *conn;
-    int i;
-
-    FOREACH_ELT(connections, i, conn)
-        if (conn == xconn) {
-            DEL(connections, i);
-            break;
-        }
-    free(xconn);
+    return add_fd(data, sock, CONN_TCP,
+                  VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+                  process_tcp_connection_read, 1);
 }
 
 /*
@@ -452,7 +591,7 @@ create_server_socket(struct socksetup *data, struct sockaddr *addr, int type)
     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",
+        com_err(data->prog, errno, _("Cannot create TCP server socket on %s"),
                 paddr(addr));
         return -1;
     }
@@ -461,7 +600,7 @@ create_server_socket(struct socksetup *data, struct sockaddr *addr, int type)
 #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",
+        com_err(data->prog, 0, _("TCP socket fd number %d (for %s) too high"),
                 sock, paddr(addr));
         return -1;
     }
@@ -469,27 +608,27 @@ create_server_socket(struct socksetup *data, struct sockaddr *addr, int type)
 
     if (setreuseaddr(sock, 1) < 0) {
         com_err(data->prog, errno,
-                "Cannot enable SO_REUSEADDR on fd %d", sock);
+                _("Cannot enable SO_REUSEADDR on fd %d"), sock);
     }
 
 #ifdef KRB5_USE_INET6
     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);
+            com_err(data->prog, errno,
+                    _("setsockopt(%d,IPV6_V6ONLY,1) failed"), sock);
         else
-            com_err(data->prog, 0, "setsockopt(%d,IPV6_V6ONLY,1) worked",
+            com_err(data->prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"),
                     sock);
 #else
-        krb5_klog_syslog(LOG_INFO, "no IPV6_V6ONLY socket option support");
+        krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support"));
 #endif /* IPV6_V6ONLY */
     }
 #endif /* KRB5_USE_INET6 */
 
     if (bind(sock, addr, socklen(addr)) == -1) {
         data->retval = errno;
-        com_err(data->prog, errno, "Cannot bind server socket on %s",
+        com_err(data->prog, errno, _("Cannot bind server socket on %s"),
                 paddr(addr));
         close(sock);
         return -1;
@@ -498,38 +637,48 @@ create_server_socket(struct socksetup *data, struct sockaddr *addr, int type)
     return sock;
 }
 
-static struct connection *
+static verto_ev *
 add_rpc_listener_fd(struct socksetup *data, struct rpc_svc_data *svc, int sock)
 {
     struct connection *conn;
-
-    conn = add_fd(data, sock, CONN_RPC_LISTENER, accept_rpc_connection);
-    if (conn == NULL)
+    verto_ev *ev;
+
+    ev = add_fd(data, sock, CONN_RPC_LISTENER,
+                VERTO_EV_FLAG_IO_READ |
+                VERTO_EV_FLAG_PERSIST |
+                VERTO_EV_FLAG_REINITIABLE,
+                accept_rpc_connection, 1);
+    if (ev == NULL)
         return NULL;
 
-    conn->u.rpc.transp = svctcp_create(sock, 0, 0);
-    if (conn->u.rpc.transp == NULL) {
-        krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %s; continuing",
+    conn = verto_get_private(ev);
+    conn->transp = svctcp_create(sock, 0, 0);
+    if (conn->transp == NULL) {
+        krb5_klog_syslog(LOG_ERR,
+                         _("Cannot create RPC service: %s; continuing"),
                          strerror(errno));
-        delete_fd(conn);
+        verto_del(ev);
         return NULL;
     }
 
-    if (!svc_register(conn->u.rpc.transp, svc->prognum, svc->versnum,
+    if (!svc_register(conn->transp, svc->prognum, svc->versnum,
                       svc->dispatch, 0)) {
-        krb5_klog_syslog(LOG_ERR, "Cannot register RPC service: %s; continuing",
+        krb5_klog_syslog(LOG_ERR,
+                         _("Cannot register RPC service: %s; continuing"),
                          strerror(errno));
-        delete_fd(conn);
+        verto_del(ev);
         return NULL;
     }
 
-    return conn;
+    return ev;
 }
 
-static struct connection *
+static verto_ev *
 add_rpc_data_fd(struct socksetup *data, int sock)
 {
-    return add_fd(data, sock, CONN_RPC, process_rpc_connection);
+    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;
@@ -563,21 +712,21 @@ setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr)
     if (sock == -1)
         return -1;
     if (listen(sock, 5) < 0) {
-        com_err(data->prog, errno, "Cannot listen on TCP server socket on %s",
-                paddr(addr));
+        com_err(data->prog, errno,
+                _("Cannot listen on TCP server socket on %s"), paddr(addr));
         close(sock);
         return -1;
     }
     if (setnbio(sock)) {
         com_err(data->prog, errno,
-                "cannot set listening tcp socket on %s non-blocking",
+                _("cannot set listening tcp socket on %s non-blocking"),
                 paddr(addr));
         close(sock);
         return -1;
     }
     if (setnolinger(sock)) {
-        com_err(data->prog, errno, "disabling SO_LINGER on TCP socket on %s",
-                paddr(addr));
+        com_err(data->prog, errno,
+                _("disabling SO_LINGER on TCP socket on %s"), paddr(addr));
         close(sock);
         return -1;
     }
@@ -639,10 +788,7 @@ setup_tcp_listener_ports(struct socksetup *data)
             if (add_tcp_listener_fd(data, s4) == NULL)
                 close(s4);
             else {
-                FD_SET(s4, &sstate.rfds);
-                if (s4 >= sstate.max)
-                    sstate.max = s4 + 1;
-                krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
+                krb5_klog_syslog(LOG_INFO, _("listening on fd %d: tcp %s"),
                                  s4, paddr((struct sockaddr *)&sin4));
             }
         }
@@ -652,15 +798,12 @@ setup_tcp_listener_ports(struct socksetup *data)
                 close(s6);
                 s6 = -1;
             } else {
-                FD_SET(s6, &sstate.rfds);
-                if (s6 >= sstate.max)
-                    sstate.max = s6 + 1;
-                krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
+                krb5_klog_syslog(LOG_INFO, _("listening on fd %d: tcp %s"),
                                  s6, paddr((struct sockaddr *)&sin6));
             }
             if (s4 < 0)
                 krb5_klog_syslog(LOG_INFO,
-                                 "assuming IPv6 socket accepts IPv4");
+                                 _("assuming IPv6 socket accepts IPv4"));
         }
 #endif
     }
@@ -706,13 +849,9 @@ setup_rpc_listener_ports(struct socksetup *data)
 
         if (add_rpc_listener_fd(data, &svc, s4) == NULL)
             close(s4);
-        else {
-            FD_SET(s4, &sstate.rfds);
-            if (s4 >= sstate.max)
-                sstate.max = s4 + 1;
-            krb5_klog_syslog(LOG_INFO, "listening on fd %d: rpc %s",
+        else
+            krb5_klog_syslog(LOG_INFO, _("listening on fd %d: rpc %s"),
                              s4, paddr((struct sockaddr *)&sin4));
-        }
 
 #ifdef KRB5_USE_INET6
         if (ipv6_enabled()) {
@@ -724,18 +863,13 @@ setup_rpc_listener_ports(struct socksetup *data)
 
             if (add_rpc_listener_fd(data, &svc, s6) == NULL)
                 close(s6);
-            else {
-                FD_SET(s6, &sstate.rfds);
-                if (s6 >= sstate.max)
-                    sstate.max = s6 + 1;
-                krb5_klog_syslog(LOG_INFO, "listening on fd %d: rpc %s",
+            else
+                krb5_klog_syslog(LOG_INFO, _("listening on fd %d: rpc %s"),
                                  s6, paddr((struct sockaddr *)&sin6));
-            }
         }
 #endif
     }
-    FD_ZERO(&rpc_listenfds);
-    rpc_listenfds = svc_fdset;
+
     return 0;
 }
 
@@ -817,22 +951,20 @@ setup_udp_port_1(struct socksetup *data, struct sockaddr *addr,
         if (pktinfo) {
             r = set_pktinfo(sock, addr->sa_family);
             if (r) {
-                com_err(data->prog, r, "Cannot request packet info for "
-                        "udp socket address %s port %d", haddrbuf, port);
+                com_err(data->prog, r,
+                        _("Cannot request packet info for udp socket address "
+                          "%s port %d"), haddrbuf, port);
                 close(sock);
                 return 1;
             }
         }
-        krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s%s", sock,
-                          paddr((struct sockaddr *)addr),
-                          pktinfo ? " (pktinfo)" : "");
+        krb5_klog_syslog(LOG_INFO, _("listening on fd %d: udp %s%s"), sock,
+                         paddr((struct sockaddr *)addr),
+                         pktinfo ? " (pktinfo)" : "");
         if (add_udp_fd (data, sock, pktinfo) == 0) {
             close(sock);
             return 1;
         }
-        FD_SET (sock, &sstate.rfds);
-        if (sock >= sstate.max)
-            sstate.max = sock + 1;
     }
     return 0;
 }
@@ -866,7 +998,7 @@ setup_udp_port(void *P_data, struct sockaddr *addr)
         {
             static int first = 1;
             if (first) {
-                krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses");
+                krb5_klog_syslog(LOG_INFO, _("skipping local ipv6 addresses"));
                 first = 0;
             }
             return 0;
@@ -886,9 +1018,9 @@ setup_udp_port(void *P_data, struct sockaddr *addr)
         return 0;
 #endif
     default:
-        krb5_klog_syslog (LOG_INFO,
-                          "skipping unrecognized local address family %d",
-                          addr->sa_family);
+        krb5_klog_syslog(LOG_INFO,
+                         _("skipping unrecognized local address family %d"),
+                         addr->sa_family);
         return 0;
     }
     return setup_udp_port_1(data, addr, haddrbuf, 0);
@@ -940,8 +1072,6 @@ scan_for_newlines:
 }
 #endif
 
-static int network_reconfiguration_needed = 0;
-
 #ifdef HAVE_STRUCT_RT_MSGHDR
 #include <net/route.h>
 
@@ -970,13 +1100,88 @@ rtm_type_name(int type)
 }
 
 static void
-process_routing_update(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+do_network_reconfig(verto_ctx *ctx, verto_ev *ev)
+{
+    struct connection *conn = verto_get_private(ev);
+    assert(loop_setup_network(ctx, conn->handle, conn->prog) == 0);
+}
+
+static int
+routing_update_needed(struct rt_msghdr *rtm)
 {
-    int n_read;
+    switch (rtm->rtm_type) {
+    case RTM_ADD:
+    case RTM_DELETE:
+    case RTM_NEWADDR:
+    case RTM_DELADDR:
+    case RTM_IFINFO:
+    case RTM_OLDADD:
+    case RTM_OLDDEL:
+        /*
+         * Some flags indicate routing table updates that don't
+         * indicate local address changes.  They may come from
+         * redirects, or ARP, etc.
+         *
+         * This set of symbols is just an initial guess based on
+         * some messages observed in real life; working out which
+         * other flags also indicate messages we should ignore,
+         * and which flags are portable to all system and thus
+         * don't need to be conditionalized, is left as a future
+         * exercise.
+         */
+#ifdef RTF_DYNAMIC
+        if (rtm->rtm_flags & RTF_DYNAMIC)
+            break;
+#endif
+#ifdef RTF_CLONED
+        if (rtm->rtm_flags & RTF_CLONED)
+            break;
+#endif
+#ifdef RTF_LLINFO
+        if (rtm->rtm_flags & RTF_LLINFO)
+            break;
+#endif
+#if 0
+        krb5_klog_syslog(LOG_DEBUG,
+                         "network reconfiguration message (%s) received",
+                         rtm_type_name(rtm->rtm_type));
+#endif
+        return 1;
+    case RTM_RESOLVE:
+#ifdef RTM_NEWMADDR
+    case RTM_NEWMADDR:
+    case RTM_DELMADDR:
+#endif
+    case RTM_MISS:
+    case RTM_REDIRECT:
+    case RTM_LOSING:
+    case RTM_GET:
+        /* Not interesting.  */
+#if 0
+        krb5_klog_syslog(LOG_DEBUG, "routing msg not interesting");
+#endif
+        break;
+    default:
+        krb5_klog_syslog(LOG_INFO,
+                         _("unhandled routing message type %d, "
+                           "will reconfigure just for the fun of it"),
+                         rtm->rtm_type);
+        return 1;
+    }
+
+    return 0;
+}
+
+static void
+process_routing_update(verto_ctx *ctx, verto_ev *ev)
+{
+    int n_read, fd;
     struct rt_msghdr rtm;
+    struct connection *conn;
 
-    while ((n_read = read(conn->fd, &rtm, sizeof(rtm))) > 0) {
+    fd = verto_get_fd(ev);
+    conn = verto_get_private(ev);
+    while ((n_read = read(fd, &rtm, sizeof(rtm))) > 0) {
         if (n_read < sizeof(rtm)) {
             /* Quick hack to figure out if the interesting
                fields are present in a short read.
@@ -989,14 +1194,14 @@ process_routing_update(void *handle, struct connection *conn, const char *prog,
                 n_read < RS(rtm_version) ||
                 n_read < RS(rtm_msglen)) {
                 krb5_klog_syslog(LOG_ERR,
-                                 "short read (%d/%d) from routing socket",
+                                 _("short read (%d/%d) from routing socket"),
                                  n_read, (int) sizeof(rtm));
                 return;
             }
         }
 #if 0
         krb5_klog_syslog(LOG_INFO,
-                         "got routing msg type %d(%s) v%d",
+                         _("got routing msg type %d(%s) v%d"),
                          rtm.rtm_type, rtm_type_name(rtm.rtm_type),
                          rtm.rtm_version);
 #endif
@@ -1005,113 +1210,74 @@ process_routing_update(void *handle, struct connection *conn, const char *prog,
                thrown away?  */
         } else if (rtm.rtm_msglen != n_read) {
             krb5_klog_syslog(LOG_ERR,
-                             "read %d from routing socket but msglen is %d",
+                             _("read %d from routing socket but msglen is %d"),
                              n_read, rtm.rtm_msglen);
         }
-        switch (rtm.rtm_type) {
-        case RTM_ADD:
-        case RTM_DELETE:
-        case RTM_NEWADDR:
-        case RTM_DELADDR:
-        case RTM_IFINFO:
-        case RTM_OLDADD:
-        case RTM_OLDDEL:
-            /*
-             * Some flags indicate routing table updates that don't
-             * indicate local address changes.  They may come from
-             * redirects, or ARP, etc.
-             *
-             * This set of symbols is just an initial guess based on
-             * some messages observed in real life; working out which
-             * other flags also indicate messages we should ignore,
-             * and which flags are portable to all system and thus
-             * don't need to be conditionalized, is left as a future
-             * exercise.
-             */
-#ifdef RTF_DYNAMIC
-            if (rtm.rtm_flags & RTF_DYNAMIC)
-                break;
-#endif
-#ifdef RTF_CLONED
-            if (rtm.rtm_flags & RTF_CLONED)
-                break;
-#endif
-#ifdef RTF_LLINFO
-            if (rtm.rtm_flags & RTF_LLINFO)
-                break;
-#endif
-#if 0
-            krb5_klog_syslog(LOG_DEBUG,
-                             "network reconfiguration message (%s) received",
-                             rtm_type_name(rtm.rtm_type));
-#endif
-            network_reconfiguration_needed = 1;
-            break;
-        case RTM_RESOLVE:
-#ifdef RTM_NEWMADDR
-        case RTM_NEWMADDR:
-        case RTM_DELMADDR:
-#endif
-        case RTM_MISS:
-        case RTM_REDIRECT:
-        case RTM_LOSING:
-        case RTM_GET:
-            /* Not interesting.  */
-#if 0
-            krb5_klog_syslog(LOG_DEBUG, "routing msg not interesting");
-#endif
-            break;
-        default:
-            krb5_klog_syslog(LOG_INFO, "unhandled routing message type %d, "
-                             "will reconfigure just for the fun of it",
-                             rtm.rtm_type);
-            network_reconfiguration_needed = 1;
-            break;
+
+        if (routing_update_needed(&rtm)) {
+            /* Ideally we would use idle here instead of timeout. However, idle
+             * is not universally supported yet in all backends. So let's just
+             * use timeout for now to avoid locking into a loop. */
+            ev = verto_add_timeout(ctx, VERTO_EV_FLAG_NONE,
+                                   do_network_reconfig, 0);
+            verto_set_private(ev, conn, NULL);
+            assert(ev);
         }
     }
 }
+#endif
 
-static void
-setup_routing_socket(struct socksetup *data)
+krb5_error_code
+loop_setup_routing_socket(verto_ctx *ctx, void *handle, const char *progname)
 {
-    int sock = socket(PF_ROUTE, SOCK_RAW, 0);
+#ifdef HAVE_STRUCT_RT_MSGHDR
+    struct socksetup data;
+    int sock;
+
+    data.ctx = ctx;
+    data.handle = handle;
+    data.prog = progname;
+    data.retval = 0;
+
+    sock = socket(PF_ROUTE, SOCK_RAW, 0);
     if (sock < 0) {
         int e = errno;
-        krb5_klog_syslog(LOG_INFO, "couldn't set up routing socket: %s",
+        krb5_klog_syslog(LOG_INFO, _("couldn't set up routing socket: %s"),
                          strerror(e));
     } else {
-        krb5_klog_syslog(LOG_INFO, "routing socket is fd %d", sock);
-        add_fd(data, sock, CONN_ROUTING, process_routing_update);
+        krb5_klog_syslog(LOG_INFO, _("routing socket is fd %d"), sock);
         setnbio(sock);
-        FD_SET(sock, &sstate.rfds);
+        add_fd(&data, sock, CONN_ROUTING,
+               VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST,
+               process_routing_update, 0);
     }
-}
 #endif
+    return 0;
+}
 
 /* XXX */
-extern int krb5int_debug_sendto_kdc;
 extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
 
 krb5_error_code
-setup_network(void *handle, const char *prog, int no_reconfig)
+loop_setup_network(verto_ctx *ctx, void *handle, const char *prog)
 {
     struct socksetup setup_data;
+    verto_ev *ev;
+    int i;
 
-    FD_ZERO(&sstate.rfds);
-    FD_ZERO(&sstate.wfds);
-    FD_ZERO(&sstate.xfds);
-    sstate.max = 0;
-
-    /* krb5int_debug_sendto_kdc = 1; */
     krb5int_sendtokdc_debug_handler = klog_handler;
 
+    /* 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;
-    krb5_klog_syslog (LOG_INFO, "setting up network...");
-#ifdef HAVE_STRUCT_RT_MSGHDR
-    if (!no_reconfig)
-        setup_routing_socket(&setup_data);
-#endif
+    krb5_klog_syslog(LOG_INFO, _("setting up network..."));
+
     /*
      * To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
      * so we might need only one UDP socket; fall back to binding
@@ -1127,9 +1293,9 @@ setup_network(void *handle, const char *prog, int no_reconfig)
     }
     setup_tcp_listener_ports(&setup_data);
     setup_rpc_listener_ports(&setup_data);
-    krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
-    if (n_sockets == 0) {
-        com_err(prog, 0, "no sockets set up?");
+    krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);
+    if (events.n == 0) {
+        com_err(prog, 0, _("no sockets set up?"));
         exit (1);
     }
 
@@ -1358,30 +1524,99 @@ send_to_from(int s, void *buf, size_t len, int flags,
 #endif
 }
 
-static void
-process_packet(void *handle, struct connection *conn, const char *prog,
-               int selflags)
-{
-    int cc;
-    socklen_t saddr_len, daddr_len;
-    krb5_fulladdr faddr;
-    krb5_error_code retval;
-    struct sockaddr_storage saddr, daddr;
+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;
+    union aux_addressing_info auxaddr;
     krb5_data request;
-    krb5_data *response;
     char pktbuf[MAX_DGRAM_SIZE];
-    int port_fd = conn->fd;
-    union aux_addressing_info auxaddr;
+};
+
+static void
+process_packet_response(void *arg, krb5_error_code code, krb5_data *response)
+{
+    struct udp_dispatch_state *state = arg;
+    int cc;
 
-    response = NULL;
-    saddr_len = sizeof(saddr);
-    daddr_len = sizeof(daddr);
-    memset(&auxaddr, 0, sizeof(auxaddr));
-    cc = recv_from_to(port_fd, pktbuf, sizeof(pktbuf), 0,
-                      (struct sockaddr *)&saddr, &saddr_len,
-                      (struct sockaddr *)&daddr, &daddr_len,
-                      &auxaddr);
+    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
             /*
@@ -1391,131 +1626,102 @@ process_packet(void *handle, struct connection *conn, const char *prog,
              */
             && errno != ECONNREFUSED
         )
-            com_err(prog, errno, "while receiving from network");
+            com_err(conn->prog, errno, _("while receiving from network"));
+        free(state);
+        return;
+    }
+    if (!cc) { /* zero-length packet? */
+        free(state);
         return;
     }
-    if (!cc)
-        return;         /* zero-length packet? */
 
 #if 0
-    if (daddr_len > 0) {
+    if (state->daddr_len > 0) {
         char addrbuf[100];
-        if (getnameinfo(ss2sa(&daddr), daddr_len, addrbuf, sizeof(addrbuf),
+        if (getnameinfo(ss2sa(&state->daddr), state->daddr_len,
+                        addrbuf, sizeof(addrbuf),
                         0, 0, NI_NUMERICHOST))
             strlcpy(addrbuf, "?", sizeof(addrbuf));
-        com_err(prog, 0, "pktinfo says local addr is %s", addrbuf);
+        com_err(conn->prog, 0, _("pktinfo says local addr is %s"), addrbuf);
     }
 #endif
 
-    if (daddr_len == 0 && conn->type == CONN_UDP) {
+    if (state->daddr_len == 0 && conn->type == CONN_UDP) {
         /*
          * If the PKTINFO option isn't set, this socket should be bound to a
          * specific local address.  This info probably should've been saved in
          * our socket data structure at setup time.
          */
-        daddr_len = sizeof(daddr);
-        if (getsockname(port_fd, (struct sockaddr *)&daddr, &daddr_len) != 0)
-            daddr_len = 0;
+            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. */
     }
 
-    request.length = cc;
-    request.data = pktbuf;
-    faddr.address = &addr;
-    init_addr(&faddr, ss2sa(&saddr));
+    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. */
-    retval = dispatch(handle, ss2sa(&daddr), &faddr, &request, &response, 0);
-    if (retval) {
-        com_err(prog, retval, "while dispatching (udp)");
-        return;
-    }
-    if (response == NULL)
-        return;
-    cc = send_to_from(port_fd, response->data, (socklen_t) response->length, 0,
-                      (struct sockaddr *)&saddr, saddr_len,
-                      (struct sockaddr *)&daddr, daddr_len,
-                      &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;
-        krb5_free_data(get_context(handle), response);
-        if (getnameinfo((struct sockaddr *)&daddr, daddr_len,
-                        daddrbuf, sizeof(daddrbuf), 0, 0,
-                        NI_NUMERICHOST) != 0) {
-            strlcpy(daddrbuf, "?", sizeof(daddrbuf));
-        }
-        if (getnameinfo((struct sockaddr *)&saddr, saddr_len,
-                        saddrbuf, sizeof(saddrbuf), sportbuf, sizeof(sportbuf),
-                        NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-            strlcpy(saddrbuf, "?", sizeof(saddrbuf));
-            strlcpy(sportbuf, "?", sizeof(sportbuf));
-        }
-        com_err(prog, e, "while sending reply to %s/%s from %s",
-                saddrbuf, sportbuf, daddrbuf);
-        return;
-    }
-    if ((size_t)cc != response->length) {
-        com_err(prog, 0, "short reply write %d vs %d\n",
-                response->length, cc);
-    }
-    krb5_free_data(get_context(handle), response);
-    return;
+    dispatch(state->handle, ss2sa(&state->daddr), &state->faddr,
+             &state->request, 0, process_packet_response, state);
 }
 
-static int tcp_or_rpc_data_counter;
-static int max_tcp_or_rpc_data_connections = 45;
-
-static void kill_tcp_or_rpc_connection(void *, struct connection *,
-                                       int isForcedClose);
-
 static int
-kill_lru_tcp_or_rpc_connection(void *handle, struct connection *newconn)
+kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
 {
-    struct connection *oldest_tcp = NULL;
-    struct connection *c;
+    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");
+    krb5_klog_syslog(LOG_INFO, _("too many connections"));
 
-    FOREACH_ELT (connections, i, c) {
-        if (c->type != CONN_TCP && c->type != CONN_RPC)
+    FOREACH_ELT (events, i, ev) {
+        if (ev == newev)
             continue;
-        if (c == newconn)
+
+        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", c->fd,
-                         c->u.tcp.start_time);
+        krb5_klog_syslog(LOG_INFO, "fd %d started at %ld",
+                         verto_get_fd(oldest_ev),
+                         c->start_time);
 #endif
-        if (oldest_tcp == NULL
-            || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
-            oldest_tcp = c;
+        if (oldest_c == NULL
+            || oldest_c->start_time > c->start_time) {
+            oldest_ev = ev;
+            oldest_c = c;
+        }
     }
-    if (oldest_tcp != NULL) {
-        krb5_klog_syslog(LOG_INFO, "dropping %s fd %d from %s",
+    if (oldest_c != NULL) {
+        krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
                          c->type == CONN_RPC ? "rpc" : "tcp",
-                         oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
-        fd = oldest_tcp->fd;
-        kill_tcp_or_rpc_connection(handle, oldest_tcp, 1);
+                         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(void *handle, struct connection *conn, const char *prog,
-                      int selflags)
+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;
+    struct connection *newconn, *conn;
     char tmpbuf[10];
+    verto_ev *newev;
 
-    s = accept(conn->fd, addr, &addrlen);
+    conn = verto_get_private(ev);
+    s = accept(verto_get_fd(ev), addr, &addrlen);
     if (s < 0)
         return;
     set_cloexec_fd(s);
@@ -1527,22 +1733,27 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
 #endif
     setnbio(s), setnolinger(s), setkeepalive(s);
 
-    sockdata.prog = prog;
+    sockdata.ctx = ctx;
+    sockdata.handle = conn->handle;
+    sockdata.prog = conn->prog;
     sockdata.retval = 0;
 
-    newconn = add_tcp_data_fd(&sockdata, s);
-    if (newconn == NULL)
+    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->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
+                    newconn->addrbuf, sizeof(newconn->addrbuf),
                     tmpbuf, sizeof(tmpbuf),
                     NI_NUMERICHOST | NI_NUMERICSERV))
-        strlcpy(newconn->u.tcp.addrbuf, "???", sizeof(newconn->u.tcp.addrbuf));
+        strlcpy(newconn->addrbuf, "???", sizeof(newconn->addrbuf));
     else {
         char *p, *end;
-        p = newconn->u.tcp.addrbuf;
-        end = p + sizeof(newconn->u.tcp.addrbuf);
+        p = newconn->addrbuf;
+        end = p + sizeof(newconn->addrbuf);
         p += strlen(p);
         if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
             *p++ = '.';
@@ -1551,469 +1762,343 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog,
     }
 #if 0
     krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s",
-                     s, newconn->u.tcp.addrbuf);
+                     s, newconn->addrbuf);
 #endif
 
-    newconn->u.tcp.addr_s = addr_s;
-    newconn->u.tcp.addrlen = addrlen;
-    newconn->u.tcp.bufsiz = 1024 * 1024;
-    newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz);
-    newconn->u.tcp.start_time = time(0);
+    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(handle, newconn);
+        kill_lru_tcp_or_rpc_connection(conn->handle, newev);
 
-    if (newconn->u.tcp.buffer == 0) {
-        com_err(prog, errno, "allocating buffer for new TCP session from %s",
-                newconn->u.tcp.addrbuf);
-        delete_fd(newconn);
-        close(s);
-        tcp_or_rpc_data_counter--;
+    if (newconn->buffer == 0) {
+        com_err(conn->prog, errno,
+                _("allocating buffer for new TCP session from %s"),
+                newconn->addrbuf);
+        verto_del(newev);
         return;
     }
-    newconn->u.tcp.offset = 0;
-    newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
-    init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
-    SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
-    SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
-
-    FD_SET(s, &sstate.rfds);
-    if (sstate.max <= s)
-        sstate.max = s + 1;
+    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
-kill_tcp_or_rpc_connection(void *handle, struct connection *conn,
-                           int isForcedClose)
+process_tcp_response(void *arg, krb5_error_code code, krb5_data *response)
 {
-    assert(conn->type == CONN_TCP || conn->type == CONN_RPC);
-    assert(conn->fd != -1);
-
-    if (conn->u.tcp.response)
-        krb5_free_data(get_context(handle), conn->u.tcp.response);
-    if (conn->u.tcp.buffer)
-        free(conn->u.tcp.buffer);
-    FD_CLR(conn->fd, &sstate.rfds);
-    FD_CLR(conn->fd, &sstate.wfds);
-    if (sstate.max == conn->fd + 1)
-        while (sstate.max > 0
-               && ! FD_ISSET(sstate.max-1, &sstate.rfds)
-               && ! FD_ISSET(sstate.max-1, &sstate.wfds)
-               /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
-        )
-            sstate.max--;
-
-    /* In the non-forced case, the RPC runtime will close the descriptor for
-     * us. */
-    if (conn->type == CONN_TCP || isForcedClose) {
-        close(conn->fd);
-    }
-
-    /* For RPC connections, call into RPC runtime to flush out any internal
-     * state. */
-    if (conn->type == CONN_RPC && isForcedClose) {
-        fd_set fds;
-
-        FD_ZERO(&fds);
-        FD_SET(conn->fd, &fds);
-
-        svc_getreqset(&fds);
-
-        if (FD_ISSET(conn->fd, &svc_fdset)) {
-            krb5_klog_syslog(LOG_ERR,
-                             "descriptor %d closed but still in svc_fdset",
-                             conn->fd);
-        }
+    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;
     }
 
-    conn->fd = -1;
-    delete_fd(conn);
+kill_tcp_connection:
     tcp_or_rpc_data_counter--;
+    free_connection(state->conn);
+    close(state->sock);
+    free(state);
 }
 
-static void
-queue_tcp_outgoing_response(struct connection *conn)
+/* Creates the tcp_dispatch_state and deletes the verto event. */
+static struct tcp_dispatch_state *
+prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)
 {
-    store_32_be(conn->u.tcp.response->length, conn->u.tcp.lenbuf);
-    SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
-           conn->u.tcp.response->length);
-    conn->u.tcp.sgp = conn->u.tcp.sgbuf;
-    conn->u.tcp.sgnum = 2;
-    FD_SET(conn->fd, &sstate.wfds);
+    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(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
 {
-    int isForcedClose = 1; /* not used now, but for completeness */
+    struct tcp_dispatch_state *state = NULL;
+    struct connection *conn = NULL;
+    ssize_t nread;
+    size_t len;
 
-    if (selflags & SSF_WRITE) {
-        ssize_t nwrote;
-        SOCKET_WRITEV_TEMP tmp;
+    conn = verto_get_private(ev);
 
-        nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
-                               tmp);
-        if (nwrote < 0) {
-            goto kill_tcp_connection;
-        }
-        if (nwrote == 0) {
-            /* eof */
-            isForcedClose = 0;
+    /*
+     * 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;
-        }
-        while (nwrote) {
-            sg_buf *sgp = conn->u.tcp.sgp;
-            if ((size_t)nwrote < SG_LEN(sgp)) {
-                SG_ADVANCE(sgp, (size_t)nwrote);
-                nwrote = 0;
-            } else {
-                nwrote -= SG_LEN(sgp);
-                conn->u.tcp.sgp++;
-                conn->u.tcp.sgnum--;
-                if (conn->u.tcp.sgnum == 0 && nwrote != 0)
-                    abort();
-            }
-        }
-        if (conn->u.tcp.sgnum == 0) {
-            /*
-             * 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.
-             */
-            isForcedClose = 0;
+        if (nread == 0) /* eof */
             goto kill_tcp_connection;
-        }
-    } else if (selflags & SSF_READ) {
-        /*
-         * 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.
-         */
-        size_t len;
-        ssize_t nread;
-        if (conn->u.tcp.offset < 4) {
-            /* msglen has not been computed.  XXX Doing at least two reads
-             * here, letting the kernel worry about buffering. */
-            len = 4 - conn->u.tcp.offset;
-            nread = SOCKET_READ(conn->fd,
-                                conn->u.tcp.buffer + conn->u.tcp.offset, len);
-            if (nread < 0)
-                /* error */
-                goto kill_tcp_connection;
-            if (nread == 0)
-                /* eof */
-                goto kill_tcp_connection;
-            conn->u.tcp.offset += nread;
-            if (conn->u.tcp.offset == 4) {
-                unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
-                conn->u.tcp.msglen = load_32_be(p);
-                if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
-                    krb5_error_code err;
-                    /* Message too big. */
-                    krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, "
-                                     "cap is %lu", conn->u.tcp.addrbuf,
-                                     (unsigned long) conn->u.tcp.msglen,
-                                     (unsigned long) conn->u.tcp.bufsiz - 4);
-                    /* XXX Should return an error.  */
-                    err = make_toolong_error (handle, &conn->u.tcp.response);
-                    if (err) {
-                        krb5_klog_syslog(LOG_ERR, "error constructing "
-                                         "KRB_ERR_FIELD_TOOLONG error! %s",
-                                         error_message(err));
-                        goto kill_tcp_connection;
-                    }
-                    goto have_response;
+        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. */
-            krb5_data request;
-            krb5_error_code err;
-            struct sockaddr_storage local_saddr;
-            socklen_t local_saddrlen = sizeof(local_saddr);
-            struct sockaddr *local_saddrp = NULL;
-
-            len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
-            nread = SOCKET_READ(conn->fd,
-                                conn->u.tcp.buffer + conn->u.tcp.offset, len);
-            if (nread < 0)      /* error */
-                goto kill_tcp_connection;
-            if (nread == 0)     /* eof */
-                goto kill_tcp_connection;
-            conn->u.tcp.offset += nread;
-            if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
-                return;
-            /* Have a complete message, and exactly one message. */
-            request.length = conn->u.tcp.msglen;
-            request.data = conn->u.tcp.buffer + 4;
-
-            if (getsockname(conn->fd, ss2sa(&local_saddr),
-                            &local_saddrlen) == 0)
-                local_saddrp = ss2sa(&local_saddr);
-
-            err = dispatch(handle, local_saddrp, &conn->u.tcp.faddr,
-                           &request, &conn->u.tcp.response, 1);
-            if (err) {
-                com_err(prog, err, "while dispatching (tcp)");
-                goto kill_tcp_connection;
-            }
-        have_response:
-            queue_tcp_outgoing_response(conn);
-            FD_CLR(conn->fd, &sstate.rfds);
         }
-    } else
-        abort();
-
-    return;
+    } 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;
 
-kill_tcp_connection:
-    kill_tcp_or_rpc_connection(handle, conn, isForcedClose);
-}
+        /* Have a complete message, and exactly one message. */
+        state = prepare_for_dispatch(ctx, ev);
+        if (!state)
+            goto kill_tcp_connection;
 
-static void
-service_conn(void *handle, struct connection *conn, const char *prog,
-             int selflags)
-{
-    conn->service(handle, conn, prog, selflags);
-}
+        state->request.length = conn->msglen;
+        state->request.data = conn->buffer + 4;
 
-static int
-getcurtime(struct timeval *tvp)
-{
-#ifdef _WIN32
-    struct _timeb tb;
-    _ftime(&tb);
-    tvp->tv_sec = tb.time;
-    tvp->tv_usec = tb.millitm * 1000;
-    return 0;
-#else
-    return gettimeofday(tvp, 0) ? errno : 0;
-#endif
-}
+        if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr),
+                        &local_saddrlen) == 0)
+            local_saddrp = ss2sa(&state->local_saddr);
 
-krb5_error_code
-listen_and_process(void *handle, const char *prog, void (*reset)(void))
-{
-    int nfound;
-    /* This struct contains 3 fd_set objects; on some platforms, they
-       can be rather large.  Making this static avoids putting all
-       that junk on the stack.  */
-    static struct select_state sout;
-    int i, sret, netchanged = 0;
-    krb5_error_code err;
-
-    if (conns == (struct connection **) NULL)
-        return KDC5_NONET;
-
-    while (!signal_requests_exit) {
-        if (signal_requests_reset) {
-            krb5_klog_reopen(get_context(handle));
-            reset();
-            signal_requests_reset = 0;
-        }
+        dispatch(state->conn->handle, local_saddrp, &conn->faddr,
+                 &state->request, 1, process_tcp_response, state);
+    }
 
-        if (network_reconfiguration_needed) {
-            /* No point in re-logging what we've just logged. */
-            if (netchanged == 0)
-                krb5_klog_syslog(LOG_INFO, "network reconfiguration needed");
-            /* It might be tidier to add a timer-callback interface to the
-             * control loop, but for this one use, it's not a big deal. */
-            err = getcurtime(&sstate.end_time);
-            if (err) {
-                com_err(prog, err, "while getting the time");
-                continue;
-            }
-            sstate.end_time.tv_sec += 3;
-            netchanged = 1;
-        } else
-            sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
+    return;
 
-        err = krb5int_cm_call_select(&sstate, &sout, &sret);
-        if (err) {
-            if (err != EINTR)
-                com_err(prog, err, "while selecting for network input(1)");
-            continue;
-        }
-        if (sret == 0 && netchanged) {
-            network_reconfiguration_needed = 0;
-            closedown_network_sockets();
-            err = setup_network(handle, prog, 0);
-            if (err) {
-                com_err(prog, err, "while reinitializing network");
-                return err;
-            }
-            netchanged = 0;
-        }
-        if (sret == -1) {
-            if (errno != EINTR)
-                com_err(prog, errno, "while selecting for network input(2)");
-            continue;
-        }
-        nfound = sret;
-        for (i=0; i<n_sockets && nfound > 0; i++) {
-            int sflags = 0;
-            if (conns[i]->fd < 0)
-                abort();
-            if (FD_ISSET(conns[i]->fd, &sout.rfds))
-                sflags |= SSF_READ, nfound--;
-            if (FD_ISSET(conns[i]->fd, &sout.wfds))
-                sflags |= SSF_WRITE, nfound--;
-            if (sflags)
-                service_conn(handle, conns[i], prog, sflags);
-        }
-    }
-    krb5_klog_syslog(LOG_INFO, "shutdown signal received");
-    return 0;
+kill_tcp_connection:
+    verto_del(ev);
 }
 
 static void
-closedown_network_sockets()
+process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev)
 {
-    int i;
     struct connection *conn;
+    SOCKET_WRITEV_TEMP tmp;
+    ssize_t nwrote;
+    int sock;
 
-    if (conns == (struct connection **) NULL)
-        return;
-
-    FOREACH_ELT (connections, i, conn) {
-        if (conn->fd >= 0) {
-            krb5_klog_syslog(LOG_INFO, "closing down fd %d", conn->fd);
-            (void) close(conn->fd);
-            if (conn->type == CONN_RPC) {
-                fd_set fds;
-
-                FD_ZERO(&fds);
-                FD_SET(conn->fd, &fds);
+    conn = verto_get_private(ev);
+    sock = verto_get_fd(ev);
 
-                svc_getreqset(&fds);
+    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 (conn->type == CONN_RPC_LISTENER) {
-            if (conn->u.rpc.transp != NULL)
-                svc_destroy(conn->u.rpc.transp);
-        }
-        DEL (connections, i);
-        /*
-         * There may also be per-connection data in the tcp structure
-         * (tcp.buffer, tcp.response) that we're not freeing here.  That should
-         * only happen if we quit with a connection in progress.
-         */
-        free(conn);
+
+        /* 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
-closedown_network()
+loop_free(verto_ctx *ctx)
 {
-    closedown_network_sockets();
-    FREE_SET_DATA(connections);
+    verto_free(ctx);
+    FREE_SET_DATA(events);
     FREE_SET_DATA(udp_port_data);
     FREE_SET_DATA(tcp_port_data);
     FREE_SET_DATA(rpc_svc_data);
 }
 
+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(void *handle, struct connection *conn, const char *prog,
-                      int selflags)
+accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
 {
     struct socksetup sockdata;
+    struct connection *conn;
     fd_set fds;
     register int s;
 
-    assert(selflags & SSF_READ);
-
-    if ((selflags & SSF_READ) == 0)
-        return;
+    conn = verto_get_private(ev);
 
-    sockdata.prog = prog;
+    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(conn->fd, &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++) {
-        /* sstate.rfds |= svc_fdset & ~(rpc_listenfds | sstate.rfds) */
-        if (FD_ISSET(s, &svc_fdset) && !FD_ISSET(s, &rpc_listenfds)
-            && !FD_ISSET(s, &sstate.rfds)) {
-            struct connection *newconn;
-            struct sockaddr_storage addr_s;
-            struct sockaddr *addr = (struct sockaddr *)&addr_s;
-            socklen_t addrlen = sizeof(addr_s);
-            char tmpbuf[10];
-
-            newconn = add_rpc_data_fd(&sockdata, s);
-            if (newconn == NULL)
-                continue;
-
-            set_cloexec_fd(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);
+        setnbio(s), setnolinger(s), setkeepalive(s);
 #endif
 
-            if (getpeername(s, addr, &addrlen) ||
-                getnameinfo(addr, addrlen,
-                            newconn->u.tcp.addrbuf,
-                            sizeof(newconn->u.tcp.addrbuf),
-                            tmpbuf, sizeof(tmpbuf),
-                            NI_NUMERICHOST | NI_NUMERICSERV)) {
-                strlcpy(newconn->u.tcp.addrbuf, "???",
-                        sizeof(newconn->u.tcp.addrbuf));
-            } else {
-                char *p, *end;
-                p = newconn->u.tcp.addrbuf;
-                end = p + sizeof(newconn->u.tcp.addrbuf);
-                p += strlen(p);
-                if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
-                    *p++ = '.';
-                    strlcpy(p, tmpbuf, end - p);
-                }
+        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->u.tcp.addrbuf);
+        krb5_klog_syslog(LOG_INFO, _("accepted RPC connection on socket %d "
+                         "from %s"), s, newconn->addrbuf);
 #endif
 
-            newconn->u.tcp.addr_s = addr_s;
-            newconn->u.tcp.addrlen = addrlen;
-            newconn->u.tcp.start_time = time(0);
+        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(handle, newconn);
+        if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
+            kill_lru_tcp_or_rpc_connection(newconn->handle, newev);
 
-            newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
-            init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
-
-            FD_SET(s, &sstate.rfds);
-            if (sstate.max <= s)
-                sstate.max = s + 1;
-        }
+        newconn->faddr.address = &newconn->kaddr;
+        init_addr(&newconn->faddr, ss2sa(&newconn->addr_s));
     }
 }
 
 static void
-process_rpc_connection(void *handle, struct connection *conn, const char *prog,
-                       int selflags)
+process_rpc_connection(verto_ctx *ctx, verto_ev *ev)
 {
     fd_set fds;
 
-    assert(selflags & SSF_READ);
-
-    if ((selflags & SSF_READ) == 0)
-        return;
-
     FD_ZERO(&fds);
-    FD_SET(conn->fd, &fds);
-
+    FD_SET(verto_get_fd(ev), &fds);
     svc_getreqset(&fds);
 
-    if (!FD_ISSET(conn->fd, &svc_fdset))
-        kill_tcp_or_rpc_connection(handle, conn, 0);
+    if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))
+        verto_del(ev);
 }
 
 #endif /* INET */