From 138f243f1e21a7c80b63345e02dc851a9b81ad4d Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 2 Sep 2011 17:07:59 +0000 Subject: [PATCH] Migrate net-server loop to use libverto From npmccallum@redhat.com. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25132 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/net-server.h | 18 +- src/kadmin/server/Makefile.in | 4 +- src/kadmin/server/ovsec_kadmd.c | 129 +--- src/kdc/Makefile.in | 4 +- src/kdc/main.c | 108 +-- src/lib/apputils/net-server.c | 1177 ++++++++++++++++--------------- 6 files changed, 691 insertions(+), 749 deletions(-) diff --git a/src/include/net-server.h b/src/include/net-server.h index 144156de0..b8414a158 100644 --- a/src/include/net-server.h +++ b/src/include/net-server.h @@ -29,23 +29,27 @@ #ifndef NET_SERVER_H #define NET_SERVER_H +#include + typedef struct _krb5_fulladdr { krb5_address * address; krb5_ui_4 port; } krb5_fulladdr; /* exported from network.c */ -extern volatile int signal_requests_exit, signal_requests_reset; void init_addr(krb5_fulladdr *, struct sockaddr *); + +/* exported from net-server.c */ +verto_ctx *loop_init(verto_ev_type types, void *handle, void (*reset)()); krb5_error_code loop_add_udp_port(int port); krb5_error_code loop_add_tcp_port(int port); krb5_error_code loop_add_rpc_service(int port, u_long prognum, u_long versnum, void (*dispatch)()); -krb5_error_code loop_setup_network(void *handle, const char *prog, - int no_reconfig); -krb5_error_code loop_listen_and_process(void *handle, const char *prog, - void (*reset)(void)); -void loop_closedown_network(void); +krb5_error_code loop_setup_routing_socket(verto_ctx *ctx, void *handle, + const char *progname); +krb5_error_code loop_setup_network(verto_ctx *ctx, void *handle, + const char *progname); +void loop_free(verto_ctx *ctx); /* to be supplied by the server application */ @@ -56,7 +60,7 @@ void loop_closedown_network(void); * The first, dispatch(), is for normal processing of a request. The * second, make_toolong_error(), is obviously for generating an error * to send back when the incoming message is bigger than - * loop_listen_and_process can accept. + * the main loop can accept. */ krb5_error_code dispatch (void *handle, struct sockaddr *local_addr, diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in index 7872872d5..f063e25d3 100644 --- a/src/kadmin/server/Makefile.in +++ b/src/kadmin/server/Makefile.in @@ -16,8 +16,8 @@ SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c ipropd_svc.c all:: $(PROG) -$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) - $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) +$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB) + $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) $(VERTO_LIBS) install:: $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(SERVER_BINDIR)/$(PROG) diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c index 91cd255ec..2800f8778 100644 --- a/src/kadmin/server/ovsec_kadmd.c +++ b/src/kadmin/server/ovsec_kadmd.c @@ -63,15 +63,6 @@ extern int daemon(int, int); #endif -void setup_signal_handlers(iprop_role iproprole); -void request_exit(int); -void request_hup(int); - -#ifdef POSIX_SIGNALS -static struct sigaction s_action; -#endif /* POSIX_SIGNALS */ - - #define TIMEOUT 15 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; @@ -235,6 +226,8 @@ int main(int argc, char *argv[]) kdb_log_context *log_ctx; + verto_ctx *ctx; + setlocale(LC_MESSAGES, ""); setvbuf(stderr, NULL, _IONBF, 0); @@ -364,6 +357,18 @@ int main(int argc, char *argv[]) exit(1); } + ctx = loop_init(VERTO_EV_TYPE_SIGNAL, global_server_handle, NULL); + if (!ctx) { + krb5_klog_syslog(LOG_ERR, + _("%s: could not initialize loop, aborting"), + whoami); + fprintf(stderr, _("%s: could not initialize loop, aborting\n"), + whoami); + kadm5_destroy(global_server_handle); + krb5_klog_close(context); + exit(1); + } + #define server_handle ((kadm5_server_handle_t)global_server_handle) if ((ret = loop_add_udp_port(server_handle->params.kpasswd_port)) || (ret = loop_add_tcp_port(server_handle->params.kpasswd_port)) @@ -377,12 +382,14 @@ int main(int argc, char *argv[]) : 0) #endif #undef server_handle - || (ret = loop_setup_network(global_server_handle, whoami, 0))) { + || (ret = loop_setup_routing_socket(ctx, global_server_handle, whoami)) + || (ret = loop_setup_network(ctx, global_server_handle, whoami))) { const char *e_txt = krb5_get_error_message (context, ret); krb5_klog_syslog(LOG_ERR, _("%s: %s while initializing network, " "aborting"), whoami, e_txt); fprintf(stderr, _("%s: %s while initializing network, aborting\n"), whoami, e_txt); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -395,6 +402,7 @@ int main(int argc, char *argv[]) "names, failing.")); fprintf(stderr, _("%s: Cannot build GSS-API authentication names.\n"), whoami); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -429,6 +437,7 @@ kterr: if (ret) { krb5_klog_syslog(LOG_ERR, "%s", krb5_get_error_message (context, ret)); fprintf(stderr, _("%s: Can't set up keytab for RPC.\n"), whoami); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -440,6 +449,7 @@ kterr: fprintf(stderr, _("%s: Cannot set GSS-API authentication names.\n"), whoami); svcauth_gssapi_unset_names(); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -462,6 +472,7 @@ kterr: if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) { fprintf(stderr, _("%s: Cannot initialize RPCSEC_GSS service name.\n"), whoami); + loop_free(ctx); exit(1); } @@ -471,6 +482,7 @@ kterr: fprintf(stderr, _("%s: Cannot initialize acl file: %s\n"), whoami, errmsg); svcauth_gssapi_unset_names(); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -482,6 +494,7 @@ kterr: krb5_klog_syslog(LOG_ERR, _("Cannot detach from tty: %s"), errmsg); fprintf(stderr, _("%s: Cannot detach from tty: %s\n"), whoami, errmsg); svcauth_gssapi_unset_names(); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -493,6 +506,7 @@ kterr: krb5_klog_syslog(LOG_ERR, _("Cannot create PID file %s: %s"), pid_file, errmsg); svcauth_gssapi_unset_names(); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -505,6 +519,7 @@ kterr: krb5_klog_syslog(LOG_ERR, _("Error getting random seed: %s, aborting"), krb5_get_error_message(context, ret)); svcauth_gssapi_unset_names(); + loop_free(ctx); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); @@ -530,6 +545,7 @@ kterr: krb5_klog_syslog(LOG_ERR, _("%s while mapping update log (`%s.ulog')"), error_message(ret), params.dbname); + loop_free(ctx); krb5_klog_close(context); exit(1); } @@ -551,6 +567,7 @@ kterr: krb5_klog_syslog(LOG_ERR, _("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."), KRB5_IPROP_PROG, KRB5_IPROP_VERS); + loop_free(ctx); krb5_klog_close(context); exit(1); } @@ -566,6 +583,7 @@ kterr: fprintf(stderr, _("%s: %s while getting IProp svc name, failing\n"), whoami, error_message(ret)); + loop_free(ctx); krb5_klog_close(context); exit(1); } @@ -601,24 +619,24 @@ kterr: err.system_error); } + loop_free(ctx); exit(1); } free(kiprop_name); #endif } - setup_signal_handlers(log_ctx->iproprole); krb5_klog_syslog(LOG_INFO, _("starting")); if (nofork) fprintf(stderr, _("%s: starting...\n"), whoami); - loop_listen_and_process(global_server_handle, whoami, NULL); + verto_run(ctx); krb5_klog_syslog(LOG_INFO, _("finished, exiting")); /* Clean up memory, etc */ svcauth_gssapi_unset_names(); kadm5_destroy(global_server_handle); - loop_closedown_network(); + loop_free(ctx); kadm5int_acl_finish(context, 0); if(gss_changepw_name) { (void) gss_release_name(&OMret, &gss_changepw_name); @@ -637,91 +655,6 @@ kterr: exit(2); } -/* - * Function: setup_signal_handlers - * - * Purpose: Setup signal handling functions using POSIX's sigaction() - * if possible, otherwise with System V's signal(). - */ - -void setup_signal_handlers(iprop_role iproprole) { -#ifdef POSIX_SIGNALS - (void) sigemptyset(&s_action.sa_mask); - s_action.sa_handler = request_exit; - (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); - (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); - (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL); - s_action.sa_handler = request_hup; - (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); - s_action.sa_handler = SIG_IGN; - (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); - - /* - * IProp will fork for a full-resync, we don't want to - * wait on it and we don't want the living dead procs either. - */ - if (iproprole == IPROP_MASTER) { - s_action.sa_handler = SIG_IGN; - (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); - } -#else /* POSIX_SIGNALS */ - signal(SIGINT, request_exit); - signal(SIGTERM, request_exit); - signal(SIGQUIT, request_exit); - signal(SIGHUP, request_hup); - signal(SIGPIPE, SIG_IGN); - - /* - * IProp will fork for a full-resync, we don't want to - * wait on it and we don't want the living dead procs either. - */ - if (iproprole == IPROP_MASTER) - (void) signal(SIGCHLD, SIG_IGN); -#endif /* POSIX_SIGNALS */ -} - -/* - * Function: request_hup - * - * Purpose: sets flag saying the server got a signal and that it should - * reset the database files when convenient. - * - * Arguments: - * Requires: - * Effects: - * Modifies: - * sets signal_requests_reset to one - */ - -void request_hup(int signum) -{ - signal_requests_reset = 1; - return; -} - -/* - * Function: request_exit - * - * Purpose: sets flags saying the server got a signal and that it - * should exit when convient. - * - * Arguments: - * Requires: - * Effects: - * modifies signal_requests_exit which ideally makes the server exit - * at some point. - * - * Modifies: - * signal_requests_exit - */ - -void request_exit(int signum) -{ - krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit")); - signal_requests_exit = 1; - return; -} - /* * Function: build_princ_name * diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in index f46cad3da..1c7bbf961 100644 --- a/src/kdc/Makefile.in +++ b/src/kdc/Makefile.in @@ -53,8 +53,8 @@ kdc5_err.h: kdc5_err.et kdc5_err.o: kdc5_err.h -krb5kdc: $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) - $(CC_LINK) -o krb5kdc $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS) +krb5kdc: $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB) + $(CC_LINK) -o krb5kdc $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS) $(VERTO_LIBS) rtest: $(RT_OBJS) $(KDB5_DEPLIBS) $(KADM_COMM_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o rtest $(RT_OBJS) $(KDB5_LIBS) $(KADM_COMM_LIBS) $(KRB5_BASE_LIBS) diff --git a/src/kdc/main.c b/src/kdc/main.c index a6a575166..979c2173a 100644 --- a/src/kdc/main.c +++ b/src/kdc/main.c @@ -78,11 +78,6 @@ extern int daemon(int, int); static void usage (char *); -static krb5_sigtype request_exit (int); -static krb5_sigtype request_hup (int); - -static void setup_signal_handlers (void); - static krb5_error_code setup_sam (void); static void initialize_realms (krb5_context, int, char **); @@ -93,10 +88,8 @@ static int nofork = 0; static int workers = 0; static const char *pid_file = NULL; static int rkey_init_done = 0; - -#ifdef POSIX_SIGNALS -static struct sigaction s_action; -#endif /* POSIX_SIGNALS */ +static volatile int signal_received = 0; +static volatile int sighup_received = 0; #define KRB5_KDC_MAX_REALMS 32 @@ -486,9 +479,9 @@ whoops: } static krb5_sigtype -request_exit(int signo) +on_monitor_signal(int signo) { - signal_requests_exit = 1; + signal_received = signo; #ifdef POSIX_SIGTYPE return; @@ -498,9 +491,9 @@ request_exit(int signo) } static krb5_sigtype -request_hup(int signo) +on_monitor_sighup(int signo) { - signal_requests_reset = 1; + sighup_received = 1; #ifdef POSIX_SIGTYPE return; @@ -509,29 +502,6 @@ request_hup(int signo) #endif } -static void -setup_signal_handlers(void) -{ -#ifdef POSIX_SIGNALS - (void) sigemptyset(&s_action.sa_mask); - s_action.sa_flags = 0; - s_action.sa_handler = request_exit; - (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); - (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); - s_action.sa_handler = request_hup; - (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); - s_action.sa_handler = SIG_IGN; - (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); -#else /* POSIX_SIGNALS */ - signal(SIGINT, request_exit); - signal(SIGTERM, request_exit); - signal(SIGHUP, request_hup); - signal(SIGPIPE, SIG_IGN); -#endif /* POSIX_SIGNALS */ - - return; -} - /* * Kill the worker subprocesses given by pids[0..bound-1], skipping any which * are set to -1, and wait for them to exit (so that we know the ports are no @@ -564,10 +534,13 @@ terminate_workers(pid_t *pids, int bound, int num_active) * function in error cases. */ static krb5_error_code -create_workers(int num) +create_workers(verto_ctx *ctx, int num) { int i, status, numleft; pid_t pid, *pids; +#ifdef POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ /* Create child worker processes; return in each child. */ krb5_klog_syslog(LOG_INFO, _("creating %d worker processes"), num); @@ -591,9 +564,29 @@ create_workers(int num) pids[i] = pid; } + /* We're going to use our own main loop here. */ + loop_free(ctx); + + /* Setup our signal handlers which will forward to the children. */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; + s_action.sa_handler = on_monitor_signal; + (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL); + s_action.sa_handler = on_monitor_sighup; + (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + signal(SIGINT, on_monitor_signal); + signal(SIGTERM, on_monitor_signal); + signal(SIGQUIT, on_monitor_signal); + signal(SIGHUP, on_monitor_sighup); +#endif /* POSIX_SIGNALS */ + /* Supervise the worker processes. */ numleft = num; - while (!signal_requests_exit) { + while (!signal_received) { /* Wait until a worker process exits or we get a signal. */ pid = wait(&status); if (pid >= 0) { @@ -612,18 +605,17 @@ create_workers(int num) } /* Propagate HUP signal to worker processes if we received one. */ - if (signal_requests_reset) { + if (sighup_received) { + sighup_received = 0; for (i = 0; i < num; i++) { if (pids[i] != -1) kill(pids[i], SIGHUP); } - signal_requests_reset = 0; } } - if (signal_requests_exit) { - krb5_klog_syslog(LOG_INFO, - _("shutdown signal received in supervisor")); - } + if (signal_received) + krb5_klog_syslog(LOG_INFO, _("signal %d received in supervisor"), + signal_received); terminate_workers(pids, num, numleft); free(pids); @@ -948,6 +940,7 @@ int main(int argc, char **argv) { krb5_error_code retval; krb5_context kcontext; + verto_ctx *ctx; int errout = 0; int i; @@ -989,7 +982,12 @@ int main(int argc, char **argv) */ initialize_realms(kcontext, argc, argv); - setup_signal_handlers(); + ctx = loop_init(VERTO_EV_TYPE_NONE, NULL, reset_for_hangup); + if (!ctx) { + kdc_err(kcontext, ENOMEM, _("while creating main loop")); + finish_realms(); + return 1; + } load_preauth_plugins(kcontext); load_authdata_plugins(kcontext); @@ -1039,7 +1037,15 @@ int main(int argc, char **argv) * children won't be able to re-open the listener sockets. Hopefully our * platform has pktinfo support and doesn't need reconfigs. */ - if ((retval = loop_setup_network(NULL, kdc_progname, (workers > 0)))) { + if (workers == 0) { + retval = loop_setup_routing_socket(ctx, NULL, kdc_progname); + if (retval) { + kdc_err(kcontext, retval, _("while initializing routing socket")); + finish_realms(); + return 1; + } + } + if ((retval = loop_setup_network(ctx, NULL, kdc_progname))) { net_init_error: kdc_err(kcontext, retval, _("while initializing network")); finish_realms(); @@ -1060,7 +1066,7 @@ int main(int argc, char **argv) } if (workers > 0) { finish_realms(); - retval = create_workers(workers); + retval = create_workers(ctx, workers); if (retval) { kdc_err(kcontext, errno, _("creating worker processes")); return 1; @@ -1071,11 +1077,9 @@ int main(int argc, char **argv) krb5_klog_syslog(LOG_INFO, _("commencing operation")); if (nofork) fprintf(stderr, _("%s: starting...\n"), kdc_progname); - if ((retval = loop_listen_and_process(0, kdc_progname, reset_for_hangup))) { - kdc_err(kcontext, retval, _("while processing network requests")); - errout++; - } - loop_closedown_network(); + + verto_run(ctx); + loop_free(ctx); krb5_klog_syslog(LOG_INFO, _("shutting down")); unload_preauth_plugins(kcontext); unload_authdata_plugins(kcontext); diff --git a/src/lib/apputils/net-server.c b/src/lib/apputils/net-server.c index 5afc3c0fa..152f11c2b 100644 --- a/src/lib/apputils/net-server.c +++ b/src/lib/apputils/net-server.c @@ -59,12 +59,13 @@ #include "fake-addrinfo.h" #include "net-server.h" +#include + /* XXX */ #define KDC5_NONET (-1779992062L) -volatile int signal_requests_exit = 0, signal_requests_reset = 0; - -static void loop_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 @@ -187,9 +188,9 @@ 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 { @@ -215,6 +216,7 @@ struct connection { } tcp; struct { SVCXPRT *transp; + int closed; } rpc; } u; }; @@ -250,33 +252,84 @@ struct connection { #define FREE_SET_DATA(set) \ (free(set.data), set.data = 0, set.max = 0, set.n = 0) - -/* Set connections; */ -static SET(struct connection *) connections; -#define n_sockets connections.n -#define conns connections.data - -/* Set 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; + +static void +do_break(verto_ctx *ctx, verto_ev *ev) +{ + krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit")); + verto_break(ctx); +} -#include "cm.h" +struct sighup_context { + void *handle; + void (*reset)(); +}; -static struct select_state sstate; -static fd_set rpc_listenfds; +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)); +} + +verto_ctx * +loop_init(verto_ev_type types, void *handle, void (*reset)()) +{ + struct sighup_context *sc; + verto_ctx *ctx; + verto_ev *ev; + + types |= VERTO_EV_TYPE_IO; + types |= VERTO_EV_TYPE_SIGNAL; + types |= VERTO_EV_TYPE_TIMEOUT; + ctx = verto_default(NULL, types); + 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)) + goto error; + + ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP); + if (!ev) + goto error; + + sc = malloc(sizeof(*sc)); + if (!sc) + goto error; + sc->handle = handle; + sc->reset = reset; + + verto_set_private(ev, sc, free_sighup_context); + return ctx; + +error: + verto_free(ctx); + return NULL; +} krb5_error_code loop_add_udp_port(int port) @@ -348,6 +401,8 @@ loop_add_rpc_service(int port, u_long prognum, #include "foreachaddr.h" struct socksetup { + verto_ctx *ctx; + void *handle; const char *prog; krb5_error_code retval; int udp_flags; @@ -355,12 +410,133 @@ struct socksetup { #define UDP_DO_IPV6 2 }; -static struct connection * +static void +free_connection(struct connection *conn) +{ + if (!conn) + return; + if (conn->u.tcp.response) + krb5_free_data(get_context(conn->handle), conn->u.tcp.response); + if (conn->u.tcp.buffer) + free(conn->u.tcp.buffer); + if (conn->type == CONN_RPC_LISTENER && conn->u.rpc.transp != NULL) + svc_destroy(conn->u.rpc.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->u.rpc.closed)) + close(fd); + + /* Free the connection struct. */ + if (conn) { + switch (conn->type) { + case CONN_RPC: + if (conn->u.rpc.closed) { + 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, *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 * +convert_event(verto_ctx *ctx, verto_ev *ev, verto_ev_flag flags, + verto_callback callback) +{ + struct connection *conn; + verto_ev *newev; + int sock; + + conn = verto_get_private(ev); + sock = verto_get_fd(ev); + if (sock < 0) + return NULL; + + newev = make_event(ctx, flags, callback, sock, conn, 1); + + /* Delete the read event without closing the socket + * or freeing the connection struct. */ + if (newev) { + verto_set_private(ev, NULL, NULL); /* Reset the destructor. */ + remove_event_from_set(ev); /* Remove it from the set. */ + verto_del(ev); + } + + return newev; +} + +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) { @@ -377,62 +553,43 @@ add_fd(struct socksetup *data, int sock, enum conn_type conntype, _("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); - 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, + 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, + 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); } /* @@ -494,21 +651,25 @@ 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; + verto_ev *ev; - conn = add_fd(data, sock, CONN_RPC_LISTENER, accept_rpc_connection); - if (conn == NULL) + ev = add_fd(data, sock, CONN_RPC_LISTENER, + VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST, + accept_rpc_connection, 1); + if (ev == NULL) return NULL; + conn = verto_get_private(ev); 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"), strerror(errno)); - delete_fd(conn); + verto_del(ev); return NULL; } @@ -517,17 +678,19 @@ add_rpc_listener_fd(struct socksetup *data, struct rpc_svc_data *svc, int sock) 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; @@ -637,9 +800,6 @@ 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"), s4, paddr((struct sockaddr *)&sin4)); } @@ -650,9 +810,6 @@ 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"), s6, paddr((struct sockaddr *)&sin6)); } @@ -704,13 +861,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; + else krb5_klog_syslog(LOG_INFO, _("listening on fd %d: rpc %s"), s4, paddr((struct sockaddr *)&sin4)); - } #ifdef KRB5_USE_INET6 if (ipv6_enabled()) { @@ -722,18 +875,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; + 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; } @@ -789,7 +937,7 @@ setup_udp_pktinfo_ports(struct socksetup *data) } #else /* no pktinfo compile-time support */ static void -setup_udp_pktinfo_ports(struct socksetup *data) +setup_udp_pktinfo_ports(verto_ctx *ctx, struct socksetup *data) { } #endif @@ -829,9 +977,6 @@ setup_udp_port_1(struct socksetup *data, struct sockaddr *addr, close(sock); return 1; } - FD_SET (sock, &sstate.rfds); - if (sock >= sstate.max) - sstate.max = sock + 1; } return 0; } @@ -939,8 +1084,6 @@ scan_for_newlines: } #endif -static int network_reconfiguration_needed = 0; - #ifdef HAVE_STRUCT_RT_MSGHDR #include @@ -969,13 +1112,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) { - int n_read; + 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) +{ + 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. @@ -1007,73 +1225,31 @@ process_routing_update(void *handle, struct connection *conn, const char *prog, _("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) { +#ifdef HAVE_STRUCT_RT_MSGHDR + struct socksetup data; + + data.ctx = ctx; + data.handle = handle; + data.prog = progname; + data.retval = 0; + int sock = socket(PF_ROUTE, SOCK_RAW, 0); if (sock < 0) { int e = errno; @@ -1081,37 +1257,38 @@ setup_routing_socket(struct socksetup *data) strerror(e)); } else { krb5_klog_syslog(LOG_INFO, _("routing socket is fd %d"), sock); - add_fd(data, sock, CONN_ROUTING, process_routing_update); 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 -loop_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 + /* * 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,8 +1304,8 @@ loop_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"), (int)n_sockets); - if (n_sockets == 0) { + 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); } @@ -1359,8 +1536,7 @@ send_to_from(int s, void *buf, size_t len, int flags, } static void -process_packet(void *handle, struct connection *conn, const char *prog, - int selflags) +process_packet(verto_ctx *ctx, verto_ev *ev) { int cc; socklen_t saddr_len, daddr_len; @@ -1371,8 +1547,13 @@ process_packet(void *handle, struct connection *conn, const char *prog, krb5_data request; krb5_data *response; char pktbuf[MAX_DGRAM_SIZE]; - int port_fd = conn->fd; + int port_fd; union aux_addressing_info auxaddr; + struct connection *conn; + + port_fd = verto_get_fd(ev); + conn = verto_get_private(ev); + assert(port_fd >= 0); response = NULL; saddr_len = sizeof(saddr); @@ -1391,7 +1572,7 @@ 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")); return; } if (!cc) @@ -1403,7 +1584,7 @@ process_packet(void *handle, struct connection *conn, const char *prog, if (getnameinfo(ss2sa(&daddr), 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 @@ -1424,9 +1605,10 @@ process_packet(void *handle, struct connection *conn, const char *prog, faddr.address = &addr; init_addr(&faddr, ss2sa(&saddr)); /* This address is in net order. */ - retval = dispatch(handle, ss2sa(&daddr), &faddr, &request, &response, 0); + retval = dispatch(conn->handle, ss2sa(&daddr), + &faddr, &request, &response, 0); if (retval) { - com_err(prog, retval, _("while dispatching (udp)")); + com_err(conn->prog, retval, _("while dispatching (udp)")); return; } if (response == NULL) @@ -1441,7 +1623,7 @@ process_packet(void *handle, struct connection *conn, const char *prog, char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV]; char daddrbuf[NI_MAXHOST]; int e = errno; - krb5_free_data(get_context(handle), response); + krb5_free_data(get_context(conn->handle), response); if (getnameinfo((struct sockaddr *)&daddr, daddr_len, daddrbuf, sizeof(daddrbuf), 0, 0, NI_NUMERICHOST) != 0) { @@ -1453,69 +1635,72 @@ process_packet(void *handle, struct connection *conn, const char *prog, strlcpy(saddrbuf, "?", sizeof(saddrbuf)); strlcpy(sportbuf, "?", sizeof(sportbuf)); } - com_err(prog, e, _("while sending reply to %s/%s from %s"), + com_err(conn->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"), + com_err(conn->prog, 0, _("short reply write %d vs %d\n"), response->length, cc); } - krb5_free_data(get_context(handle), response); + krb5_free_data(get_context(conn->handle), response); return; } -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, *oldest_c = NULL; + verto_ev *ev, *oldest_ev = NULL; int i, fd = -1; 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; + + c = verto_get_private(ev); + if (!c) continue; - if (c == newconn) + if (c->type != CONN_TCP && c->type != CONN_RPC) continue; #if 0 - krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd, + krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", + verto_get_fd(oldest_ev), c->u.tcp.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->u.tcp.start_time > c->u.tcp.start_time) { + oldest_ev = ev; + oldest_c = c; + } } - if (oldest_tcp != NULL) { + 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->u.tcp.addrbuf); + if (oldest_c->type == CONN_RPC) + oldest_c->u.rpc.closed = 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,12 +1712,17 @@ 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), @@ -1561,15 +1751,13 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog, newconn->u.tcp.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, + com_err(conn->prog, errno, _("allocating buffer for new TCP session from %s"), newconn->u.tcp.addrbuf); - delete_fd(newconn); - close(s); - tcp_or_rpc_data_counter--; + verto_del(newev); return; } newconn->u.tcp.offset = 0; @@ -1577,92 +1765,126 @@ accept_tcp_connection(void *handle, struct connection *conn, const char *prog, 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; } static void -kill_tcp_or_rpc_connection(void *handle, struct connection *conn, - int isForcedClose) +process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev) { - 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); + struct connection *conn; + ssize_t nread; + size_t len; + int sock; - svc_getreqset(&fds); + conn = verto_get_private(ev); + sock = verto_get_fd(ev); - if (FD_ISSET(conn->fd, &svc_fdset)) { - krb5_klog_syslog(LOG_ERR, - _("descriptor %d closed but still in svc_fdset"), - conn->fd); + /* + * 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->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(sock, + 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 (conn->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; + } } + } 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(sock, + 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(sock, ss2sa(&local_saddr), + &local_saddrlen) == 0) + local_saddrp = ss2sa(&local_saddr); + + err = dispatch(conn->handle, local_saddrp, &conn->u.tcp.faddr, + &request, &conn->u.tcp.response, 1); + if (err) { + com_err(conn->prog, err, _("while dispatching (tcp)")); + goto kill_tcp_connection; + } + if (conn->u.tcp.response == NULL) + goto kill_tcp_connection; + have_response: + /* Queue outgoing response. */ + 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; + + if (convert_event(ctx, ev, + VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST, + process_tcp_connection_write)) + return; } - conn->fd = -1; - delete_fd(conn); - tcp_or_rpc_data_counter--; -} + return; -static void -queue_tcp_outgoing_response(struct connection *conn) -{ - 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); +kill_tcp_connection: + verto_del(ev); } static void -process_tcp_connection(void *handle, struct connection *conn, const char *prog, - int selflags) +process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev) { - int isForcedClose = 1; /* not used now, but for completeness */ + struct connection *conn; + SOCKET_WRITEV_TEMP tmp; + ssize_t nwrote; + int sock; - if (selflags & SSF_WRITE) { - ssize_t nwrote; - SOCKET_WRITEV_TEMP tmp; + conn = verto_get_private(ev); + sock = verto_get_fd(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; - goto kill_tcp_connection; - } + nwrote = SOCKET_WRITEV(sock, conn->u.tcp.sgp, + conn->u.tcp.sgnum, tmp); + if (nwrote > 0) { /* non-error and non-eof */ while (nwrote) { sg_buf *sgp = conn->u.tcp.sgp; if ((size_t)nwrote < SG_LEN(sgp)) { @@ -1676,354 +1898,133 @@ process_tcp_connection(void *handle, struct connection *conn, const char *prog, 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; - 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; - } - } - } 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; - } - if (conn->u.tcp.response == NULL) - goto kill_tcp_connection; - have_response: - queue_tcp_outgoing_response(conn); - FD_CLR(conn->fd, &sstate.rfds); - } - } else - abort(); - return; + /* 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->u.tcp.sgnum > 0) + return; + } -kill_tcp_connection: - kill_tcp_or_rpc_connection(handle, conn, isForcedClose); + /* 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); } -static void -service_conn(void *handle, struct connection *conn, const char *prog, - int selflags) +void +loop_free(verto_ctx *ctx) { - conn->service(handle, conn, prog, selflags); + 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 -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 -} - -krb5_error_code -loop_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; - size_t i; - int 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; - } - - 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; - - 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; - loop_closedown_network_sockets(); - err = loop_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 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; -} - -static void -loop_closedown_network_sockets() +have_event_for_fd(int fd) { + verto_ev *ev; int i; - struct connection *conn; - - 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); - svc_getreqset(&fds); - } - } - 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); + FOREACH_ELT(events, i, ev) { + if (verto_get_fd(ev) == fd) + return 1; } -} -void -loop_closedown_network() -{ - loop_closedown_network_sockets(); - FREE_SET_DATA(connections); - FREE_SET_DATA(udp_port_data); - FREE_SET_DATA(tcp_port_data); - FREE_SET_DATA(rpc_svc_data); + 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]; + 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; - newconn = add_rpc_data_fd(&sockdata, s); - if (newconn == NULL) - continue; + newev = add_rpc_data_fd(&sockdata, s); + if (newev == NULL) + continue; + newconn = verto_get_private(newev); - set_cloexec_fd(s); + 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->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 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->u.tcp.addrbuf); #endif - newconn->u.tcp.addr_s = addr_s; - newconn->u.tcp.addrlen = addrlen; - newconn->u.tcp.start_time = time(0); + newconn->u.tcp.addr_s = addr_s; + newconn->u.tcp.addrlen = addrlen; + newconn->u.tcp.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->u.tcp.faddr.address = &newconn->u.tcp.kaddr; + init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.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 */ -- 2.26.2