/* -*- 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
* 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
/* 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
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))
#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;
+
+verto_ctx *
+loop_init(verto_ev_type types)
+{
+ types |= VERTO_EV_TYPE_IO;
+ types |= VERTO_EV_TYPE_SIGNAL;
+ types |= VERTO_EV_TYPE_TIMEOUT;
+
+#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
+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;
-#include "cm.h"
+ sc = malloc(sizeof(*sc));
+ if (!sc)
+ return ENOMEM;
-static struct select_state sstate;
-static fd_set rpc_listenfds;
+ sc->handle = handle;
+ sc->reset = reset;
+ verto_set_private(ev, sc, free_sighup_context);
+ return 0;
+}
krb5_error_code
-add_udp_port(int port)
+loop_add_udp_port(int port)
{
int i;
void *tmp;
}
krb5_error_code
-add_tcp_port(int port)
+loop_add_tcp_port(int port)
{
int i;
void *tmp;
}
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;
#include "foreachaddr.h"
struct socksetup {
+ verto_ctx *ctx;
+ void *handle;
const char *prog;
krb5_error_code retval;
int udp_flags;
#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
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);
+ 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 struct connection *
-add_tcp_data_fd(struct socksetup *data, int sock)
+static verto_ev *
+add_tcp_read_fd(struct socksetup *data, int sock)
{
- return add_fd(data, sock, CONN_TCP, process_tcp_connection);
-}
-
-static void
-delete_fd(struct connection *xconn)
-{
- 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);
}
/*
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;
}
#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;
}
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;
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;
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;
}
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));
}
}
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
}
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()) {
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;
}
sock = create_server_socket(data, addr, SOCK_DGRAM);
if (sock == -1)
return 1;
+ setnbio(sock);
#if !(defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \
(defined(IP_PKTINFO) || defined(IPV6_PKTINFO)))
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;
}
{
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;
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);
}
#endif
-static int network_reconfiguration_needed = 0;
-
#ifdef HAVE_STRUCT_RT_MSGHDR
#include <net/route.h>
}
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.
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
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)
+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
- 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
}
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);
}
#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;
+};
- 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);
+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) {
- if (errno != EINTR
+ /* 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
*/
&& 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);
#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++ = '.';
}
#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);
- 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 */