xref: /freebsd/crypto/krb5/src/lib/apputils/net-server.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* lib/apputils/net-server.c - Network code for krb5 servers (kdc, kadmind) */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright 1990,2000,2007,2008,2009,2010,2016 by the Massachusetts Institute
5*7f2fe78bSCy Schubert  * of Technology.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Export of this software from the United States of America may
8*7f2fe78bSCy Schubert  *   require a specific license from the United States Government.
9*7f2fe78bSCy Schubert  *   It is the responsibility of any person or organization contemplating
10*7f2fe78bSCy Schubert  *   export to obtain such a license before exporting.
11*7f2fe78bSCy Schubert  *
12*7f2fe78bSCy Schubert  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13*7f2fe78bSCy Schubert  * distribute this software and its documentation for any purpose and
14*7f2fe78bSCy Schubert  * without fee is hereby granted, provided that the above copyright
15*7f2fe78bSCy Schubert  * notice appear in all copies and that both that copyright notice and
16*7f2fe78bSCy Schubert  * this permission notice appear in supporting documentation, and that
17*7f2fe78bSCy Schubert  * the name of M.I.T. not be used in advertising or publicity pertaining
18*7f2fe78bSCy Schubert  * to distribution of the software without specific, written prior
19*7f2fe78bSCy Schubert  * permission.  Furthermore if you modify this software you must label
20*7f2fe78bSCy Schubert  * your software as modified software and not distribute it in such a
21*7f2fe78bSCy Schubert  * fashion that it might be confused with the original M.I.T. software.
22*7f2fe78bSCy Schubert  * M.I.T. makes no representations about the suitability of
23*7f2fe78bSCy Schubert  * this software for any purpose.  It is provided "as is" without express
24*7f2fe78bSCy Schubert  * or implied warranty.
25*7f2fe78bSCy Schubert  */
26*7f2fe78bSCy Schubert 
27*7f2fe78bSCy Schubert #include "k5-int.h"
28*7f2fe78bSCy Schubert #include "adm_proto.h"
29*7f2fe78bSCy Schubert #include <sys/ioctl.h>
30*7f2fe78bSCy Schubert #include <syslog.h>
31*7f2fe78bSCy Schubert 
32*7f2fe78bSCy Schubert #include <stddef.h>
33*7f2fe78bSCy Schubert #include "port-sockets.h"
34*7f2fe78bSCy Schubert #include "socket-utils.h"
35*7f2fe78bSCy Schubert 
36*7f2fe78bSCy Schubert #include <gssrpc/rpc.h>
37*7f2fe78bSCy Schubert 
38*7f2fe78bSCy Schubert #ifdef HAVE_NETINET_IN_H
39*7f2fe78bSCy Schubert #include <sys/types.h>
40*7f2fe78bSCy Schubert #include <netinet/in.h>
41*7f2fe78bSCy Schubert #include <sys/socket.h>
42*7f2fe78bSCy Schubert #ifdef HAVE_SYS_SOCKIO_H
43*7f2fe78bSCy Schubert /* for SIOCGIFCONF, etc. */
44*7f2fe78bSCy Schubert #include <sys/sockio.h>
45*7f2fe78bSCy Schubert #endif
46*7f2fe78bSCy Schubert #include <sys/time.h>
47*7f2fe78bSCy Schubert #if HAVE_SYS_SELECT_H
48*7f2fe78bSCy Schubert #include <sys/select.h>
49*7f2fe78bSCy Schubert #endif
50*7f2fe78bSCy Schubert #include <arpa/inet.h>
51*7f2fe78bSCy Schubert 
52*7f2fe78bSCy Schubert #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
53*7f2fe78bSCy Schubert #include <net/if.h>
54*7f2fe78bSCy Schubert #endif
55*7f2fe78bSCy Schubert 
56*7f2fe78bSCy Schubert #ifdef HAVE_SYS_FILIO_H
57*7f2fe78bSCy Schubert #include <sys/filio.h>          /* FIONBIO */
58*7f2fe78bSCy Schubert #endif
59*7f2fe78bSCy Schubert 
60*7f2fe78bSCy Schubert #include "fake-addrinfo.h"
61*7f2fe78bSCy Schubert #include "net-server.h"
62*7f2fe78bSCy Schubert #include <signal.h>
63*7f2fe78bSCy Schubert #include <netdb.h>
64*7f2fe78bSCy Schubert 
65*7f2fe78bSCy Schubert #include "udppktinfo.h"
66*7f2fe78bSCy Schubert 
67*7f2fe78bSCy Schubert /* XXX */
68*7f2fe78bSCy Schubert #define KDC5_NONET                               (-1779992062L)
69*7f2fe78bSCy Schubert 
70*7f2fe78bSCy Schubert static int tcp_or_rpc_data_counter;
71*7f2fe78bSCy Schubert static int max_tcp_or_rpc_data_connections = 45;
72*7f2fe78bSCy Schubert 
73*7f2fe78bSCy Schubert static int
setreuseaddr(int sock,int value)74*7f2fe78bSCy Schubert setreuseaddr(int sock, int value)
75*7f2fe78bSCy Schubert {
76*7f2fe78bSCy Schubert     int st;
77*7f2fe78bSCy Schubert 
78*7f2fe78bSCy Schubert     st = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
79*7f2fe78bSCy Schubert     if (st)
80*7f2fe78bSCy Schubert         return st;
81*7f2fe78bSCy Schubert #ifdef SO_REUSEPORT
82*7f2fe78bSCy Schubert     st = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
83*7f2fe78bSCy Schubert     if (st)
84*7f2fe78bSCy Schubert         return st;
85*7f2fe78bSCy Schubert #endif
86*7f2fe78bSCy Schubert     return 0;
87*7f2fe78bSCy Schubert }
88*7f2fe78bSCy Schubert 
89*7f2fe78bSCy Schubert #if defined(IPV6_V6ONLY)
90*7f2fe78bSCy Schubert static int
setv6only(int sock,int value)91*7f2fe78bSCy Schubert setv6only(int sock, int value)
92*7f2fe78bSCy Schubert {
93*7f2fe78bSCy Schubert     return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
94*7f2fe78bSCy Schubert }
95*7f2fe78bSCy Schubert #endif
96*7f2fe78bSCy Schubert 
97*7f2fe78bSCy Schubert static const char *
paddr(struct sockaddr * sa)98*7f2fe78bSCy Schubert paddr(struct sockaddr *sa)
99*7f2fe78bSCy Schubert {
100*7f2fe78bSCy Schubert     static char buf[100];
101*7f2fe78bSCy Schubert     char portbuf[10];
102*7f2fe78bSCy Schubert     if (getnameinfo(sa, sa_socklen(sa),
103*7f2fe78bSCy Schubert                     buf, sizeof(buf), portbuf, sizeof(portbuf),
104*7f2fe78bSCy Schubert                     NI_NUMERICHOST|NI_NUMERICSERV))
105*7f2fe78bSCy Schubert         strlcpy(buf, "<unprintable>", sizeof(buf));
106*7f2fe78bSCy Schubert     else {
107*7f2fe78bSCy Schubert         unsigned int len = sizeof(buf) - strlen(buf);
108*7f2fe78bSCy Schubert         char *p = buf + strlen(buf);
109*7f2fe78bSCy Schubert         if (len > 2+strlen(portbuf)) {
110*7f2fe78bSCy Schubert             *p++ = '.';
111*7f2fe78bSCy Schubert             len--;
112*7f2fe78bSCy Schubert             strncpy(p, portbuf, len);
113*7f2fe78bSCy Schubert         }
114*7f2fe78bSCy Schubert     }
115*7f2fe78bSCy Schubert     return buf;
116*7f2fe78bSCy Schubert }
117*7f2fe78bSCy Schubert 
118*7f2fe78bSCy Schubert /* KDC data.  */
119*7f2fe78bSCy Schubert 
120*7f2fe78bSCy Schubert enum conn_type {
121*7f2fe78bSCy Schubert     CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC
122*7f2fe78bSCy Schubert };
123*7f2fe78bSCy Schubert 
124*7f2fe78bSCy Schubert enum bind_type {
125*7f2fe78bSCy Schubert     UDP, TCP, RPC
126*7f2fe78bSCy Schubert };
127*7f2fe78bSCy Schubert 
128*7f2fe78bSCy Schubert static const char *const bind_type_names[] = {
129*7f2fe78bSCy Schubert     [UDP] = "UDP",
130*7f2fe78bSCy Schubert     [TCP] = "TCP",
131*7f2fe78bSCy Schubert     [RPC] = "RPC",
132*7f2fe78bSCy Schubert };
133*7f2fe78bSCy Schubert 
134*7f2fe78bSCy Schubert /* Per-connection info.  */
135*7f2fe78bSCy Schubert struct connection {
136*7f2fe78bSCy Schubert     void *handle;
137*7f2fe78bSCy Schubert     const char *prog;
138*7f2fe78bSCy Schubert     enum conn_type type;
139*7f2fe78bSCy Schubert 
140*7f2fe78bSCy Schubert     /* Connection fields (TCP or RPC) */
141*7f2fe78bSCy Schubert     struct sockaddr_storage addr_s;
142*7f2fe78bSCy Schubert     socklen_t addrlen;
143*7f2fe78bSCy Schubert     char addrbuf[56];
144*7f2fe78bSCy Schubert     krb5_address remote_addr_buf;
145*7f2fe78bSCy Schubert     krb5_fulladdr remote_addr;
146*7f2fe78bSCy Schubert 
147*7f2fe78bSCy Schubert     /* Incoming data (TCP) */
148*7f2fe78bSCy Schubert     size_t bufsiz;
149*7f2fe78bSCy Schubert     size_t offset;
150*7f2fe78bSCy Schubert     char *buffer;
151*7f2fe78bSCy Schubert     size_t msglen;
152*7f2fe78bSCy Schubert 
153*7f2fe78bSCy Schubert     /* Outgoing data (TCP) */
154*7f2fe78bSCy Schubert     krb5_data *response;
155*7f2fe78bSCy Schubert     unsigned char lenbuf[4];
156*7f2fe78bSCy Schubert     sg_buf sgbuf[2];
157*7f2fe78bSCy Schubert     sg_buf *sgp;
158*7f2fe78bSCy Schubert     int sgnum;
159*7f2fe78bSCy Schubert 
160*7f2fe78bSCy Schubert     /* Crude denial-of-service avoidance support (TCP or RPC) */
161*7f2fe78bSCy Schubert     time_t start_time;
162*7f2fe78bSCy Schubert 
163*7f2fe78bSCy Schubert     /* RPC-specific fields */
164*7f2fe78bSCy Schubert     SVCXPRT *transp;
165*7f2fe78bSCy Schubert     int rpc_force_close;
166*7f2fe78bSCy Schubert };
167*7f2fe78bSCy Schubert 
168*7f2fe78bSCy Schubert #define SET(TYPE) struct { TYPE *data; size_t n, max; }
169*7f2fe78bSCy Schubert 
170*7f2fe78bSCy Schubert /* Start at the top and work down -- this should allow for deletions
171*7f2fe78bSCy Schubert    without disrupting the iteration, since we delete by overwriting
172*7f2fe78bSCy Schubert    the element to be removed with the last element.  */
173*7f2fe78bSCy Schubert #define FOREACH_ELT(set,idx,vvar)                                       \
174*7f2fe78bSCy Schubert     for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
175*7f2fe78bSCy Schubert 
176*7f2fe78bSCy Schubert #define GROW_SET(set, incr, tmpptr)                                     \
177*7f2fe78bSCy Schubert     ((set.max + incr < set.max                                          \
178*7f2fe78bSCy Schubert       || ((set.max + incr) * sizeof(set.data[0]) / sizeof(set.data[0])  \
179*7f2fe78bSCy Schubert           != set.max + incr))                                           \
180*7f2fe78bSCy Schubert      ? 0                         /* overflow */                         \
181*7f2fe78bSCy Schubert      : ((tmpptr = realloc(set.data,                                     \
182*7f2fe78bSCy Schubert                           (set.max + incr) * sizeof(set.data[0])))      \
183*7f2fe78bSCy Schubert         ? (set.data = tmpptr, set.max += incr, 1)                       \
184*7f2fe78bSCy Schubert         : 0))
185*7f2fe78bSCy Schubert 
186*7f2fe78bSCy Schubert /* 1 = success, 0 = failure */
187*7f2fe78bSCy Schubert #define ADD(set, val, tmpptr)                           \
188*7f2fe78bSCy Schubert     ((set.n < set.max || GROW_SET(set, 10, tmpptr))     \
189*7f2fe78bSCy Schubert      ? (set.data[set.n++] = val, 1)                     \
190*7f2fe78bSCy Schubert      : 0)
191*7f2fe78bSCy Schubert 
192*7f2fe78bSCy Schubert #define DEL(set, idx)                           \
193*7f2fe78bSCy Schubert     (set.data[idx] = set.data[--set.n], 0)
194*7f2fe78bSCy Schubert 
195*7f2fe78bSCy Schubert #define FREE_SET_DATA(set)                                      \
196*7f2fe78bSCy Schubert     (free(set.data), set.data = 0, set.max = 0, set.n = 0)
197*7f2fe78bSCy Schubert 
198*7f2fe78bSCy Schubert /*
199*7f2fe78bSCy Schubert  * N.B.: The Emacs cc-mode indentation code seems to get confused if
200*7f2fe78bSCy Schubert  * the macro argument here is one word only.  So use "unsigned short"
201*7f2fe78bSCy Schubert  * instead of the "u_short" we were using before.
202*7f2fe78bSCy Schubert  */
203*7f2fe78bSCy Schubert struct rpc_svc_data {
204*7f2fe78bSCy Schubert     u_long prognum;
205*7f2fe78bSCy Schubert     u_long versnum;
206*7f2fe78bSCy Schubert     void (*dispatch)();
207*7f2fe78bSCy Schubert };
208*7f2fe78bSCy Schubert 
209*7f2fe78bSCy Schubert struct bind_address {
210*7f2fe78bSCy Schubert     char *address;
211*7f2fe78bSCy Schubert     u_short port;
212*7f2fe78bSCy Schubert     enum bind_type type;
213*7f2fe78bSCy Schubert     struct rpc_svc_data rpc_svc_data;
214*7f2fe78bSCy Schubert };
215*7f2fe78bSCy Schubert 
216*7f2fe78bSCy Schubert static SET(verto_ev *) events;
217*7f2fe78bSCy Schubert static SET(struct bind_address) bind_addresses;
218*7f2fe78bSCy Schubert 
219*7f2fe78bSCy Schubert verto_ctx *
loop_init(verto_ev_type types)220*7f2fe78bSCy Schubert loop_init(verto_ev_type types)
221*7f2fe78bSCy Schubert {
222*7f2fe78bSCy Schubert     types |= VERTO_EV_TYPE_IO;
223*7f2fe78bSCy Schubert     types |= VERTO_EV_TYPE_SIGNAL;
224*7f2fe78bSCy Schubert     types |= VERTO_EV_TYPE_TIMEOUT;
225*7f2fe78bSCy Schubert     return verto_default(NULL, types);
226*7f2fe78bSCy Schubert }
227*7f2fe78bSCy Schubert 
228*7f2fe78bSCy Schubert static void
do_break(verto_ctx * ctx,verto_ev * ev)229*7f2fe78bSCy Schubert do_break(verto_ctx *ctx, verto_ev *ev)
230*7f2fe78bSCy Schubert {
231*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Got signal to request exit"));
232*7f2fe78bSCy Schubert     verto_break(ctx);
233*7f2fe78bSCy Schubert }
234*7f2fe78bSCy Schubert 
235*7f2fe78bSCy Schubert struct sighup_context {
236*7f2fe78bSCy Schubert     void *handle;
237*7f2fe78bSCy Schubert     void (*reset)(void *);
238*7f2fe78bSCy Schubert };
239*7f2fe78bSCy Schubert 
240*7f2fe78bSCy Schubert static void
do_reset(verto_ctx * ctx,verto_ev * ev)241*7f2fe78bSCy Schubert do_reset(verto_ctx *ctx, verto_ev *ev)
242*7f2fe78bSCy Schubert {
243*7f2fe78bSCy Schubert     struct sighup_context *sc = (struct sighup_context*) verto_get_private(ev);
244*7f2fe78bSCy Schubert 
245*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Got signal to reset"));
246*7f2fe78bSCy Schubert     krb5_klog_reopen(get_context(sc->handle));
247*7f2fe78bSCy Schubert     if (sc->reset)
248*7f2fe78bSCy Schubert         sc->reset(sc->handle);
249*7f2fe78bSCy Schubert }
250*7f2fe78bSCy Schubert 
251*7f2fe78bSCy Schubert static void
free_sighup_context(verto_ctx * ctx,verto_ev * ev)252*7f2fe78bSCy Schubert free_sighup_context(verto_ctx *ctx, verto_ev *ev)
253*7f2fe78bSCy Schubert {
254*7f2fe78bSCy Schubert     free(verto_get_private(ev));
255*7f2fe78bSCy Schubert }
256*7f2fe78bSCy Schubert 
257*7f2fe78bSCy Schubert krb5_error_code
loop_setup_signals(verto_ctx * ctx,void * handle,void (* reset)())258*7f2fe78bSCy Schubert loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)())
259*7f2fe78bSCy Schubert {
260*7f2fe78bSCy Schubert     struct sighup_context *sc;
261*7f2fe78bSCy Schubert     verto_ev *ev;
262*7f2fe78bSCy Schubert 
263*7f2fe78bSCy Schubert     if (!verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGINT)  ||
264*7f2fe78bSCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGTERM) ||
265*7f2fe78bSCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_break, SIGQUIT) ||
266*7f2fe78bSCy Schubert         !verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, VERTO_SIG_IGN, SIGPIPE))
267*7f2fe78bSCy Schubert         return ENOMEM;
268*7f2fe78bSCy Schubert 
269*7f2fe78bSCy Schubert     ev = verto_add_signal(ctx, VERTO_EV_FLAG_PERSIST, do_reset, SIGHUP);
270*7f2fe78bSCy Schubert     if (!ev)
271*7f2fe78bSCy Schubert         return ENOMEM;
272*7f2fe78bSCy Schubert 
273*7f2fe78bSCy Schubert     sc = malloc(sizeof(*sc));
274*7f2fe78bSCy Schubert     if (!sc)
275*7f2fe78bSCy Schubert         return ENOMEM;
276*7f2fe78bSCy Schubert 
277*7f2fe78bSCy Schubert     sc->handle = handle;
278*7f2fe78bSCy Schubert     sc->reset = reset;
279*7f2fe78bSCy Schubert     verto_set_private(ev, sc, free_sighup_context);
280*7f2fe78bSCy Schubert     return 0;
281*7f2fe78bSCy Schubert }
282*7f2fe78bSCy Schubert 
283*7f2fe78bSCy Schubert /*
284*7f2fe78bSCy Schubert  * Add a bind address to the loop.
285*7f2fe78bSCy Schubert  *
286*7f2fe78bSCy Schubert  * Arguments:
287*7f2fe78bSCy Schubert  * - address
288*7f2fe78bSCy Schubert  *      A string for the address (or hostname).  Pass NULL to use the wildcard
289*7f2fe78bSCy Schubert  *      address.  The address is parsed with k5_parse_host_string().
290*7f2fe78bSCy Schubert  * - port
291*7f2fe78bSCy Schubert  *      What port the socket should be set to.
292*7f2fe78bSCy Schubert  * - type
293*7f2fe78bSCy Schubert  *      bind_type for the socket.
294*7f2fe78bSCy Schubert  * - rpc_data
295*7f2fe78bSCy Schubert  *      For RPC addresses, the svc_register() arguments to use when TCP
296*7f2fe78bSCy Schubert  *      connections are created.  Ignored for other types.
297*7f2fe78bSCy Schubert  */
298*7f2fe78bSCy Schubert static krb5_error_code
loop_add_address(const char * address,int port,enum bind_type type,struct rpc_svc_data * rpc_data)299*7f2fe78bSCy Schubert loop_add_address(const char *address, int port, enum bind_type type,
300*7f2fe78bSCy Schubert                  struct rpc_svc_data *rpc_data)
301*7f2fe78bSCy Schubert {
302*7f2fe78bSCy Schubert     struct bind_address addr, val;
303*7f2fe78bSCy Schubert     int i;
304*7f2fe78bSCy Schubert     void *tmp;
305*7f2fe78bSCy Schubert     char *addr_copy = NULL;
306*7f2fe78bSCy Schubert 
307*7f2fe78bSCy Schubert     assert(!(type == RPC && rpc_data == NULL));
308*7f2fe78bSCy Schubert 
309*7f2fe78bSCy Schubert     /* Make sure a valid port number was passed. */
310*7f2fe78bSCy Schubert     if (port < 0 || port > 65535) {
311*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_ERR, _("Invalid port %d"), port);
312*7f2fe78bSCy Schubert         return EINVAL;
313*7f2fe78bSCy Schubert     }
314*7f2fe78bSCy Schubert 
315*7f2fe78bSCy Schubert     /* Check for conflicting addresses. */
316*7f2fe78bSCy Schubert     FOREACH_ELT(bind_addresses, i, val) {
317*7f2fe78bSCy Schubert         if (type != val.type || port != val.port)
318*7f2fe78bSCy Schubert             continue;
319*7f2fe78bSCy Schubert 
320*7f2fe78bSCy Schubert         /* If a wildcard address is being added, make sure to remove any direct
321*7f2fe78bSCy Schubert          * addresses. */
322*7f2fe78bSCy Schubert         if (address == NULL && val.address != NULL) {
323*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_DEBUG,
324*7f2fe78bSCy Schubert                              _("Removing address %s since wildcard address"
325*7f2fe78bSCy Schubert                                " is being added"),
326*7f2fe78bSCy Schubert                              val.address);
327*7f2fe78bSCy Schubert             free(val.address);
328*7f2fe78bSCy Schubert             DEL(bind_addresses, i);
329*7f2fe78bSCy Schubert         } else if (val.address == NULL || !strcmp(address, val.address)) {
330*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_DEBUG,
331*7f2fe78bSCy Schubert                              _("Address already added to server"));
332*7f2fe78bSCy Schubert             return 0;
333*7f2fe78bSCy Schubert         }
334*7f2fe78bSCy Schubert     }
335*7f2fe78bSCy Schubert 
336*7f2fe78bSCy Schubert     /* Copy the address if it is specified. */
337*7f2fe78bSCy Schubert     if (address != NULL) {
338*7f2fe78bSCy Schubert         addr_copy = strdup(address);
339*7f2fe78bSCy Schubert         if (addr_copy == NULL)
340*7f2fe78bSCy Schubert             return ENOMEM;
341*7f2fe78bSCy Schubert     }
342*7f2fe78bSCy Schubert 
343*7f2fe78bSCy Schubert     /* Add the new address to bind_addresses. */
344*7f2fe78bSCy Schubert     memset(&addr, 0, sizeof(addr));
345*7f2fe78bSCy Schubert     addr.address = addr_copy;
346*7f2fe78bSCy Schubert     addr.port = port;
347*7f2fe78bSCy Schubert     addr.type = type;
348*7f2fe78bSCy Schubert     if (rpc_data != NULL)
349*7f2fe78bSCy Schubert         addr.rpc_svc_data = *rpc_data;
350*7f2fe78bSCy Schubert     if (!ADD(bind_addresses, addr, tmp)) {
351*7f2fe78bSCy Schubert         free(addr_copy);
352*7f2fe78bSCy Schubert         return ENOMEM;
353*7f2fe78bSCy Schubert     }
354*7f2fe78bSCy Schubert 
355*7f2fe78bSCy Schubert     return 0;
356*7f2fe78bSCy Schubert }
357*7f2fe78bSCy Schubert 
358*7f2fe78bSCy Schubert /*
359*7f2fe78bSCy Schubert  * Add bind addresses to the loop.
360*7f2fe78bSCy Schubert  *
361*7f2fe78bSCy Schubert  * Arguments:
362*7f2fe78bSCy Schubert  *
363*7f2fe78bSCy Schubert  * - addresses
364*7f2fe78bSCy Schubert  *      A string for the addresses.  Pass NULL to use the wildcard address.
365*7f2fe78bSCy Schubert  *      Supported delimiters can be found in ADDRESSES_DELIM.  Addresses are
366*7f2fe78bSCy Schubert  *      parsed with k5_parse_host_name().
367*7f2fe78bSCy Schubert  * - default_port
368*7f2fe78bSCy Schubert  *      What port the socket should be set to if not specified in addresses.
369*7f2fe78bSCy Schubert  * - type
370*7f2fe78bSCy Schubert  *      bind_type for the socket.
371*7f2fe78bSCy Schubert  * - rpc_data
372*7f2fe78bSCy Schubert  *      For RPC addresses, the svc_register() arguments to use when TCP
373*7f2fe78bSCy Schubert  *      connections are created.  Ignored for other types.
374*7f2fe78bSCy Schubert  */
375*7f2fe78bSCy Schubert static krb5_error_code
loop_add_addresses(const char * addresses,int default_port,enum bind_type type,struct rpc_svc_data * rpc_data)376*7f2fe78bSCy Schubert loop_add_addresses(const char *addresses, int default_port,
377*7f2fe78bSCy Schubert                    enum bind_type type, struct rpc_svc_data *rpc_data)
378*7f2fe78bSCy Schubert {
379*7f2fe78bSCy Schubert     krb5_error_code ret = 0;
380*7f2fe78bSCy Schubert     char *addresses_copy = NULL, *host = NULL, *saveptr, *addr;
381*7f2fe78bSCy Schubert     int port;
382*7f2fe78bSCy Schubert 
383*7f2fe78bSCy Schubert     /* If no addresses are set, add a wildcard address. */
384*7f2fe78bSCy Schubert     if (addresses == NULL)
385*7f2fe78bSCy Schubert         return loop_add_address(NULL, default_port, type, rpc_data);
386*7f2fe78bSCy Schubert 
387*7f2fe78bSCy Schubert     /* Copy the addresses string before using strtok(). */
388*7f2fe78bSCy Schubert     addresses_copy = strdup(addresses);
389*7f2fe78bSCy Schubert     if (addresses_copy == NULL) {
390*7f2fe78bSCy Schubert         ret = ENOMEM;
391*7f2fe78bSCy Schubert         goto cleanup;
392*7f2fe78bSCy Schubert     }
393*7f2fe78bSCy Schubert 
394*7f2fe78bSCy Schubert     /* Start tokenizing the addresses string.  If we get NULL the string
395*7f2fe78bSCy Schubert      * contained no addresses, so add a wildcard address. */
396*7f2fe78bSCy Schubert     addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr);
397*7f2fe78bSCy Schubert     if (addr == NULL) {
398*7f2fe78bSCy Schubert         ret = loop_add_address(NULL, default_port, type, rpc_data);
399*7f2fe78bSCy Schubert         goto cleanup;
400*7f2fe78bSCy Schubert     }
401*7f2fe78bSCy Schubert 
402*7f2fe78bSCy Schubert     /* Loop through each address and add it to the loop. */
403*7f2fe78bSCy Schubert     for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) {
404*7f2fe78bSCy Schubert         /* Parse the host string. */
405*7f2fe78bSCy Schubert         ret = k5_parse_host_string(addr, default_port, &host, &port);
406*7f2fe78bSCy Schubert         if (ret)
407*7f2fe78bSCy Schubert             goto cleanup;
408*7f2fe78bSCy Schubert 
409*7f2fe78bSCy Schubert         ret = loop_add_address(host, port, type, rpc_data);
410*7f2fe78bSCy Schubert         if (ret)
411*7f2fe78bSCy Schubert             goto cleanup;
412*7f2fe78bSCy Schubert 
413*7f2fe78bSCy Schubert         free(host);
414*7f2fe78bSCy Schubert         host = NULL;
415*7f2fe78bSCy Schubert     }
416*7f2fe78bSCy Schubert 
417*7f2fe78bSCy Schubert cleanup:
418*7f2fe78bSCy Schubert     free(addresses_copy);
419*7f2fe78bSCy Schubert     free(host);
420*7f2fe78bSCy Schubert     return ret;
421*7f2fe78bSCy Schubert }
422*7f2fe78bSCy Schubert 
423*7f2fe78bSCy Schubert krb5_error_code
loop_add_udp_address(int default_port,const char * addresses)424*7f2fe78bSCy Schubert loop_add_udp_address(int default_port, const char *addresses)
425*7f2fe78bSCy Schubert {
426*7f2fe78bSCy Schubert     return loop_add_addresses(addresses, default_port, UDP, NULL);
427*7f2fe78bSCy Schubert }
428*7f2fe78bSCy Schubert 
429*7f2fe78bSCy Schubert krb5_error_code
loop_add_tcp_address(int default_port,const char * addresses)430*7f2fe78bSCy Schubert loop_add_tcp_address(int default_port, const char *addresses)
431*7f2fe78bSCy Schubert {
432*7f2fe78bSCy Schubert     return loop_add_addresses(addresses, default_port, TCP, NULL);
433*7f2fe78bSCy Schubert }
434*7f2fe78bSCy Schubert 
435*7f2fe78bSCy Schubert krb5_error_code
loop_add_rpc_service(int default_port,const char * addresses,u_long prognum,u_long versnum,void (* dispatchfn)())436*7f2fe78bSCy Schubert loop_add_rpc_service(int default_port, const char *addresses, u_long prognum,
437*7f2fe78bSCy Schubert                      u_long versnum, void (*dispatchfn)())
438*7f2fe78bSCy Schubert {
439*7f2fe78bSCy Schubert     struct rpc_svc_data svc;
440*7f2fe78bSCy Schubert 
441*7f2fe78bSCy Schubert     svc.prognum = prognum;
442*7f2fe78bSCy Schubert     svc.versnum = versnum;
443*7f2fe78bSCy Schubert     svc.dispatch = dispatchfn;
444*7f2fe78bSCy Schubert     return loop_add_addresses(addresses, default_port, RPC, &svc);
445*7f2fe78bSCy Schubert }
446*7f2fe78bSCy Schubert 
447*7f2fe78bSCy Schubert #define USE_AF AF_INET
448*7f2fe78bSCy Schubert #define USE_TYPE SOCK_DGRAM
449*7f2fe78bSCy Schubert #define USE_PROTO 0
450*7f2fe78bSCy Schubert #define SOCKET_ERRNO errno
451*7f2fe78bSCy Schubert #include "foreachaddr.h"
452*7f2fe78bSCy Schubert 
453*7f2fe78bSCy Schubert static void
free_connection(struct connection * conn)454*7f2fe78bSCy Schubert free_connection(struct connection *conn)
455*7f2fe78bSCy Schubert {
456*7f2fe78bSCy Schubert     if (!conn)
457*7f2fe78bSCy Schubert         return;
458*7f2fe78bSCy Schubert     if (conn->response)
459*7f2fe78bSCy Schubert         krb5_free_data(get_context(conn->handle), conn->response);
460*7f2fe78bSCy Schubert     if (conn->buffer)
461*7f2fe78bSCy Schubert         free(conn->buffer);
462*7f2fe78bSCy Schubert     if (conn->type == CONN_RPC_LISTENER && conn->transp != NULL)
463*7f2fe78bSCy Schubert         svc_destroy(conn->transp);
464*7f2fe78bSCy Schubert     free(conn);
465*7f2fe78bSCy Schubert }
466*7f2fe78bSCy Schubert 
467*7f2fe78bSCy Schubert static void
remove_event_from_set(verto_ev * ev)468*7f2fe78bSCy Schubert remove_event_from_set(verto_ev *ev)
469*7f2fe78bSCy Schubert {
470*7f2fe78bSCy Schubert     verto_ev *tmp;
471*7f2fe78bSCy Schubert     int i;
472*7f2fe78bSCy Schubert 
473*7f2fe78bSCy Schubert     /* Remove the event from the events. */
474*7f2fe78bSCy Schubert     FOREACH_ELT(events, i, tmp)
475*7f2fe78bSCy Schubert         if (tmp == ev) {
476*7f2fe78bSCy Schubert             DEL(events, i);
477*7f2fe78bSCy Schubert             break;
478*7f2fe78bSCy Schubert         }
479*7f2fe78bSCy Schubert }
480*7f2fe78bSCy Schubert 
481*7f2fe78bSCy Schubert static void
free_socket(verto_ctx * ctx,verto_ev * ev)482*7f2fe78bSCy Schubert free_socket(verto_ctx *ctx, verto_ev *ev)
483*7f2fe78bSCy Schubert {
484*7f2fe78bSCy Schubert     struct connection *conn = NULL;
485*7f2fe78bSCy Schubert     fd_set fds;
486*7f2fe78bSCy Schubert     int fd;
487*7f2fe78bSCy Schubert 
488*7f2fe78bSCy Schubert     remove_event_from_set(ev);
489*7f2fe78bSCy Schubert 
490*7f2fe78bSCy Schubert     fd = verto_get_fd(ev);
491*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
492*7f2fe78bSCy Schubert 
493*7f2fe78bSCy Schubert     /* Close the file descriptor. */
494*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_INFO, _("closing down fd %d"), fd);
495*7f2fe78bSCy Schubert     if (fd >= 0 && (!conn || conn->type != CONN_RPC || conn->rpc_force_close))
496*7f2fe78bSCy Schubert         close(fd);
497*7f2fe78bSCy Schubert 
498*7f2fe78bSCy Schubert     /* Free the connection struct. */
499*7f2fe78bSCy Schubert     if (conn) {
500*7f2fe78bSCy Schubert         switch (conn->type) {
501*7f2fe78bSCy Schubert         case CONN_RPC:
502*7f2fe78bSCy Schubert             if (conn->rpc_force_close) {
503*7f2fe78bSCy Schubert                 FD_ZERO(&fds);
504*7f2fe78bSCy Schubert                 FD_SET(fd, &fds);
505*7f2fe78bSCy Schubert                 svc_getreqset(&fds);
506*7f2fe78bSCy Schubert                 if (FD_ISSET(fd, &svc_fdset)) {
507*7f2fe78bSCy Schubert                     krb5_klog_syslog(LOG_ERR,
508*7f2fe78bSCy Schubert                                      _("descriptor %d closed but still "
509*7f2fe78bSCy Schubert                                        "in svc_fdset"),
510*7f2fe78bSCy Schubert                                      fd);
511*7f2fe78bSCy Schubert                 }
512*7f2fe78bSCy Schubert             }
513*7f2fe78bSCy Schubert             /* Fall through. */
514*7f2fe78bSCy Schubert         case CONN_TCP:
515*7f2fe78bSCy Schubert             tcp_or_rpc_data_counter--;
516*7f2fe78bSCy Schubert             break;
517*7f2fe78bSCy Schubert         default:
518*7f2fe78bSCy Schubert             break;
519*7f2fe78bSCy Schubert         }
520*7f2fe78bSCy Schubert 
521*7f2fe78bSCy Schubert         free_connection(conn);
522*7f2fe78bSCy Schubert     }
523*7f2fe78bSCy Schubert }
524*7f2fe78bSCy Schubert 
525*7f2fe78bSCy Schubert static verto_ev *
make_event(verto_ctx * ctx,verto_ev_flag flags,verto_callback callback,int sock,struct connection * conn)526*7f2fe78bSCy Schubert make_event(verto_ctx *ctx, verto_ev_flag flags, verto_callback callback,
527*7f2fe78bSCy Schubert            int sock, struct connection *conn)
528*7f2fe78bSCy Schubert {
529*7f2fe78bSCy Schubert     verto_ev *ev;
530*7f2fe78bSCy Schubert     void *tmp;
531*7f2fe78bSCy Schubert 
532*7f2fe78bSCy Schubert     ev = verto_add_io(ctx, flags, callback, sock);
533*7f2fe78bSCy Schubert     if (!ev) {
534*7f2fe78bSCy Schubert         com_err(conn->prog, ENOMEM, _("cannot create io event"));
535*7f2fe78bSCy Schubert         return NULL;
536*7f2fe78bSCy Schubert     }
537*7f2fe78bSCy Schubert 
538*7f2fe78bSCy Schubert     if (!ADD(events, ev, tmp)) {
539*7f2fe78bSCy Schubert         com_err(conn->prog, ENOMEM, _("cannot save event"));
540*7f2fe78bSCy Schubert         verto_del(ev);
541*7f2fe78bSCy Schubert         return NULL;
542*7f2fe78bSCy Schubert     }
543*7f2fe78bSCy Schubert 
544*7f2fe78bSCy Schubert     verto_set_private(ev, conn, free_socket);
545*7f2fe78bSCy Schubert     return ev;
546*7f2fe78bSCy Schubert }
547*7f2fe78bSCy Schubert 
548*7f2fe78bSCy Schubert static krb5_error_code
add_fd(int sock,enum conn_type conntype,verto_ev_flag flags,void * handle,const char * prog,verto_ctx * ctx,verto_callback callback,verto_ev ** ev_out)549*7f2fe78bSCy Schubert add_fd(int sock, enum conn_type conntype, verto_ev_flag flags, void *handle,
550*7f2fe78bSCy Schubert        const char *prog, verto_ctx *ctx, verto_callback callback,
551*7f2fe78bSCy Schubert        verto_ev **ev_out)
552*7f2fe78bSCy Schubert {
553*7f2fe78bSCy Schubert     struct connection *newconn;
554*7f2fe78bSCy Schubert 
555*7f2fe78bSCy Schubert     *ev_out = NULL;
556*7f2fe78bSCy Schubert 
557*7f2fe78bSCy Schubert #ifndef _WIN32
558*7f2fe78bSCy Schubert     if (sock >= FD_SETSIZE) {
559*7f2fe78bSCy Schubert         com_err(prog, 0, _("file descriptor number %d too high"), sock);
560*7f2fe78bSCy Schubert         return EMFILE;
561*7f2fe78bSCy Schubert     }
562*7f2fe78bSCy Schubert #endif
563*7f2fe78bSCy Schubert     newconn = malloc(sizeof(*newconn));
564*7f2fe78bSCy Schubert     if (newconn == NULL) {
565*7f2fe78bSCy Schubert         com_err(prog, ENOMEM,
566*7f2fe78bSCy Schubert                 _("cannot allocate storage for connection info"));
567*7f2fe78bSCy Schubert         return ENOMEM;
568*7f2fe78bSCy Schubert     }
569*7f2fe78bSCy Schubert     memset(newconn, 0, sizeof(*newconn));
570*7f2fe78bSCy Schubert     newconn->handle = handle;
571*7f2fe78bSCy Schubert     newconn->prog = prog;
572*7f2fe78bSCy Schubert     newconn->type = conntype;
573*7f2fe78bSCy Schubert 
574*7f2fe78bSCy Schubert     *ev_out = make_event(ctx, flags, callback, sock, newconn);
575*7f2fe78bSCy Schubert     return 0;
576*7f2fe78bSCy Schubert }
577*7f2fe78bSCy Schubert 
578*7f2fe78bSCy Schubert static void process_packet(verto_ctx *ctx, verto_ev *ev);
579*7f2fe78bSCy Schubert static void accept_tcp_connection(verto_ctx *ctx, verto_ev *ev);
580*7f2fe78bSCy Schubert static void process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev);
581*7f2fe78bSCy Schubert static void process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev);
582*7f2fe78bSCy Schubert static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);
583*7f2fe78bSCy Schubert static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);
584*7f2fe78bSCy Schubert 
585*7f2fe78bSCy Schubert /*
586*7f2fe78bSCy Schubert  * Create a socket and bind it to addr.  Ensure the socket will work with
587*7f2fe78bSCy Schubert  * select().  Set the socket cloexec, reuseaddr, and if applicable v6-only.
588*7f2fe78bSCy Schubert  * Does not call listen().  On failure, log an error and return an error code.
589*7f2fe78bSCy Schubert  */
590*7f2fe78bSCy Schubert static krb5_error_code
create_server_socket(struct sockaddr * addr,int type,const char * prog,int * fd_out)591*7f2fe78bSCy Schubert create_server_socket(struct sockaddr *addr, int type, const char *prog,
592*7f2fe78bSCy Schubert                      int *fd_out)
593*7f2fe78bSCy Schubert {
594*7f2fe78bSCy Schubert     int sock, e;
595*7f2fe78bSCy Schubert 
596*7f2fe78bSCy Schubert     *fd_out = -1;
597*7f2fe78bSCy Schubert 
598*7f2fe78bSCy Schubert     sock = socket(addr->sa_family, type, 0);
599*7f2fe78bSCy Schubert     if (sock == -1) {
600*7f2fe78bSCy Schubert         e = errno;
601*7f2fe78bSCy Schubert         com_err(prog, e, _("Cannot create TCP server socket on %s"),
602*7f2fe78bSCy Schubert                 paddr(addr));
603*7f2fe78bSCy Schubert         return e;
604*7f2fe78bSCy Schubert     }
605*7f2fe78bSCy Schubert     set_cloexec_fd(sock);
606*7f2fe78bSCy Schubert 
607*7f2fe78bSCy Schubert #ifndef _WIN32                  /* Windows FD_SETSIZE is a count. */
608*7f2fe78bSCy Schubert     if (sock >= FD_SETSIZE) {
609*7f2fe78bSCy Schubert         close(sock);
610*7f2fe78bSCy Schubert         com_err(prog, 0, _("TCP socket fd number %d (for %s) too high"),
611*7f2fe78bSCy Schubert                 sock, paddr(addr));
612*7f2fe78bSCy Schubert         return EMFILE;
613*7f2fe78bSCy Schubert     }
614*7f2fe78bSCy Schubert #endif
615*7f2fe78bSCy Schubert 
616*7f2fe78bSCy Schubert     if (setreuseaddr(sock, 1) < 0)
617*7f2fe78bSCy Schubert         com_err(prog, errno, _("Cannot enable SO_REUSEADDR on fd %d"), sock);
618*7f2fe78bSCy Schubert 
619*7f2fe78bSCy Schubert     if (addr->sa_family == AF_INET6) {
620*7f2fe78bSCy Schubert #ifdef IPV6_V6ONLY
621*7f2fe78bSCy Schubert         if (setv6only(sock, 1)) {
622*7f2fe78bSCy Schubert             com_err(prog, errno, _("setsockopt(%d,IPV6_V6ONLY,1) failed"),
623*7f2fe78bSCy Schubert                     sock);
624*7f2fe78bSCy Schubert         } else {
625*7f2fe78bSCy Schubert             com_err(prog, 0, _("setsockopt(%d,IPV6_V6ONLY,1) worked"), sock);
626*7f2fe78bSCy Schubert         }
627*7f2fe78bSCy Schubert #else
628*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_INFO, _("no IPV6_V6ONLY socket option support"));
629*7f2fe78bSCy Schubert #endif /* IPV6_V6ONLY */
630*7f2fe78bSCy Schubert     }
631*7f2fe78bSCy Schubert 
632*7f2fe78bSCy Schubert     if (bind(sock, addr, sa_socklen(addr)) == -1) {
633*7f2fe78bSCy Schubert         e = errno;
634*7f2fe78bSCy Schubert         com_err(prog, e, _("Cannot bind server socket on %s"), paddr(addr));
635*7f2fe78bSCy Schubert         close(sock);
636*7f2fe78bSCy Schubert         return e;
637*7f2fe78bSCy Schubert     }
638*7f2fe78bSCy Schubert 
639*7f2fe78bSCy Schubert     *fd_out = sock;
640*7f2fe78bSCy Schubert     return 0;
641*7f2fe78bSCy Schubert }
642*7f2fe78bSCy Schubert 
643*7f2fe78bSCy Schubert static const int one = 1;
644*7f2fe78bSCy Schubert 
645*7f2fe78bSCy Schubert static int
setnbio(int sock)646*7f2fe78bSCy Schubert setnbio(int sock)
647*7f2fe78bSCy Schubert {
648*7f2fe78bSCy Schubert     return ioctlsocket(sock, FIONBIO, (const void *)&one);
649*7f2fe78bSCy Schubert }
650*7f2fe78bSCy Schubert 
651*7f2fe78bSCy Schubert static int
setkeepalive(int sock)652*7f2fe78bSCy Schubert setkeepalive(int sock)
653*7f2fe78bSCy Schubert {
654*7f2fe78bSCy Schubert     return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
655*7f2fe78bSCy Schubert }
656*7f2fe78bSCy Schubert 
657*7f2fe78bSCy Schubert static int
setnolinger(int s)658*7f2fe78bSCy Schubert setnolinger(int s)
659*7f2fe78bSCy Schubert {
660*7f2fe78bSCy Schubert     static const struct linger ling = { 0, 0 };
661*7f2fe78bSCy Schubert     return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
662*7f2fe78bSCy Schubert }
663*7f2fe78bSCy Schubert 
664*7f2fe78bSCy Schubert /* An enum map to socket families for each bind_type. */
665*7f2fe78bSCy Schubert static const int bind_socktypes[] =
666*7f2fe78bSCy Schubert {
667*7f2fe78bSCy Schubert     [UDP] = SOCK_DGRAM,
668*7f2fe78bSCy Schubert     [TCP] = SOCK_STREAM,
669*7f2fe78bSCy Schubert     [RPC] = SOCK_STREAM
670*7f2fe78bSCy Schubert };
671*7f2fe78bSCy Schubert 
672*7f2fe78bSCy Schubert /* An enum map containing conn_type (for struct connection) for each
673*7f2fe78bSCy Schubert  * bind_type.  */
674*7f2fe78bSCy Schubert static const enum conn_type bind_conn_types[] =
675*7f2fe78bSCy Schubert {
676*7f2fe78bSCy Schubert     [UDP] = CONN_UDP,
677*7f2fe78bSCy Schubert     [TCP] = CONN_TCP_LISTENER,
678*7f2fe78bSCy Schubert     [RPC] = CONN_RPC_LISTENER
679*7f2fe78bSCy Schubert };
680*7f2fe78bSCy Schubert 
681*7f2fe78bSCy Schubert /*
682*7f2fe78bSCy Schubert  * Set up a listening socket.
683*7f2fe78bSCy Schubert  *
684*7f2fe78bSCy Schubert  * Arguments:
685*7f2fe78bSCy Schubert  *
686*7f2fe78bSCy Schubert  * - ba
687*7f2fe78bSCy Schubert  *      The bind address and port for the socket.
688*7f2fe78bSCy Schubert  * - ai
689*7f2fe78bSCy Schubert  *      The addrinfo struct to use for creating the socket.
690*7f2fe78bSCy Schubert  * - ctype
691*7f2fe78bSCy Schubert  *      The conn_type of this socket.
692*7f2fe78bSCy Schubert  */
693*7f2fe78bSCy Schubert static krb5_error_code
setup_socket(struct bind_address * ba,struct sockaddr * sock_address,void * handle,const char * prog,verto_ctx * ctx,int tcp_listen_backlog,verto_callback vcb,enum conn_type ctype)694*7f2fe78bSCy Schubert setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
695*7f2fe78bSCy Schubert              void *handle, const char *prog, verto_ctx *ctx,
696*7f2fe78bSCy Schubert              int tcp_listen_backlog, verto_callback vcb, enum conn_type ctype)
697*7f2fe78bSCy Schubert {
698*7f2fe78bSCy Schubert     krb5_error_code ret;
699*7f2fe78bSCy Schubert     struct connection *conn;
700*7f2fe78bSCy Schubert     verto_ev_flag flags;
701*7f2fe78bSCy Schubert     verto_ev *ev = NULL;
702*7f2fe78bSCy Schubert     int sock = -1;
703*7f2fe78bSCy Schubert 
704*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"),
705*7f2fe78bSCy Schubert                      bind_type_names[ba->type], paddr(sock_address));
706*7f2fe78bSCy Schubert 
707*7f2fe78bSCy Schubert     /* Create the socket. */
708*7f2fe78bSCy Schubert     ret = create_server_socket(sock_address, bind_socktypes[ba->type], prog,
709*7f2fe78bSCy Schubert                                &sock);
710*7f2fe78bSCy Schubert     if (ret)
711*7f2fe78bSCy Schubert         goto cleanup;
712*7f2fe78bSCy Schubert 
713*7f2fe78bSCy Schubert     /* Listen for backlogged connections on TCP sockets.  (For RPC sockets this
714*7f2fe78bSCy Schubert      * will be done by svc_register().) */
715*7f2fe78bSCy Schubert     if (ba->type == TCP && listen(sock, tcp_listen_backlog) != 0) {
716*7f2fe78bSCy Schubert         ret = errno;
717*7f2fe78bSCy Schubert         com_err(prog, errno, _("Cannot listen on %s server socket on %s"),
718*7f2fe78bSCy Schubert                 bind_type_names[ba->type], paddr(sock_address));
719*7f2fe78bSCy Schubert         goto cleanup;
720*7f2fe78bSCy Schubert     }
721*7f2fe78bSCy Schubert 
722*7f2fe78bSCy Schubert     /* Set non-blocking I/O for UDP and TCP listener sockets. */
723*7f2fe78bSCy Schubert     if (ba->type != RPC && setnbio(sock) != 0) {
724*7f2fe78bSCy Schubert         ret = errno;
725*7f2fe78bSCy Schubert         com_err(prog, errno,
726*7f2fe78bSCy Schubert                 _("cannot set listening %s socket on %s non-blocking"),
727*7f2fe78bSCy Schubert                 bind_type_names[ba->type], paddr(sock_address));
728*7f2fe78bSCy Schubert         goto cleanup;
729*7f2fe78bSCy Schubert     }
730*7f2fe78bSCy Schubert 
731*7f2fe78bSCy Schubert     /* Turn off the linger option for TCP sockets. */
732*7f2fe78bSCy Schubert     if (ba->type == TCP && setnolinger(sock) != 0) {
733*7f2fe78bSCy Schubert         ret = errno;
734*7f2fe78bSCy Schubert         com_err(prog, errno, _("cannot set SO_LINGER on %s socket on %s"),
735*7f2fe78bSCy Schubert                 bind_type_names[ba->type], paddr(sock_address));
736*7f2fe78bSCy Schubert         goto cleanup;
737*7f2fe78bSCy Schubert     }
738*7f2fe78bSCy Schubert 
739*7f2fe78bSCy Schubert     /* Try to turn on pktinfo for UDP wildcard sockets. */
740*7f2fe78bSCy Schubert     if (ba->type == UDP && sa_is_wildcard(sock_address)) {
741*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_DEBUG, _("Setting pktinfo on socket %s"),
742*7f2fe78bSCy Schubert                          paddr(sock_address));
743*7f2fe78bSCy Schubert         ret = set_pktinfo(sock, sock_address->sa_family);
744*7f2fe78bSCy Schubert         if (ret) {
745*7f2fe78bSCy Schubert             com_err(prog, ret,
746*7f2fe78bSCy Schubert                     _("Cannot request packet info for UDP socket address "
747*7f2fe78bSCy Schubert                       "%s port %d"), paddr(sock_address), ba->port);
748*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_INFO, _("System does not support pktinfo yet "
749*7f2fe78bSCy Schubert                                          "binding to a wildcard address.  "
750*7f2fe78bSCy Schubert                                          "Packets are not guaranteed to "
751*7f2fe78bSCy Schubert                                          "return on the received address."));
752*7f2fe78bSCy Schubert         }
753*7f2fe78bSCy Schubert     }
754*7f2fe78bSCy Schubert 
755*7f2fe78bSCy Schubert     /* Add the socket to the event loop. */
756*7f2fe78bSCy Schubert     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST |
757*7f2fe78bSCy Schubert         VERTO_EV_FLAG_REINITIABLE;
758*7f2fe78bSCy Schubert     ret = add_fd(sock, ctype, flags, handle, prog, ctx, vcb, &ev);
759*7f2fe78bSCy Schubert     if (ret) {
760*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_ERR, _("Error attempting to add verto event"));
761*7f2fe78bSCy Schubert         goto cleanup;
762*7f2fe78bSCy Schubert     }
763*7f2fe78bSCy Schubert 
764*7f2fe78bSCy Schubert     if (ba->type == RPC) {
765*7f2fe78bSCy Schubert         conn = verto_get_private(ev);
766*7f2fe78bSCy Schubert         conn->transp = svctcp_create(sock, 0, 0);
767*7f2fe78bSCy Schubert         if (conn->transp == NULL) {
768*7f2fe78bSCy Schubert             ret = errno;
769*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_ERR, _("Cannot create RPC service: %s"),
770*7f2fe78bSCy Schubert                              strerror(ret));
771*7f2fe78bSCy Schubert             goto cleanup;
772*7f2fe78bSCy Schubert         }
773*7f2fe78bSCy Schubert 
774*7f2fe78bSCy Schubert         ret = svc_register(conn->transp, ba->rpc_svc_data.prognum,
775*7f2fe78bSCy Schubert                            ba->rpc_svc_data.versnum, ba->rpc_svc_data.dispatch,
776*7f2fe78bSCy Schubert                            0);
777*7f2fe78bSCy Schubert         if (!ret) {
778*7f2fe78bSCy Schubert             ret = errno;
779*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_ERR, _("Cannot register RPC service: %s"),
780*7f2fe78bSCy Schubert                              strerror(ret));
781*7f2fe78bSCy Schubert             goto cleanup;
782*7f2fe78bSCy Schubert         }
783*7f2fe78bSCy Schubert     }
784*7f2fe78bSCy Schubert 
785*7f2fe78bSCy Schubert     ev = NULL;
786*7f2fe78bSCy Schubert     sock = -1;
787*7f2fe78bSCy Schubert     ret = 0;
788*7f2fe78bSCy Schubert 
789*7f2fe78bSCy Schubert cleanup:
790*7f2fe78bSCy Schubert     if (sock >= 0)
791*7f2fe78bSCy Schubert         close(sock);
792*7f2fe78bSCy Schubert     if (ev != NULL)
793*7f2fe78bSCy Schubert         verto_del(ev);
794*7f2fe78bSCy Schubert     return ret;
795*7f2fe78bSCy Schubert }
796*7f2fe78bSCy Schubert 
797*7f2fe78bSCy Schubert /*
798*7f2fe78bSCy Schubert  * Setup all the socket addresses that the net-server should listen to.
799*7f2fe78bSCy Schubert  *
800*7f2fe78bSCy Schubert  * This function uses getaddrinfo to figure out all the addresses. This will
801*7f2fe78bSCy Schubert  * automatically figure out which socket families that should be used on the
802*7f2fe78bSCy Schubert  * host making it useful even for wildcard addresses.
803*7f2fe78bSCy Schubert  */
804*7f2fe78bSCy Schubert static krb5_error_code
setup_addresses(verto_ctx * ctx,void * handle,const char * prog,int tcp_listen_backlog)805*7f2fe78bSCy Schubert setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
806*7f2fe78bSCy Schubert                 int tcp_listen_backlog)
807*7f2fe78bSCy Schubert {
808*7f2fe78bSCy Schubert     /* An bind_type enum map for the verto callback functions. */
809*7f2fe78bSCy Schubert     static verto_callback *const verto_callbacks[] = {
810*7f2fe78bSCy Schubert         [UDP] = &process_packet,
811*7f2fe78bSCy Schubert         [TCP] = &accept_tcp_connection,
812*7f2fe78bSCy Schubert         [RPC] = &accept_rpc_connection
813*7f2fe78bSCy Schubert     };
814*7f2fe78bSCy Schubert     krb5_error_code ret = 0;
815*7f2fe78bSCy Schubert     size_t i;
816*7f2fe78bSCy Schubert     int err, bound_any;
817*7f2fe78bSCy Schubert     struct bind_address addr;
818*7f2fe78bSCy Schubert     struct addrinfo hints, *ai_list = NULL, *ai = NULL;
819*7f2fe78bSCy Schubert     verto_callback vcb;
820*7f2fe78bSCy Schubert 
821*7f2fe78bSCy Schubert     /* Check to make sure addresses were added to the server. */
822*7f2fe78bSCy Schubert     if (bind_addresses.n == 0) {
823*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_ERR, _("No addresses added to the net server"));
824*7f2fe78bSCy Schubert         return EINVAL;
825*7f2fe78bSCy Schubert     }
826*7f2fe78bSCy Schubert 
827*7f2fe78bSCy Schubert     /* Ask for all address families, listener addresses, and no port name
828*7f2fe78bSCy Schubert      * resolution. */
829*7f2fe78bSCy Schubert     memset(&hints, 0, sizeof(struct addrinfo));
830*7f2fe78bSCy Schubert     hints.ai_family = AF_UNSPEC;
831*7f2fe78bSCy Schubert     hints.ai_flags = AI_PASSIVE;
832*7f2fe78bSCy Schubert #ifdef AI_NUMERICSERV
833*7f2fe78bSCy Schubert     hints.ai_flags |= AI_NUMERICSERV;
834*7f2fe78bSCy Schubert #endif
835*7f2fe78bSCy Schubert 
836*7f2fe78bSCy Schubert     /* Add all the requested addresses. */
837*7f2fe78bSCy Schubert     for (i = 0; i < bind_addresses.n; i++) {
838*7f2fe78bSCy Schubert         addr = bind_addresses.data[i];
839*7f2fe78bSCy Schubert         hints.ai_socktype = bind_socktypes[addr.type];
840*7f2fe78bSCy Schubert 
841*7f2fe78bSCy Schubert         /* Call getaddrinfo, using a dummy port value. */
842*7f2fe78bSCy Schubert         err = getaddrinfo(addr.address, "0", &hints, &ai_list);
843*7f2fe78bSCy Schubert         if (err) {
844*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_ERR,
845*7f2fe78bSCy Schubert                              _("Failed getting address info (for %s): %s"),
846*7f2fe78bSCy Schubert                              (addr.address == NULL) ? "<wildcard>" :
847*7f2fe78bSCy Schubert                              addr.address, gai_strerror(err));
848*7f2fe78bSCy Schubert             ret = EIO;
849*7f2fe78bSCy Schubert             goto cleanup;
850*7f2fe78bSCy Schubert         }
851*7f2fe78bSCy Schubert 
852*7f2fe78bSCy Schubert         /*
853*7f2fe78bSCy Schubert          * Loop through all the sockets that getaddrinfo could find to match
854*7f2fe78bSCy Schubert          * the requested address.  For wildcard listeners, this should usually
855*7f2fe78bSCy Schubert          * have two results, one for each of IPv4 and IPv6, or one or the
856*7f2fe78bSCy Schubert          * other, depending on the system.  On IPv4-only systems, getaddrinfo()
857*7f2fe78bSCy Schubert          * may return both IPv4 and IPv6 addresses, but creating an IPv6 socket
858*7f2fe78bSCy Schubert          * may give an EAFNOSUPPORT error, so tolerate that error as long as we
859*7f2fe78bSCy Schubert          * can bind at least one socket.
860*7f2fe78bSCy Schubert          */
861*7f2fe78bSCy Schubert         bound_any = 0;
862*7f2fe78bSCy Schubert         for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
863*7f2fe78bSCy Schubert             /* Make sure getaddrinfo returned a socket with the same type that
864*7f2fe78bSCy Schubert              * was requested. */
865*7f2fe78bSCy Schubert             assert(hints.ai_socktype == ai->ai_socktype);
866*7f2fe78bSCy Schubert 
867*7f2fe78bSCy Schubert             /* Set the real port number. */
868*7f2fe78bSCy Schubert             sa_setport(ai->ai_addr, addr.port);
869*7f2fe78bSCy Schubert 
870*7f2fe78bSCy Schubert             ret = setup_socket(&addr, ai->ai_addr, handle, prog, ctx,
871*7f2fe78bSCy Schubert                                tcp_listen_backlog, verto_callbacks[addr.type],
872*7f2fe78bSCy Schubert                                bind_conn_types[addr.type]);
873*7f2fe78bSCy Schubert             if (ret) {
874*7f2fe78bSCy Schubert                 krb5_klog_syslog(LOG_ERR,
875*7f2fe78bSCy Schubert                                  _("Failed setting up a %s socket (for %s)"),
876*7f2fe78bSCy Schubert                                  bind_type_names[addr.type],
877*7f2fe78bSCy Schubert                                  paddr(ai->ai_addr));
878*7f2fe78bSCy Schubert                 if (ret != EAFNOSUPPORT)
879*7f2fe78bSCy Schubert                     goto cleanup;
880*7f2fe78bSCy Schubert             } else {
881*7f2fe78bSCy Schubert                 bound_any = 1;
882*7f2fe78bSCy Schubert             }
883*7f2fe78bSCy Schubert         }
884*7f2fe78bSCy Schubert         if (!bound_any)
885*7f2fe78bSCy Schubert             goto cleanup;
886*7f2fe78bSCy Schubert         ret = 0;
887*7f2fe78bSCy Schubert 
888*7f2fe78bSCy Schubert         if (ai_list != NULL)
889*7f2fe78bSCy Schubert             freeaddrinfo(ai_list);
890*7f2fe78bSCy Schubert         ai_list = NULL;
891*7f2fe78bSCy Schubert     }
892*7f2fe78bSCy Schubert 
893*7f2fe78bSCy Schubert cleanup:
894*7f2fe78bSCy Schubert     if (ai_list != NULL)
895*7f2fe78bSCy Schubert         freeaddrinfo(ai_list);
896*7f2fe78bSCy Schubert     return ret;
897*7f2fe78bSCy Schubert }
898*7f2fe78bSCy Schubert 
899*7f2fe78bSCy Schubert krb5_error_code
loop_setup_network(verto_ctx * ctx,void * handle,const char * prog,int tcp_listen_backlog)900*7f2fe78bSCy Schubert loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,
901*7f2fe78bSCy Schubert                    int tcp_listen_backlog)
902*7f2fe78bSCy Schubert {
903*7f2fe78bSCy Schubert     krb5_error_code ret;
904*7f2fe78bSCy Schubert     verto_ev *ev;
905*7f2fe78bSCy Schubert     int i;
906*7f2fe78bSCy Schubert 
907*7f2fe78bSCy Schubert     /* Check to make sure that at least one address was added to the loop. */
908*7f2fe78bSCy Schubert     if (bind_addresses.n == 0)
909*7f2fe78bSCy Schubert         return EINVAL;
910*7f2fe78bSCy Schubert 
911*7f2fe78bSCy Schubert     /* Close any open connections. */
912*7f2fe78bSCy Schubert     FOREACH_ELT(events, i, ev)
913*7f2fe78bSCy Schubert         verto_del(ev);
914*7f2fe78bSCy Schubert     events.n = 0;
915*7f2fe78bSCy Schubert 
916*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_INFO, _("setting up network..."));
917*7f2fe78bSCy Schubert     ret = setup_addresses(ctx, handle, prog, tcp_listen_backlog);
918*7f2fe78bSCy Schubert     if (ret) {
919*7f2fe78bSCy Schubert         com_err(prog, ret, _("Error setting up network"));
920*7f2fe78bSCy Schubert         exit(1);
921*7f2fe78bSCy Schubert     }
922*7f2fe78bSCy Schubert     krb5_klog_syslog (LOG_INFO, _("set up %d sockets"), (int) events.n);
923*7f2fe78bSCy Schubert     if (events.n == 0) {
924*7f2fe78bSCy Schubert         /* If no sockets were set up, we can't continue. */
925*7f2fe78bSCy Schubert         com_err(prog, 0, _("no sockets set up?"));
926*7f2fe78bSCy Schubert         exit (1);
927*7f2fe78bSCy Schubert     }
928*7f2fe78bSCy Schubert 
929*7f2fe78bSCy Schubert     return 0;
930*7f2fe78bSCy Schubert }
931*7f2fe78bSCy Schubert 
932*7f2fe78bSCy Schubert void
init_addr(krb5_fulladdr * faddr,struct sockaddr * sa)933*7f2fe78bSCy Schubert init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
934*7f2fe78bSCy Schubert {
935*7f2fe78bSCy Schubert     switch (sa->sa_family) {
936*7f2fe78bSCy Schubert     case AF_INET:
937*7f2fe78bSCy Schubert         faddr->address->addrtype = ADDRTYPE_INET;
938*7f2fe78bSCy Schubert         faddr->address->length = 4;
939*7f2fe78bSCy Schubert         faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr;
940*7f2fe78bSCy Schubert         faddr->port = ntohs(sa2sin(sa)->sin_port);
941*7f2fe78bSCy Schubert         break;
942*7f2fe78bSCy Schubert     case AF_INET6:
943*7f2fe78bSCy Schubert         if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) {
944*7f2fe78bSCy Schubert             faddr->address->addrtype = ADDRTYPE_INET;
945*7f2fe78bSCy Schubert             faddr->address->length = 4;
946*7f2fe78bSCy Schubert             faddr->address->contents = 12 + (krb5_octet *) &sa2sin6(sa)->sin6_addr;
947*7f2fe78bSCy Schubert         } else {
948*7f2fe78bSCy Schubert             faddr->address->addrtype = ADDRTYPE_INET6;
949*7f2fe78bSCy Schubert             faddr->address->length = 16;
950*7f2fe78bSCy Schubert             faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr;
951*7f2fe78bSCy Schubert         }
952*7f2fe78bSCy Schubert         faddr->port = ntohs(sa2sin6(sa)->sin6_port);
953*7f2fe78bSCy Schubert         break;
954*7f2fe78bSCy Schubert     default:
955*7f2fe78bSCy Schubert         faddr->address->addrtype = -1;
956*7f2fe78bSCy Schubert         faddr->address->length = 0;
957*7f2fe78bSCy Schubert         faddr->address->contents = 0;
958*7f2fe78bSCy Schubert         faddr->port = 0;
959*7f2fe78bSCy Schubert         break;
960*7f2fe78bSCy Schubert     }
961*7f2fe78bSCy Schubert }
962*7f2fe78bSCy Schubert 
963*7f2fe78bSCy Schubert struct udp_dispatch_state {
964*7f2fe78bSCy Schubert     void *handle;
965*7f2fe78bSCy Schubert     const char *prog;
966*7f2fe78bSCy Schubert     int port_fd;
967*7f2fe78bSCy Schubert     krb5_address remote_addr_buf;
968*7f2fe78bSCy Schubert     krb5_fulladdr remote_addr;
969*7f2fe78bSCy Schubert     krb5_address local_addr_buf;
970*7f2fe78bSCy Schubert     krb5_fulladdr local_addr;
971*7f2fe78bSCy Schubert     socklen_t saddr_len;
972*7f2fe78bSCy Schubert     socklen_t daddr_len;
973*7f2fe78bSCy Schubert     struct sockaddr_storage saddr;
974*7f2fe78bSCy Schubert     struct sockaddr_storage daddr;
975*7f2fe78bSCy Schubert     aux_addressing_info auxaddr;
976*7f2fe78bSCy Schubert     krb5_data request;
977*7f2fe78bSCy Schubert     char pktbuf[MAX_DGRAM_SIZE];
978*7f2fe78bSCy Schubert };
979*7f2fe78bSCy Schubert 
980*7f2fe78bSCy Schubert static void
process_packet_response(void * arg,krb5_error_code code,krb5_data * response)981*7f2fe78bSCy Schubert process_packet_response(void *arg, krb5_error_code code, krb5_data *response)
982*7f2fe78bSCy Schubert {
983*7f2fe78bSCy Schubert     struct udp_dispatch_state *state = arg;
984*7f2fe78bSCy Schubert     int cc;
985*7f2fe78bSCy Schubert 
986*7f2fe78bSCy Schubert     if (code)
987*7f2fe78bSCy Schubert         com_err(state->prog ? state->prog : NULL, code,
988*7f2fe78bSCy Schubert                 _("while dispatching (udp)"));
989*7f2fe78bSCy Schubert     if (code || response == NULL)
990*7f2fe78bSCy Schubert         goto out;
991*7f2fe78bSCy Schubert 
992*7f2fe78bSCy Schubert     cc = send_to_from(state->port_fd, response->data,
993*7f2fe78bSCy Schubert                       (socklen_t) response->length, 0,
994*7f2fe78bSCy Schubert                       (struct sockaddr *)&state->saddr, state->saddr_len,
995*7f2fe78bSCy Schubert                       (struct sockaddr *)&state->daddr, state->daddr_len,
996*7f2fe78bSCy Schubert                       &state->auxaddr);
997*7f2fe78bSCy Schubert     if (cc == -1) {
998*7f2fe78bSCy Schubert         /* Note that the local address (daddr*) has no port number
999*7f2fe78bSCy Schubert          * info associated with it. */
1000*7f2fe78bSCy Schubert         char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV];
1001*7f2fe78bSCy Schubert         char daddrbuf[NI_MAXHOST];
1002*7f2fe78bSCy Schubert         int e = errno;
1003*7f2fe78bSCy Schubert 
1004*7f2fe78bSCy Schubert         if (getnameinfo((struct sockaddr *)&state->daddr, state->daddr_len,
1005*7f2fe78bSCy Schubert                         daddrbuf, sizeof(daddrbuf), 0, 0,
1006*7f2fe78bSCy Schubert                         NI_NUMERICHOST) != 0) {
1007*7f2fe78bSCy Schubert             strlcpy(daddrbuf, "?", sizeof(daddrbuf));
1008*7f2fe78bSCy Schubert         }
1009*7f2fe78bSCy Schubert 
1010*7f2fe78bSCy Schubert         if (getnameinfo((struct sockaddr *)&state->saddr, state->saddr_len,
1011*7f2fe78bSCy Schubert                         saddrbuf, sizeof(saddrbuf), sportbuf, sizeof(sportbuf),
1012*7f2fe78bSCy Schubert                         NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
1013*7f2fe78bSCy Schubert             strlcpy(saddrbuf, "?", sizeof(saddrbuf));
1014*7f2fe78bSCy Schubert             strlcpy(sportbuf, "?", sizeof(sportbuf));
1015*7f2fe78bSCy Schubert         }
1016*7f2fe78bSCy Schubert 
1017*7f2fe78bSCy Schubert         com_err(state->prog, e, _("while sending reply to %s/%s from %s"),
1018*7f2fe78bSCy Schubert                 saddrbuf, sportbuf, daddrbuf);
1019*7f2fe78bSCy Schubert         goto out;
1020*7f2fe78bSCy Schubert     }
1021*7f2fe78bSCy Schubert     if ((size_t)cc != response->length) {
1022*7f2fe78bSCy Schubert         com_err(state->prog, 0, _("short reply write %d vs %d\n"),
1023*7f2fe78bSCy Schubert                 response->length, cc);
1024*7f2fe78bSCy Schubert     }
1025*7f2fe78bSCy Schubert 
1026*7f2fe78bSCy Schubert out:
1027*7f2fe78bSCy Schubert     krb5_free_data(get_context(state->handle), response);
1028*7f2fe78bSCy Schubert     free(state);
1029*7f2fe78bSCy Schubert }
1030*7f2fe78bSCy Schubert 
1031*7f2fe78bSCy Schubert static void
process_packet(verto_ctx * ctx,verto_ev * ev)1032*7f2fe78bSCy Schubert process_packet(verto_ctx *ctx, verto_ev *ev)
1033*7f2fe78bSCy Schubert {
1034*7f2fe78bSCy Schubert     int cc;
1035*7f2fe78bSCy Schubert     struct connection *conn;
1036*7f2fe78bSCy Schubert     struct udp_dispatch_state *state;
1037*7f2fe78bSCy Schubert 
1038*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
1039*7f2fe78bSCy Schubert 
1040*7f2fe78bSCy Schubert     state = malloc(sizeof(*state));
1041*7f2fe78bSCy Schubert     if (!state) {
1042*7f2fe78bSCy Schubert         com_err(conn->prog, ENOMEM, _("while dispatching (udp)"));
1043*7f2fe78bSCy Schubert         return;
1044*7f2fe78bSCy Schubert     }
1045*7f2fe78bSCy Schubert 
1046*7f2fe78bSCy Schubert     state->handle = conn->handle;
1047*7f2fe78bSCy Schubert     state->prog = conn->prog;
1048*7f2fe78bSCy Schubert     state->port_fd = verto_get_fd(ev);
1049*7f2fe78bSCy Schubert     assert(state->port_fd >= 0);
1050*7f2fe78bSCy Schubert 
1051*7f2fe78bSCy Schubert     state->saddr_len = sizeof(state->saddr);
1052*7f2fe78bSCy Schubert     state->daddr_len = sizeof(state->daddr);
1053*7f2fe78bSCy Schubert     memset(&state->auxaddr, 0, sizeof(state->auxaddr));
1054*7f2fe78bSCy Schubert     cc = recv_from_to(state->port_fd, state->pktbuf, sizeof(state->pktbuf), 0,
1055*7f2fe78bSCy Schubert                       (struct sockaddr *)&state->saddr, &state->saddr_len,
1056*7f2fe78bSCy Schubert                       (struct sockaddr *)&state->daddr, &state->daddr_len,
1057*7f2fe78bSCy Schubert                       &state->auxaddr);
1058*7f2fe78bSCy Schubert     if (cc == -1) {
1059*7f2fe78bSCy Schubert         if (errno != EINTR && errno != EAGAIN
1060*7f2fe78bSCy Schubert             /*
1061*7f2fe78bSCy Schubert              * This is how Linux indicates that a previous transmission was
1062*7f2fe78bSCy Schubert              * refused, e.g., if the client timed out before getting the
1063*7f2fe78bSCy Schubert              * response packet.
1064*7f2fe78bSCy Schubert              */
1065*7f2fe78bSCy Schubert             && errno != ECONNREFUSED
1066*7f2fe78bSCy Schubert         )
1067*7f2fe78bSCy Schubert             com_err(conn->prog, errno, _("while receiving from network"));
1068*7f2fe78bSCy Schubert         free(state);
1069*7f2fe78bSCy Schubert         return;
1070*7f2fe78bSCy Schubert     }
1071*7f2fe78bSCy Schubert     if (!cc) { /* zero-length packet? */
1072*7f2fe78bSCy Schubert         free(state);
1073*7f2fe78bSCy Schubert         return;
1074*7f2fe78bSCy Schubert     }
1075*7f2fe78bSCy Schubert 
1076*7f2fe78bSCy Schubert     if (state->daddr_len == 0 && conn->type == CONN_UDP) {
1077*7f2fe78bSCy Schubert         /*
1078*7f2fe78bSCy Schubert          * An address couldn't be obtained, so the PKTINFO option probably
1079*7f2fe78bSCy Schubert          * isn't available.  If the socket is bound to a specific address, then
1080*7f2fe78bSCy Schubert          * try to get the address here.
1081*7f2fe78bSCy Schubert          */
1082*7f2fe78bSCy Schubert         state->daddr_len = sizeof(state->daddr);
1083*7f2fe78bSCy Schubert         if (getsockname(state->port_fd, (struct sockaddr *)&state->daddr,
1084*7f2fe78bSCy Schubert                         &state->daddr_len) != 0)
1085*7f2fe78bSCy Schubert             state->daddr_len = 0;
1086*7f2fe78bSCy Schubert         /* On failure, keep going anyways. */
1087*7f2fe78bSCy Schubert     }
1088*7f2fe78bSCy Schubert 
1089*7f2fe78bSCy Schubert     state->request.length = cc;
1090*7f2fe78bSCy Schubert     state->request.data = state->pktbuf;
1091*7f2fe78bSCy Schubert 
1092*7f2fe78bSCy Schubert     state->remote_addr.address = &state->remote_addr_buf;
1093*7f2fe78bSCy Schubert     init_addr(&state->remote_addr, ss2sa(&state->saddr));
1094*7f2fe78bSCy Schubert 
1095*7f2fe78bSCy Schubert     state->local_addr.address = &state->local_addr_buf;
1096*7f2fe78bSCy Schubert     init_addr(&state->local_addr, ss2sa(&state->daddr));
1097*7f2fe78bSCy Schubert 
1098*7f2fe78bSCy Schubert     /* This address is in net order. */
1099*7f2fe78bSCy Schubert     dispatch(state->handle, &state->local_addr, &state->remote_addr,
1100*7f2fe78bSCy Schubert              &state->request, 0, ctx, process_packet_response, state);
1101*7f2fe78bSCy Schubert }
1102*7f2fe78bSCy Schubert 
1103*7f2fe78bSCy Schubert static int
kill_lru_tcp_or_rpc_connection(void * handle,verto_ev * newev)1104*7f2fe78bSCy Schubert kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
1105*7f2fe78bSCy Schubert {
1106*7f2fe78bSCy Schubert     struct connection *c = NULL, *oldest_c = NULL;
1107*7f2fe78bSCy Schubert     verto_ev *ev, *oldest_ev = NULL;
1108*7f2fe78bSCy Schubert     int i, fd = -1;
1109*7f2fe78bSCy Schubert 
1110*7f2fe78bSCy Schubert     krb5_klog_syslog(LOG_INFO, _("too many connections"));
1111*7f2fe78bSCy Schubert 
1112*7f2fe78bSCy Schubert     FOREACH_ELT (events, i, ev) {
1113*7f2fe78bSCy Schubert         if (ev == newev)
1114*7f2fe78bSCy Schubert             continue;
1115*7f2fe78bSCy Schubert 
1116*7f2fe78bSCy Schubert         c = verto_get_private(ev);
1117*7f2fe78bSCy Schubert         if (!c)
1118*7f2fe78bSCy Schubert             continue;
1119*7f2fe78bSCy Schubert         if (c->type != CONN_TCP && c->type != CONN_RPC)
1120*7f2fe78bSCy Schubert             continue;
1121*7f2fe78bSCy Schubert         if (oldest_c == NULL
1122*7f2fe78bSCy Schubert             || oldest_c->start_time > c->start_time) {
1123*7f2fe78bSCy Schubert             oldest_ev = ev;
1124*7f2fe78bSCy Schubert             oldest_c = c;
1125*7f2fe78bSCy Schubert         }
1126*7f2fe78bSCy Schubert     }
1127*7f2fe78bSCy Schubert     if (oldest_c != NULL) {
1128*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
1129*7f2fe78bSCy Schubert                          c->type == CONN_RPC ? "rpc" : "tcp",
1130*7f2fe78bSCy Schubert                          verto_get_fd(oldest_ev), oldest_c->addrbuf);
1131*7f2fe78bSCy Schubert         if (oldest_c->type == CONN_RPC)
1132*7f2fe78bSCy Schubert             oldest_c->rpc_force_close = 1;
1133*7f2fe78bSCy Schubert         verto_del(oldest_ev);
1134*7f2fe78bSCy Schubert     }
1135*7f2fe78bSCy Schubert     return fd;
1136*7f2fe78bSCy Schubert }
1137*7f2fe78bSCy Schubert 
1138*7f2fe78bSCy Schubert static void
accept_tcp_connection(verto_ctx * ctx,verto_ev * ev)1139*7f2fe78bSCy Schubert accept_tcp_connection(verto_ctx *ctx, verto_ev *ev)
1140*7f2fe78bSCy Schubert {
1141*7f2fe78bSCy Schubert     int s;
1142*7f2fe78bSCy Schubert     struct sockaddr_storage addr_s;
1143*7f2fe78bSCy Schubert     struct sockaddr *addr = (struct sockaddr *)&addr_s;
1144*7f2fe78bSCy Schubert     socklen_t addrlen = sizeof(addr_s);
1145*7f2fe78bSCy Schubert     struct connection *newconn, *conn;
1146*7f2fe78bSCy Schubert     char tmpbuf[10];
1147*7f2fe78bSCy Schubert     verto_ev_flag flags;
1148*7f2fe78bSCy Schubert     verto_ev *newev;
1149*7f2fe78bSCy Schubert 
1150*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
1151*7f2fe78bSCy Schubert     s = accept(verto_get_fd(ev), addr, &addrlen);
1152*7f2fe78bSCy Schubert     if (s < 0)
1153*7f2fe78bSCy Schubert         return;
1154*7f2fe78bSCy Schubert     set_cloexec_fd(s);
1155*7f2fe78bSCy Schubert #ifndef _WIN32
1156*7f2fe78bSCy Schubert     if (s >= FD_SETSIZE) {
1157*7f2fe78bSCy Schubert         close(s);
1158*7f2fe78bSCy Schubert         return;
1159*7f2fe78bSCy Schubert     }
1160*7f2fe78bSCy Schubert #endif
1161*7f2fe78bSCy Schubert     setnbio(s), setnolinger(s), setkeepalive(s);
1162*7f2fe78bSCy Schubert 
1163*7f2fe78bSCy Schubert     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1164*7f2fe78bSCy Schubert     if (add_fd(s, CONN_TCP, flags, conn->handle, conn->prog, ctx,
1165*7f2fe78bSCy Schubert                process_tcp_connection_read, &newev) != 0) {
1166*7f2fe78bSCy Schubert         close(s);
1167*7f2fe78bSCy Schubert         return;
1168*7f2fe78bSCy Schubert     }
1169*7f2fe78bSCy Schubert     newconn = verto_get_private(newev);
1170*7f2fe78bSCy Schubert 
1171*7f2fe78bSCy Schubert     if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
1172*7f2fe78bSCy Schubert                     newconn->addrbuf, sizeof(newconn->addrbuf),
1173*7f2fe78bSCy Schubert                     tmpbuf, sizeof(tmpbuf),
1174*7f2fe78bSCy Schubert                     NI_NUMERICHOST | NI_NUMERICSERV))
1175*7f2fe78bSCy Schubert         strlcpy(newconn->addrbuf, "???", sizeof(newconn->addrbuf));
1176*7f2fe78bSCy Schubert     else {
1177*7f2fe78bSCy Schubert         char *p, *end;
1178*7f2fe78bSCy Schubert         p = newconn->addrbuf;
1179*7f2fe78bSCy Schubert         end = p + sizeof(newconn->addrbuf);
1180*7f2fe78bSCy Schubert         p += strlen(p);
1181*7f2fe78bSCy Schubert         if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
1182*7f2fe78bSCy Schubert             *p++ = '.';
1183*7f2fe78bSCy Schubert             strlcpy(p, tmpbuf, end - p);
1184*7f2fe78bSCy Schubert         }
1185*7f2fe78bSCy Schubert     }
1186*7f2fe78bSCy Schubert 
1187*7f2fe78bSCy Schubert     newconn->addr_s = addr_s;
1188*7f2fe78bSCy Schubert     newconn->addrlen = addrlen;
1189*7f2fe78bSCy Schubert     newconn->bufsiz = 1024 * 1024;
1190*7f2fe78bSCy Schubert     newconn->buffer = malloc(newconn->bufsiz);
1191*7f2fe78bSCy Schubert     newconn->start_time = time(0);
1192*7f2fe78bSCy Schubert 
1193*7f2fe78bSCy Schubert     if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
1194*7f2fe78bSCy Schubert         kill_lru_tcp_or_rpc_connection(conn->handle, newev);
1195*7f2fe78bSCy Schubert 
1196*7f2fe78bSCy Schubert     if (newconn->buffer == 0) {
1197*7f2fe78bSCy Schubert         com_err(conn->prog, errno,
1198*7f2fe78bSCy Schubert                 _("allocating buffer for new TCP session from %s"),
1199*7f2fe78bSCy Schubert                 newconn->addrbuf);
1200*7f2fe78bSCy Schubert         verto_del(newev);
1201*7f2fe78bSCy Schubert         return;
1202*7f2fe78bSCy Schubert     }
1203*7f2fe78bSCy Schubert     newconn->offset = 0;
1204*7f2fe78bSCy Schubert     newconn->remote_addr.address = &newconn->remote_addr_buf;
1205*7f2fe78bSCy Schubert     init_addr(&newconn->remote_addr, ss2sa(&newconn->addr_s));
1206*7f2fe78bSCy Schubert     SG_SET(&newconn->sgbuf[0], newconn->lenbuf, 4);
1207*7f2fe78bSCy Schubert     SG_SET(&newconn->sgbuf[1], 0, 0);
1208*7f2fe78bSCy Schubert }
1209*7f2fe78bSCy Schubert 
1210*7f2fe78bSCy Schubert struct tcp_dispatch_state {
1211*7f2fe78bSCy Schubert     struct sockaddr_storage local_saddr;
1212*7f2fe78bSCy Schubert     krb5_address local_addr_buf;
1213*7f2fe78bSCy Schubert     krb5_fulladdr local_addr;
1214*7f2fe78bSCy Schubert     struct connection *conn;
1215*7f2fe78bSCy Schubert     krb5_data request;
1216*7f2fe78bSCy Schubert     verto_ctx *ctx;
1217*7f2fe78bSCy Schubert     int sock;
1218*7f2fe78bSCy Schubert };
1219*7f2fe78bSCy Schubert 
1220*7f2fe78bSCy Schubert static void
process_tcp_response(void * arg,krb5_error_code code,krb5_data * response)1221*7f2fe78bSCy Schubert process_tcp_response(void *arg, krb5_error_code code, krb5_data *response)
1222*7f2fe78bSCy Schubert {
1223*7f2fe78bSCy Schubert     struct tcp_dispatch_state *state = arg;
1224*7f2fe78bSCy Schubert     verto_ev *ev;
1225*7f2fe78bSCy Schubert 
1226*7f2fe78bSCy Schubert     assert(state);
1227*7f2fe78bSCy Schubert     state->conn->response = response;
1228*7f2fe78bSCy Schubert 
1229*7f2fe78bSCy Schubert     if (code)
1230*7f2fe78bSCy Schubert         com_err(state->conn->prog, code, _("while dispatching (tcp)"));
1231*7f2fe78bSCy Schubert     if (code || !response)
1232*7f2fe78bSCy Schubert         goto kill_tcp_connection;
1233*7f2fe78bSCy Schubert 
1234*7f2fe78bSCy Schubert     /* Queue outgoing response. */
1235*7f2fe78bSCy Schubert     store_32_be(response->length, state->conn->lenbuf);
1236*7f2fe78bSCy Schubert     SG_SET(&state->conn->sgbuf[1], response->data, response->length);
1237*7f2fe78bSCy Schubert     state->conn->sgp = state->conn->sgbuf;
1238*7f2fe78bSCy Schubert     state->conn->sgnum = 2;
1239*7f2fe78bSCy Schubert 
1240*7f2fe78bSCy Schubert     ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,
1241*7f2fe78bSCy Schubert                     process_tcp_connection_write, state->sock, state->conn);
1242*7f2fe78bSCy Schubert     if (ev) {
1243*7f2fe78bSCy Schubert         free(state);
1244*7f2fe78bSCy Schubert         return;
1245*7f2fe78bSCy Schubert     }
1246*7f2fe78bSCy Schubert 
1247*7f2fe78bSCy Schubert kill_tcp_connection:
1248*7f2fe78bSCy Schubert     tcp_or_rpc_data_counter--;
1249*7f2fe78bSCy Schubert     free_connection(state->conn);
1250*7f2fe78bSCy Schubert     close(state->sock);
1251*7f2fe78bSCy Schubert     free(state);
1252*7f2fe78bSCy Schubert }
1253*7f2fe78bSCy Schubert 
1254*7f2fe78bSCy Schubert /* Creates the tcp_dispatch_state and deletes the verto event. */
1255*7f2fe78bSCy Schubert static struct tcp_dispatch_state *
prepare_for_dispatch(verto_ctx * ctx,verto_ev * ev)1256*7f2fe78bSCy Schubert prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)
1257*7f2fe78bSCy Schubert {
1258*7f2fe78bSCy Schubert     struct tcp_dispatch_state *state;
1259*7f2fe78bSCy Schubert 
1260*7f2fe78bSCy Schubert     state = malloc(sizeof(*state));
1261*7f2fe78bSCy Schubert     if (!state) {
1262*7f2fe78bSCy Schubert         krb5_klog_syslog(LOG_ERR, _("error allocating tcp dispatch private!"));
1263*7f2fe78bSCy Schubert         return NULL;
1264*7f2fe78bSCy Schubert     }
1265*7f2fe78bSCy Schubert     state->conn = verto_get_private(ev);
1266*7f2fe78bSCy Schubert     state->sock = verto_get_fd(ev);
1267*7f2fe78bSCy Schubert     state->ctx = ctx;
1268*7f2fe78bSCy Schubert     verto_set_private(ev, NULL, NULL); /* Don't close the fd or free conn! */
1269*7f2fe78bSCy Schubert     remove_event_from_set(ev); /* Remove it from the set. */
1270*7f2fe78bSCy Schubert     verto_del(ev);
1271*7f2fe78bSCy Schubert     return state;
1272*7f2fe78bSCy Schubert }
1273*7f2fe78bSCy Schubert 
1274*7f2fe78bSCy Schubert static void
process_tcp_connection_read(verto_ctx * ctx,verto_ev * ev)1275*7f2fe78bSCy Schubert process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
1276*7f2fe78bSCy Schubert {
1277*7f2fe78bSCy Schubert     struct tcp_dispatch_state *state = NULL;
1278*7f2fe78bSCy Schubert     struct connection *conn = NULL;
1279*7f2fe78bSCy Schubert     ssize_t nread;
1280*7f2fe78bSCy Schubert     size_t len;
1281*7f2fe78bSCy Schubert 
1282*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
1283*7f2fe78bSCy Schubert 
1284*7f2fe78bSCy Schubert     /*
1285*7f2fe78bSCy Schubert      * Read message length and data into one big buffer, already allocated
1286*7f2fe78bSCy Schubert      * at connect time.  If we have a complete message, we stop reading, so
1287*7f2fe78bSCy Schubert      * we should only be here if there is no data in the buffer, or only an
1288*7f2fe78bSCy Schubert      * incomplete message.
1289*7f2fe78bSCy Schubert      */
1290*7f2fe78bSCy Schubert     if (conn->offset < 4) {
1291*7f2fe78bSCy Schubert         krb5_data *response = NULL;
1292*7f2fe78bSCy Schubert 
1293*7f2fe78bSCy Schubert         /* msglen has not been computed.  XXX Doing at least two reads
1294*7f2fe78bSCy Schubert          * here, letting the kernel worry about buffering. */
1295*7f2fe78bSCy Schubert         len = 4 - conn->offset;
1296*7f2fe78bSCy Schubert         nread = SOCKET_READ(verto_get_fd(ev),
1297*7f2fe78bSCy Schubert                             conn->buffer + conn->offset, len);
1298*7f2fe78bSCy Schubert         if (nread < 0) /* error */
1299*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1300*7f2fe78bSCy Schubert         if (nread == 0) /* eof */
1301*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1302*7f2fe78bSCy Schubert         conn->offset += nread;
1303*7f2fe78bSCy Schubert         if (conn->offset == 4) {
1304*7f2fe78bSCy Schubert             unsigned char *p = (unsigned char *)conn->buffer;
1305*7f2fe78bSCy Schubert             conn->msglen = load_32_be(p);
1306*7f2fe78bSCy Schubert             if (conn->msglen > conn->bufsiz - 4) {
1307*7f2fe78bSCy Schubert                 krb5_error_code err;
1308*7f2fe78bSCy Schubert                 /* Message too big. */
1309*7f2fe78bSCy Schubert                 krb5_klog_syslog(LOG_ERR, _("TCP client %s wants %lu bytes, "
1310*7f2fe78bSCy Schubert                                             "cap is %lu"), conn->addrbuf,
1311*7f2fe78bSCy Schubert                                  (unsigned long) conn->msglen,
1312*7f2fe78bSCy Schubert                                  (unsigned long) conn->bufsiz - 4);
1313*7f2fe78bSCy Schubert                 /* XXX Should return an error.  */
1314*7f2fe78bSCy Schubert                 err = make_toolong_error (conn->handle,
1315*7f2fe78bSCy Schubert                                           &response);
1316*7f2fe78bSCy Schubert                 if (err) {
1317*7f2fe78bSCy Schubert                     krb5_klog_syslog(LOG_ERR, _("error constructing "
1318*7f2fe78bSCy Schubert                                                 "KRB_ERR_FIELD_TOOLONG error! %s"),
1319*7f2fe78bSCy Schubert                                      error_message(err));
1320*7f2fe78bSCy Schubert                     goto kill_tcp_connection;
1321*7f2fe78bSCy Schubert                 }
1322*7f2fe78bSCy Schubert 
1323*7f2fe78bSCy Schubert                 state = prepare_for_dispatch(ctx, ev);
1324*7f2fe78bSCy Schubert                 if (!state) {
1325*7f2fe78bSCy Schubert                     krb5_free_data(get_context(conn->handle), response);
1326*7f2fe78bSCy Schubert                     goto kill_tcp_connection;
1327*7f2fe78bSCy Schubert                 }
1328*7f2fe78bSCy Schubert                 process_tcp_response(state, 0, response);
1329*7f2fe78bSCy Schubert             }
1330*7f2fe78bSCy Schubert         }
1331*7f2fe78bSCy Schubert     } else {
1332*7f2fe78bSCy Schubert         /* msglen known. */
1333*7f2fe78bSCy Schubert         socklen_t local_saddrlen = sizeof(struct sockaddr_storage);
1334*7f2fe78bSCy Schubert 
1335*7f2fe78bSCy Schubert         len = conn->msglen - (conn->offset - 4);
1336*7f2fe78bSCy Schubert         nread = SOCKET_READ(verto_get_fd(ev),
1337*7f2fe78bSCy Schubert                             conn->buffer + conn->offset, len);
1338*7f2fe78bSCy Schubert         if (nread < 0) /* error */
1339*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1340*7f2fe78bSCy Schubert         if (nread == 0) /* eof */
1341*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1342*7f2fe78bSCy Schubert         conn->offset += nread;
1343*7f2fe78bSCy Schubert         if (conn->offset < conn->msglen + 4)
1344*7f2fe78bSCy Schubert             return;
1345*7f2fe78bSCy Schubert 
1346*7f2fe78bSCy Schubert         /* Have a complete message, and exactly one message. */
1347*7f2fe78bSCy Schubert         state = prepare_for_dispatch(ctx, ev);
1348*7f2fe78bSCy Schubert         if (!state)
1349*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1350*7f2fe78bSCy Schubert 
1351*7f2fe78bSCy Schubert         state->request.length = conn->msglen;
1352*7f2fe78bSCy Schubert         state->request.data = conn->buffer + 4;
1353*7f2fe78bSCy Schubert 
1354*7f2fe78bSCy Schubert         if (getsockname(verto_get_fd(ev), ss2sa(&state->local_saddr),
1355*7f2fe78bSCy Schubert                         &local_saddrlen) < 0) {
1356*7f2fe78bSCy Schubert             krb5_klog_syslog(LOG_ERR, _("getsockname failed: %s"),
1357*7f2fe78bSCy Schubert                              error_message(errno));
1358*7f2fe78bSCy Schubert             goto kill_tcp_connection;
1359*7f2fe78bSCy Schubert         }
1360*7f2fe78bSCy Schubert         state->local_addr.address = &state->local_addr_buf;
1361*7f2fe78bSCy Schubert         init_addr(&state->local_addr, ss2sa(&state->local_saddr));
1362*7f2fe78bSCy Schubert         dispatch(state->conn->handle, &state->local_addr, &conn->remote_addr,
1363*7f2fe78bSCy Schubert                  &state->request, 1, ctx, process_tcp_response, state);
1364*7f2fe78bSCy Schubert     }
1365*7f2fe78bSCy Schubert 
1366*7f2fe78bSCy Schubert     return;
1367*7f2fe78bSCy Schubert 
1368*7f2fe78bSCy Schubert kill_tcp_connection:
1369*7f2fe78bSCy Schubert     verto_del(ev);
1370*7f2fe78bSCy Schubert }
1371*7f2fe78bSCy Schubert 
1372*7f2fe78bSCy Schubert static void
process_tcp_connection_write(verto_ctx * ctx,verto_ev * ev)1373*7f2fe78bSCy Schubert process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev)
1374*7f2fe78bSCy Schubert {
1375*7f2fe78bSCy Schubert     struct connection *conn;
1376*7f2fe78bSCy Schubert     SOCKET_WRITEV_TEMP tmp;
1377*7f2fe78bSCy Schubert     ssize_t nwrote;
1378*7f2fe78bSCy Schubert     int sock;
1379*7f2fe78bSCy Schubert 
1380*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
1381*7f2fe78bSCy Schubert     sock = verto_get_fd(ev);
1382*7f2fe78bSCy Schubert 
1383*7f2fe78bSCy Schubert     nwrote = SOCKET_WRITEV(sock, conn->sgp,
1384*7f2fe78bSCy Schubert                            conn->sgnum, tmp);
1385*7f2fe78bSCy Schubert     if (nwrote > 0) { /* non-error and non-eof */
1386*7f2fe78bSCy Schubert         while (nwrote) {
1387*7f2fe78bSCy Schubert             sg_buf *sgp = conn->sgp;
1388*7f2fe78bSCy Schubert             if ((size_t)nwrote < SG_LEN(sgp)) {
1389*7f2fe78bSCy Schubert                 SG_ADVANCE(sgp, (size_t)nwrote);
1390*7f2fe78bSCy Schubert                 nwrote = 0;
1391*7f2fe78bSCy Schubert             } else {
1392*7f2fe78bSCy Schubert                 nwrote -= SG_LEN(sgp);
1393*7f2fe78bSCy Schubert                 conn->sgp++;
1394*7f2fe78bSCy Schubert                 conn->sgnum--;
1395*7f2fe78bSCy Schubert                 if (conn->sgnum == 0 && nwrote != 0)
1396*7f2fe78bSCy Schubert                     abort();
1397*7f2fe78bSCy Schubert             }
1398*7f2fe78bSCy Schubert         }
1399*7f2fe78bSCy Schubert 
1400*7f2fe78bSCy Schubert         /* If we still have more data to send, just return so that
1401*7f2fe78bSCy Schubert          * the main loop can call this function again when the socket
1402*7f2fe78bSCy Schubert          * is ready for more writing. */
1403*7f2fe78bSCy Schubert         if (conn->sgnum > 0)
1404*7f2fe78bSCy Schubert             return;
1405*7f2fe78bSCy Schubert     }
1406*7f2fe78bSCy Schubert 
1407*7f2fe78bSCy Schubert     /* Finished sending.  We should go back to reading, though if we
1408*7f2fe78bSCy Schubert      * sent a FIELD_TOOLONG error in reply to a length with the high
1409*7f2fe78bSCy Schubert      * bit set, RFC 4120 says we have to close the TCP stream. */
1410*7f2fe78bSCy Schubert     verto_del(ev);
1411*7f2fe78bSCy Schubert }
1412*7f2fe78bSCy Schubert 
1413*7f2fe78bSCy Schubert void
loop_free(verto_ctx * ctx)1414*7f2fe78bSCy Schubert loop_free(verto_ctx *ctx)
1415*7f2fe78bSCy Schubert {
1416*7f2fe78bSCy Schubert     int i;
1417*7f2fe78bSCy Schubert     struct bind_address val;
1418*7f2fe78bSCy Schubert 
1419*7f2fe78bSCy Schubert     verto_free(ctx);
1420*7f2fe78bSCy Schubert 
1421*7f2fe78bSCy Schubert     /* Free each addresses added to the loop. */
1422*7f2fe78bSCy Schubert     FOREACH_ELT(bind_addresses, i, val)
1423*7f2fe78bSCy Schubert         free(val.address);
1424*7f2fe78bSCy Schubert     FREE_SET_DATA(bind_addresses);
1425*7f2fe78bSCy Schubert     FREE_SET_DATA(events);
1426*7f2fe78bSCy Schubert }
1427*7f2fe78bSCy Schubert 
1428*7f2fe78bSCy Schubert static int
have_event_for_fd(int fd)1429*7f2fe78bSCy Schubert have_event_for_fd(int fd)
1430*7f2fe78bSCy Schubert {
1431*7f2fe78bSCy Schubert     verto_ev *ev;
1432*7f2fe78bSCy Schubert     int i;
1433*7f2fe78bSCy Schubert 
1434*7f2fe78bSCy Schubert     FOREACH_ELT(events, i, ev) {
1435*7f2fe78bSCy Schubert         if (verto_get_fd(ev) == fd)
1436*7f2fe78bSCy Schubert             return 1;
1437*7f2fe78bSCy Schubert     }
1438*7f2fe78bSCy Schubert 
1439*7f2fe78bSCy Schubert     return 0;
1440*7f2fe78bSCy Schubert }
1441*7f2fe78bSCy Schubert 
1442*7f2fe78bSCy Schubert static void
accept_rpc_connection(verto_ctx * ctx,verto_ev * ev)1443*7f2fe78bSCy Schubert accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
1444*7f2fe78bSCy Schubert {
1445*7f2fe78bSCy Schubert     verto_ev_flag flags;
1446*7f2fe78bSCy Schubert     struct connection *conn;
1447*7f2fe78bSCy Schubert     fd_set fds;
1448*7f2fe78bSCy Schubert     int s;
1449*7f2fe78bSCy Schubert 
1450*7f2fe78bSCy Schubert     conn = verto_get_private(ev);
1451*7f2fe78bSCy Schubert 
1452*7f2fe78bSCy Schubert     /* Service the woken RPC listener descriptor. */
1453*7f2fe78bSCy Schubert     FD_ZERO(&fds);
1454*7f2fe78bSCy Schubert     FD_SET(verto_get_fd(ev), &fds);
1455*7f2fe78bSCy Schubert     svc_getreqset(&fds);
1456*7f2fe78bSCy Schubert 
1457*7f2fe78bSCy Schubert     /* Scan svc_fdset for any new connections. */
1458*7f2fe78bSCy Schubert     for (s = 0; s < FD_SETSIZE; s++) {
1459*7f2fe78bSCy Schubert         struct sockaddr_storage addr_s;
1460*7f2fe78bSCy Schubert         struct sockaddr *addr = (struct sockaddr *) &addr_s;
1461*7f2fe78bSCy Schubert         socklen_t addrlen = sizeof(addr_s);
1462*7f2fe78bSCy Schubert         struct connection *newconn;
1463*7f2fe78bSCy Schubert         char tmpbuf[10];
1464*7f2fe78bSCy Schubert         verto_ev *newev;
1465*7f2fe78bSCy Schubert 
1466*7f2fe78bSCy Schubert         /* If we already have this fd, continue. */
1467*7f2fe78bSCy Schubert         if (!FD_ISSET(s, &svc_fdset) || have_event_for_fd(s))
1468*7f2fe78bSCy Schubert             continue;
1469*7f2fe78bSCy Schubert 
1470*7f2fe78bSCy Schubert         flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
1471*7f2fe78bSCy Schubert         if (add_fd(s, CONN_RPC, flags, conn->handle, conn->prog, ctx,
1472*7f2fe78bSCy Schubert                    process_rpc_connection, &newev) != 0)
1473*7f2fe78bSCy Schubert             continue;
1474*7f2fe78bSCy Schubert         newconn = verto_get_private(newev);
1475*7f2fe78bSCy Schubert 
1476*7f2fe78bSCy Schubert         set_cloexec_fd(s);
1477*7f2fe78bSCy Schubert 
1478*7f2fe78bSCy Schubert         if (getpeername(s, addr, &addrlen) ||
1479*7f2fe78bSCy Schubert             getnameinfo(addr, addrlen,
1480*7f2fe78bSCy Schubert                         newconn->addrbuf,
1481*7f2fe78bSCy Schubert                         sizeof(newconn->addrbuf),
1482*7f2fe78bSCy Schubert                         tmpbuf, sizeof(tmpbuf),
1483*7f2fe78bSCy Schubert                         NI_NUMERICHOST | NI_NUMERICSERV)) {
1484*7f2fe78bSCy Schubert             strlcpy(newconn->addrbuf, "???",
1485*7f2fe78bSCy Schubert                     sizeof(newconn->addrbuf));
1486*7f2fe78bSCy Schubert         } else {
1487*7f2fe78bSCy Schubert             char *p, *end;
1488*7f2fe78bSCy Schubert             p = newconn->addrbuf;
1489*7f2fe78bSCy Schubert             end = p + sizeof(newconn->addrbuf);
1490*7f2fe78bSCy Schubert             p += strlen(p);
1491*7f2fe78bSCy Schubert             if ((size_t)(end - p) > 2 + strlen(tmpbuf)) {
1492*7f2fe78bSCy Schubert                 *p++ = '.';
1493*7f2fe78bSCy Schubert                 strlcpy(p, tmpbuf, end - p);
1494*7f2fe78bSCy Schubert             }
1495*7f2fe78bSCy Schubert         }
1496*7f2fe78bSCy Schubert 
1497*7f2fe78bSCy Schubert         newconn->addr_s = addr_s;
1498*7f2fe78bSCy Schubert         newconn->addrlen = addrlen;
1499*7f2fe78bSCy Schubert         newconn->start_time = time(0);
1500*7f2fe78bSCy Schubert 
1501*7f2fe78bSCy Schubert         if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
1502*7f2fe78bSCy Schubert             kill_lru_tcp_or_rpc_connection(newconn->handle, newev);
1503*7f2fe78bSCy Schubert 
1504*7f2fe78bSCy Schubert         newconn->remote_addr.address = &newconn->remote_addr_buf;
1505*7f2fe78bSCy Schubert         init_addr(&newconn->remote_addr, ss2sa(&newconn->addr_s));
1506*7f2fe78bSCy Schubert     }
1507*7f2fe78bSCy Schubert }
1508*7f2fe78bSCy Schubert 
1509*7f2fe78bSCy Schubert static void
process_rpc_connection(verto_ctx * ctx,verto_ev * ev)1510*7f2fe78bSCy Schubert process_rpc_connection(verto_ctx *ctx, verto_ev *ev)
1511*7f2fe78bSCy Schubert {
1512*7f2fe78bSCy Schubert     fd_set fds;
1513*7f2fe78bSCy Schubert 
1514*7f2fe78bSCy Schubert     FD_ZERO(&fds);
1515*7f2fe78bSCy Schubert     FD_SET(verto_get_fd(ev), &fds);
1516*7f2fe78bSCy Schubert     svc_getreqset(&fds);
1517*7f2fe78bSCy Schubert 
1518*7f2fe78bSCy Schubert     if (!FD_ISSET(verto_get_fd(ev), &svc_fdset))
1519*7f2fe78bSCy Schubert         verto_del(ev);
1520*7f2fe78bSCy Schubert }
1521*7f2fe78bSCy Schubert 
1522*7f2fe78bSCy Schubert #endif /* INET */
1523