xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/network.c (revision 56a424cc)
17c478bd9Sstevel@tonic-gate /*
2*56a424ccSmp  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * kdc/network.c
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * Copyright 1990,2000 by the Massachusetts Institute of Technology.
107c478bd9Sstevel@tonic-gate  *
117c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
127c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
137c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
147c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
157c478bd9Sstevel@tonic-gate  *
167c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
177c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
187c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
197c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
207c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
217c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
227c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
237c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
247c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
257c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
267c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
277c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
287c478bd9Sstevel@tonic-gate  * or implied warranty.
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * Network code for Kerberos v5 KDC.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #define NEED_SOCKETS
367c478bd9Sstevel@tonic-gate #include "k5-int.h"
377c478bd9Sstevel@tonic-gate #include "com_err.h"
387c478bd9Sstevel@tonic-gate #include "kdc_util.h"
397c478bd9Sstevel@tonic-gate #include "extern.h"
407c478bd9Sstevel@tonic-gate #include "kdc5_err.h"
417c478bd9Sstevel@tonic-gate #include "adm_proto.h"
427c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
437c478bd9Sstevel@tonic-gate #include <syslog.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include <stddef.h>
467c478bd9Sstevel@tonic-gate #include <ctype.h>
477c478bd9Sstevel@tonic-gate #include <port-sockets.h>
487c478bd9Sstevel@tonic-gate /* #include <socket-utils.h> */
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H
517c478bd9Sstevel@tonic-gate #include <sys/types.h>
527c478bd9Sstevel@tonic-gate #include <netinet/in.h>
537c478bd9Sstevel@tonic-gate #include <sys/socket.h>
547c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_SOCKIO_H
557c478bd9Sstevel@tonic-gate /* for SIOCGIFCONF, etc. */
567c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
577c478bd9Sstevel@tonic-gate #endif
587c478bd9Sstevel@tonic-gate #include <sys/time.h>
597c478bd9Sstevel@tonic-gate #include <libintl.h>
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #if HAVE_SYS_SELECT_H
627c478bd9Sstevel@tonic-gate #include <sys/select.h>
637c478bd9Sstevel@tonic-gate #endif
647c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
657c478bd9Sstevel@tonic-gate #include <inet/ip.h>
667c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
697c478bd9Sstevel@tonic-gate #include <net/if.h>
707c478bd9Sstevel@tonic-gate #endif
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H
737c478bd9Sstevel@tonic-gate #include <sys/filio.h>		/* FIONBIO */
747c478bd9Sstevel@tonic-gate #endif
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate #include <fake-addrinfo.h>
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /* Misc utility routines.  */
797c478bd9Sstevel@tonic-gate static void
807c478bd9Sstevel@tonic-gate set_sa_port(struct sockaddr *addr, int port)
817c478bd9Sstevel@tonic-gate {
827c478bd9Sstevel@tonic-gate     switch (addr->sa_family) {
837c478bd9Sstevel@tonic-gate     case AF_INET:
847c478bd9Sstevel@tonic-gate 	sa2sin(addr)->sin_port = port;
857c478bd9Sstevel@tonic-gate 	break;
867c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
877c478bd9Sstevel@tonic-gate     case AF_INET6:
887c478bd9Sstevel@tonic-gate 	sa2sin6(addr)->sin6_port = port;
897c478bd9Sstevel@tonic-gate 	break;
907c478bd9Sstevel@tonic-gate #endif
917c478bd9Sstevel@tonic-gate     default:
927c478bd9Sstevel@tonic-gate 	break;
937c478bd9Sstevel@tonic-gate     }
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
96*56a424ccSmp static int ipv6_enabled()
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
997c478bd9Sstevel@tonic-gate     static int result = -1;
1007c478bd9Sstevel@tonic-gate     if (result == -1) {
1017c478bd9Sstevel@tonic-gate 	int s;
1027c478bd9Sstevel@tonic-gate 	s = socket(AF_INET6, SOCK_STREAM, 0);
1037c478bd9Sstevel@tonic-gate 	if (s >= 0) {
1047c478bd9Sstevel@tonic-gate 	    result = 1;
1057c478bd9Sstevel@tonic-gate 	    close(s);
1067c478bd9Sstevel@tonic-gate 	} else
1077c478bd9Sstevel@tonic-gate 	    result = 0;
1087c478bd9Sstevel@tonic-gate     }
109*56a424ccSmp     return result;
1107c478bd9Sstevel@tonic-gate #else
111*56a424ccSmp     return 0;
1127c478bd9Sstevel@tonic-gate #endif
1137c478bd9Sstevel@tonic-gate }
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate static int
1167c478bd9Sstevel@tonic-gate setreuseaddr(int sock, int value)
1177c478bd9Sstevel@tonic-gate {
1187c478bd9Sstevel@tonic-gate     return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate #if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY)
1227c478bd9Sstevel@tonic-gate static int
1237c478bd9Sstevel@tonic-gate setv6only(int sock, int value)
1247c478bd9Sstevel@tonic-gate {
1257c478bd9Sstevel@tonic-gate     return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
1267c478bd9Sstevel@tonic-gate }
1277c478bd9Sstevel@tonic-gate #endif
1287c478bd9Sstevel@tonic-gate 
129*56a424ccSmp 
1307c478bd9Sstevel@tonic-gate static const char *paddr (struct sockaddr *sa)
1317c478bd9Sstevel@tonic-gate {
1327c478bd9Sstevel@tonic-gate     static char buf[100];
1337c478bd9Sstevel@tonic-gate     char portbuf[10];
1347c478bd9Sstevel@tonic-gate     if (getnameinfo(sa, socklen(sa),
1357c478bd9Sstevel@tonic-gate 		    buf, sizeof(buf), portbuf, sizeof(portbuf),
1367c478bd9Sstevel@tonic-gate 		    NI_NUMERICHOST|NI_NUMERICSERV))
1377c478bd9Sstevel@tonic-gate 	strcpy(buf, "<unprintable>");
1387c478bd9Sstevel@tonic-gate     else {
139*56a424ccSmp 	unsigned int len = sizeof(buf) - strlen(buf);
1407c478bd9Sstevel@tonic-gate 	char *p = buf + strlen(buf);
1417c478bd9Sstevel@tonic-gate 	if (len > 2+strlen(portbuf)) {
1427c478bd9Sstevel@tonic-gate 	    *p++ = '.';
1437c478bd9Sstevel@tonic-gate 	    len--;
1447c478bd9Sstevel@tonic-gate 	    strncpy(p, portbuf, len);
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate     }
1477c478bd9Sstevel@tonic-gate     return buf;
1487c478bd9Sstevel@tonic-gate }
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate /* KDC data.  */
1517c478bd9Sstevel@tonic-gate 
152*56a424ccSmp enum kdc_conn_type { CONN_UDP, CONN_TCP_LISTENER, CONN_TCP };
153*56a424ccSmp 
1547c478bd9Sstevel@tonic-gate /* Per-connection info.  */
1557c478bd9Sstevel@tonic-gate struct connection {
1567c478bd9Sstevel@tonic-gate     int fd;
157*56a424ccSmp     enum kdc_conn_type type;
1587c478bd9Sstevel@tonic-gate     void (*service)(struct connection *, const char *, int);
1597c478bd9Sstevel@tonic-gate     /* Solaris Kerberos: for auditing */
1607c478bd9Sstevel@tonic-gate     in_port_t port; /* local port */
1617c478bd9Sstevel@tonic-gate     union {
1627c478bd9Sstevel@tonic-gate 	/* Type-specific information.  */
1637c478bd9Sstevel@tonic-gate 	struct {
1647c478bd9Sstevel@tonic-gate 	    int x;
1657c478bd9Sstevel@tonic-gate 	} udp;
1667c478bd9Sstevel@tonic-gate 	struct {
1677c478bd9Sstevel@tonic-gate 	    int x;
1687c478bd9Sstevel@tonic-gate 	} tcp_listener;
1697c478bd9Sstevel@tonic-gate 	struct {
1707c478bd9Sstevel@tonic-gate 	    /* connection */
1717c478bd9Sstevel@tonic-gate 	    struct sockaddr_storage addr_s;
1727c478bd9Sstevel@tonic-gate 	    socklen_t addrlen;
1737c478bd9Sstevel@tonic-gate 	    char addrbuf[56];
1747c478bd9Sstevel@tonic-gate 	    krb5_fulladdr faddr;
1757c478bd9Sstevel@tonic-gate 	    krb5_address kaddr;
1767c478bd9Sstevel@tonic-gate 	    /* incoming */
1777c478bd9Sstevel@tonic-gate 	    size_t bufsiz;
1787c478bd9Sstevel@tonic-gate 	    size_t offset;
1797c478bd9Sstevel@tonic-gate 	    char *buffer;
1807c478bd9Sstevel@tonic-gate 	    size_t msglen;
1817c478bd9Sstevel@tonic-gate 	    /* outgoing */
1827c478bd9Sstevel@tonic-gate 	    krb5_data *response;
1837c478bd9Sstevel@tonic-gate 	    unsigned char lenbuf[4];
1847c478bd9Sstevel@tonic-gate 	    sg_buf sgbuf[2];
1857c478bd9Sstevel@tonic-gate 	    sg_buf *sgp;
1867c478bd9Sstevel@tonic-gate 	    int sgnum;
1877c478bd9Sstevel@tonic-gate 	    /* crude denial-of-service avoidance support */
1887c478bd9Sstevel@tonic-gate 	    time_t start_time;
1897c478bd9Sstevel@tonic-gate 	} tcp;
1907c478bd9Sstevel@tonic-gate     } u;
1917c478bd9Sstevel@tonic-gate };
1927c478bd9Sstevel@tonic-gate 
193*56a424ccSmp 
1947c478bd9Sstevel@tonic-gate #define SET(TYPE) struct { TYPE *data; int n, max; }
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate /* Start at the top and work down -- this should allow for deletions
1977c478bd9Sstevel@tonic-gate    without disrupting the iteration, since we delete by overwriting
1987c478bd9Sstevel@tonic-gate    the element to be removed with the last element.  */
1997c478bd9Sstevel@tonic-gate #define FOREACH_ELT(set,idx,vvar) \
2007c478bd9Sstevel@tonic-gate   for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate #define GROW_SET(set, incr, tmpptr) \
2037c478bd9Sstevel@tonic-gate   (((int)(set.max + incr) < set.max					\
2047c478bd9Sstevel@tonic-gate     || (((size_t)((int)(set.max + incr) * sizeof(set.data[0]))		\
2057c478bd9Sstevel@tonic-gate 	 / sizeof(set.data[0]))						\
2067c478bd9Sstevel@tonic-gate 	!= (set.max + incr)))						\
2077c478bd9Sstevel@tonic-gate    ? 0				/* overflow */				\
2087c478bd9Sstevel@tonic-gate    : ((tmpptr = realloc(set.data,					\
2097c478bd9Sstevel@tonic-gate 			(int)(set.max + incr) * sizeof(set.data[0])))	\
2107c478bd9Sstevel@tonic-gate       ? (set.data = tmpptr, set.max += incr, 1)				\
2117c478bd9Sstevel@tonic-gate       : 0))
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate /* 1 = success, 0 = failure */
2147c478bd9Sstevel@tonic-gate #define ADD(set, val, tmpptr) \
2157c478bd9Sstevel@tonic-gate   ((set.n < set.max || GROW_SET(set, 10, tmpptr))			\
2167c478bd9Sstevel@tonic-gate    ? (set.data[set.n++] = val, 1)					\
2177c478bd9Sstevel@tonic-gate    : 0)
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate #define DEL(set, idx) \
2207c478bd9Sstevel@tonic-gate   (set.data[idx] = set.data[--set.n], 0)
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate #define FREE_SET_DATA(set) if(set.data) free(set.data);                 \
2237c478bd9Sstevel@tonic-gate    (set.data = 0, set.max = 0)
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate /* Set<struct connection *> connections; */
2277c478bd9Sstevel@tonic-gate static SET(struct connection *) connections;
2287c478bd9Sstevel@tonic-gate #define n_sockets	connections.n
2297c478bd9Sstevel@tonic-gate #define conns		connections.data
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate /* Set<u_short> udp_port_data, tcp_port_data; */
2327c478bd9Sstevel@tonic-gate static SET(u_short) udp_port_data, tcp_port_data;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate #include <cm.h>
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate static struct select_state sstate;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate static krb5_error_code add_udp_port(int port)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate     int	i;
2417c478bd9Sstevel@tonic-gate     void *tmp;
2427c478bd9Sstevel@tonic-gate     u_short val;
2437c478bd9Sstevel@tonic-gate     u_short s_port = port;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate     if (s_port != port)
2467c478bd9Sstevel@tonic-gate 	return EINVAL;
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate     FOREACH_ELT (udp_port_data, i, val)
2497c478bd9Sstevel@tonic-gate 	if (s_port == val)
2507c478bd9Sstevel@tonic-gate 	    return 0;
2517c478bd9Sstevel@tonic-gate     if (!ADD(udp_port_data, s_port, tmp))
2527c478bd9Sstevel@tonic-gate 	return ENOMEM;
2537c478bd9Sstevel@tonic-gate     return 0;
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate static krb5_error_code add_tcp_port(int port)
2577c478bd9Sstevel@tonic-gate {
2587c478bd9Sstevel@tonic-gate     int	i;
2597c478bd9Sstevel@tonic-gate     void *tmp;
2607c478bd9Sstevel@tonic-gate     u_short val;
2617c478bd9Sstevel@tonic-gate     u_short s_port = port;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate     if (s_port != port)
2647c478bd9Sstevel@tonic-gate 	return EINVAL;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate     FOREACH_ELT (tcp_port_data, i, val)
2677c478bd9Sstevel@tonic-gate 	if (s_port == val)
2687c478bd9Sstevel@tonic-gate 	    return 0;
2697c478bd9Sstevel@tonic-gate     if (!ADD(tcp_port_data, s_port, tmp))
2707c478bd9Sstevel@tonic-gate 	return ENOMEM;
2717c478bd9Sstevel@tonic-gate     return 0;
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate 
274*56a424ccSmp 
2757c478bd9Sstevel@tonic-gate #define USE_AF AF_INET
2767c478bd9Sstevel@tonic-gate #define USE_TYPE SOCK_DGRAM
2777c478bd9Sstevel@tonic-gate #define USE_PROTO 0
2787c478bd9Sstevel@tonic-gate #define SOCKET_ERRNO errno
279*56a424ccSmp #include "foreachaddr.h"
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate struct socksetup {
2827c478bd9Sstevel@tonic-gate     const char *prog;
2837c478bd9Sstevel@tonic-gate     krb5_error_code retval;
2847c478bd9Sstevel@tonic-gate };
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static struct connection *
287*56a424ccSmp add_fd (struct socksetup *data, int sock, enum kdc_conn_type conntype,
2887c478bd9Sstevel@tonic-gate 	void (*service)(struct connection *, const char *, int))
2897c478bd9Sstevel@tonic-gate {
2907c478bd9Sstevel@tonic-gate     struct connection *newconn;
2917c478bd9Sstevel@tonic-gate     void *tmp;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate     newconn = malloc(sizeof(*newconn));
2947c478bd9Sstevel@tonic-gate     if (newconn == 0) {
2957c478bd9Sstevel@tonic-gate 	data->retval = errno;
2967c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
2977c478bd9Sstevel@tonic-gate 		gettext("cannot allocate storage for connection info"));
2987c478bd9Sstevel@tonic-gate 	return 0;
2997c478bd9Sstevel@tonic-gate     }
3007c478bd9Sstevel@tonic-gate     if (!ADD(connections, newconn, tmp)) {
3017c478bd9Sstevel@tonic-gate 	data->retval = errno;
3027c478bd9Sstevel@tonic-gate 	com_err(data->prog, data->retval, gettext("cannot save socket info"));
3037c478bd9Sstevel@tonic-gate 	free(newconn);
3047c478bd9Sstevel@tonic-gate 	return 0;
3057c478bd9Sstevel@tonic-gate     }
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate     memset(newconn, 0, sizeof(*newconn));
3087c478bd9Sstevel@tonic-gate     newconn->type = conntype;
3097c478bd9Sstevel@tonic-gate     newconn->fd = sock;
3107c478bd9Sstevel@tonic-gate     newconn->service = service;
3117c478bd9Sstevel@tonic-gate     return newconn;
3127c478bd9Sstevel@tonic-gate }
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate static void process_packet(struct connection *, const char *, int);
3157c478bd9Sstevel@tonic-gate static void accept_tcp_connection(struct connection *, const char *, int);
3167c478bd9Sstevel@tonic-gate static void process_tcp_connection(struct connection *, const char *, int);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate static struct connection *
3197c478bd9Sstevel@tonic-gate add_udp_fd (struct socksetup *data, int sock)
3207c478bd9Sstevel@tonic-gate {
3217c478bd9Sstevel@tonic-gate     return add_fd(data, sock, CONN_UDP, process_packet);
3227c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate static struct connection *
3257c478bd9Sstevel@tonic-gate add_tcp_listener_fd (struct socksetup *data, int sock)
3267c478bd9Sstevel@tonic-gate {
3277c478bd9Sstevel@tonic-gate     return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection);
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate static struct connection *
3317c478bd9Sstevel@tonic-gate add_tcp_data_fd (struct socksetup *data, int sock)
3327c478bd9Sstevel@tonic-gate {
3337c478bd9Sstevel@tonic-gate     return add_fd(data, sock, CONN_TCP, process_tcp_connection);
3347c478bd9Sstevel@tonic-gate }
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate static void
3377c478bd9Sstevel@tonic-gate delete_fd (struct connection *xconn)
3387c478bd9Sstevel@tonic-gate {
3397c478bd9Sstevel@tonic-gate     struct connection *conn;
3407c478bd9Sstevel@tonic-gate     int i;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate     FOREACH_ELT(connections, i, conn)
3437c478bd9Sstevel@tonic-gate 	if (conn == xconn) {
3447c478bd9Sstevel@tonic-gate 	    DEL(connections, i);
345*56a424ccSmp 	    break;
3467c478bd9Sstevel@tonic-gate 	}
3477c478bd9Sstevel@tonic-gate     free(xconn);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate static int
3517c478bd9Sstevel@tonic-gate setnbio(int sock)
3527c478bd9Sstevel@tonic-gate {
3537c478bd9Sstevel@tonic-gate     static const int one = 1;
3547c478bd9Sstevel@tonic-gate     return ioctlsocket(sock, FIONBIO, (const void *)&one);
3557c478bd9Sstevel@tonic-gate }
356*56a424ccSmp 
3577c478bd9Sstevel@tonic-gate static int
3587c478bd9Sstevel@tonic-gate setnolinger(int s)
3597c478bd9Sstevel@tonic-gate {
3607c478bd9Sstevel@tonic-gate     static const struct linger ling = { 0, 0 };
3617c478bd9Sstevel@tonic-gate     return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
3627c478bd9Sstevel@tonic-gate }
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate /* Returns -1 or socket fd.  */
3657c478bd9Sstevel@tonic-gate static int
3667c478bd9Sstevel@tonic-gate setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr)
3677c478bd9Sstevel@tonic-gate {
3687c478bd9Sstevel@tonic-gate     int sock;
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate     sock = socket(addr->sa_family, SOCK_STREAM, 0);
3717c478bd9Sstevel@tonic-gate     if (sock == -1) {
3727c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3737c478bd9Sstevel@tonic-gate 		gettext("Cannot create TCP server socket on %s"),
3747c478bd9Sstevel@tonic-gate 		paddr(addr));
3757c478bd9Sstevel@tonic-gate 	return -1;
3767c478bd9Sstevel@tonic-gate     }
3777c478bd9Sstevel@tonic-gate     /*
3787c478bd9Sstevel@tonic-gate      * Solaris Kerberos: noticed that there where bind problems for tcp sockets
3797c478bd9Sstevel@tonic-gate      * if kdc restarted quickly.  Setting SO_REUSEADDR allowed binds to succeed.
3807c478bd9Sstevel@tonic-gate      */
3817c478bd9Sstevel@tonic-gate     if (setreuseaddr(sock, 1) < 0) {
3827c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3837c478bd9Sstevel@tonic-gate 		gettext("enabling SO_REUSEADDR on TCP socket"));
3847c478bd9Sstevel@tonic-gate 	close(sock);
3857c478bd9Sstevel@tonic-gate 	return -1;
3867c478bd9Sstevel@tonic-gate     }
3877c478bd9Sstevel@tonic-gate     if (bind(sock, addr, socklen(addr)) == -1) {
3887c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3897c478bd9Sstevel@tonic-gate 		gettext("Cannot bind TCP server socket on %s"), paddr(addr));
3907c478bd9Sstevel@tonic-gate 	close(sock);
3917c478bd9Sstevel@tonic-gate 	return -1;
3927c478bd9Sstevel@tonic-gate     }
3937c478bd9Sstevel@tonic-gate     if (listen(sock, 5) < 0) {
3947c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3957c478bd9Sstevel@tonic-gate 		gettext("Cannot listen on TCP server socket on %s"),
3967c478bd9Sstevel@tonic-gate 		paddr(addr));
3977c478bd9Sstevel@tonic-gate 	close(sock);
3987c478bd9Sstevel@tonic-gate 	return -1;
3997c478bd9Sstevel@tonic-gate     }
4007c478bd9Sstevel@tonic-gate     if (setnbio(sock)) {
4017c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
4027c478bd9Sstevel@tonic-gate 		gettext("cannot set listening tcp socket on %s non-blocking"),
4037c478bd9Sstevel@tonic-gate 		paddr(addr));
4047c478bd9Sstevel@tonic-gate 	close(sock);
4057c478bd9Sstevel@tonic-gate 	return -1;
4067c478bd9Sstevel@tonic-gate     }
4077c478bd9Sstevel@tonic-gate     if (setnolinger(sock)) {
4087c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
4097c478bd9Sstevel@tonic-gate 		gettext("disabling SO_LINGER on TCP socket on %s"),
4107c478bd9Sstevel@tonic-gate 		paddr(addr));
4117c478bd9Sstevel@tonic-gate 	close(sock);
4127c478bd9Sstevel@tonic-gate 	return -1;
4137c478bd9Sstevel@tonic-gate     }
4147c478bd9Sstevel@tonic-gate     return sock;
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate static int
4187c478bd9Sstevel@tonic-gate setup_tcp_listener_ports(struct socksetup *data)
4197c478bd9Sstevel@tonic-gate {
4207c478bd9Sstevel@tonic-gate     struct sockaddr_in sin4;
4217c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4227c478bd9Sstevel@tonic-gate     struct sockaddr_in6 sin6;
4237c478bd9Sstevel@tonic-gate #endif
4247c478bd9Sstevel@tonic-gate     int i, port;
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate     memset(&sin4, 0, sizeof(sin4));
4277c478bd9Sstevel@tonic-gate     sin4.sin_family = AF_INET;
4287c478bd9Sstevel@tonic-gate #ifdef HAVE_SA_LEN
4297c478bd9Sstevel@tonic-gate     sin4.sin_len = sizeof(sin4);
4307c478bd9Sstevel@tonic-gate #endif
4317c478bd9Sstevel@tonic-gate     sin4.sin_addr.s_addr = INADDR_ANY;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4347c478bd9Sstevel@tonic-gate     memset(&sin6, 0, sizeof(sin6));
4357c478bd9Sstevel@tonic-gate     sin6.sin6_family = AF_INET6;
4367c478bd9Sstevel@tonic-gate #ifdef SIN6_LEN
4377c478bd9Sstevel@tonic-gate     sin6.sin6_len = sizeof(sin6);
4387c478bd9Sstevel@tonic-gate #endif
4397c478bd9Sstevel@tonic-gate     sin6.sin6_addr = in6addr_any;
4407c478bd9Sstevel@tonic-gate #endif
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate     FOREACH_ELT (tcp_port_data, i, port) {
4437c478bd9Sstevel@tonic-gate 	int s4, s6;
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	set_sa_port((struct sockaddr *)&sin4, htons(port));
4467c478bd9Sstevel@tonic-gate 	if (!ipv6_enabled()) {
4477c478bd9Sstevel@tonic-gate 	    s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
4487c478bd9Sstevel@tonic-gate 	    if (s4 < 0)
4497c478bd9Sstevel@tonic-gate 		return -1;
4507c478bd9Sstevel@tonic-gate 	    s6 = -1;
4517c478bd9Sstevel@tonic-gate 	} else {
4527c478bd9Sstevel@tonic-gate #ifndef KRB5_USE_INET6
4537c478bd9Sstevel@tonic-gate 	    abort();
4547c478bd9Sstevel@tonic-gate #else
4557c478bd9Sstevel@tonic-gate 	    s4 = s6 = -1;
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	    set_sa_port((struct sockaddr *)&sin6, htons(port));
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	    s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6);
4607c478bd9Sstevel@tonic-gate 	    if (s6 < 0)
4617c478bd9Sstevel@tonic-gate 		return -1;
4627c478bd9Sstevel@tonic-gate #ifdef IPV6_V6ONLY
4637c478bd9Sstevel@tonic-gate 	    if (setv6only(s6, 0))
4647c478bd9Sstevel@tonic-gate 		com_err(data->prog, errno,
4657c478bd9Sstevel@tonic-gate 		       	gettext("setsockopt(IPV6_V6ONLY,0) failed"));
4667c478bd9Sstevel@tonic-gate #endif
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	    s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
4697c478bd9Sstevel@tonic-gate #endif /* KRB5_USE_INET6 */
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	/* Sockets are created, prepare to listen on them.  */
4737c478bd9Sstevel@tonic-gate 	if (s4 >= 0) {
4747c478bd9Sstevel@tonic-gate 	    FD_SET(s4, &sstate.rfds);
4757c478bd9Sstevel@tonic-gate 	    if (s4 >= sstate.max)
4767c478bd9Sstevel@tonic-gate 		sstate.max = s4 + 1;
4777c478bd9Sstevel@tonic-gate 	    if (add_tcp_listener_fd(data, s4) == 0)
4787c478bd9Sstevel@tonic-gate 		close(s4);
4797c478bd9Sstevel@tonic-gate 	    else
480*56a424ccSmp 		krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
481*56a424ccSmp 				 s4, paddr((struct sockaddr *)&sin4));
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4847c478bd9Sstevel@tonic-gate 	if (s6 >= 0) {
4857c478bd9Sstevel@tonic-gate 	    FD_SET(s6, &sstate.rfds);
4867c478bd9Sstevel@tonic-gate 	    if (s6 >= sstate.max)
4877c478bd9Sstevel@tonic-gate 		sstate.max = s6 + 1;
4887c478bd9Sstevel@tonic-gate 	    if (add_tcp_listener_fd(data, s6) == 0) {
4897c478bd9Sstevel@tonic-gate 		close(s6);
4907c478bd9Sstevel@tonic-gate 		s6 = -1;
4917c478bd9Sstevel@tonic-gate 	    } else
492*56a424ccSmp 		krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
493*56a424ccSmp 				 s6, paddr((struct sockaddr *)&sin6));
4947c478bd9Sstevel@tonic-gate 	    if (s4 < 0)
4957c478bd9Sstevel@tonic-gate 		krb5_klog_syslog(LOG_INFO,
4967c478bd9Sstevel@tonic-gate 				 "assuming IPv6 socket accepts IPv4");
4977c478bd9Sstevel@tonic-gate 	}
4987c478bd9Sstevel@tonic-gate #endif
4997c478bd9Sstevel@tonic-gate     }
5007c478bd9Sstevel@tonic-gate     return 0;
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate static int
5047c478bd9Sstevel@tonic-gate setup_udp_port(void *P_data, struct sockaddr *addr)
5057c478bd9Sstevel@tonic-gate {
5067c478bd9Sstevel@tonic-gate     struct socksetup *data = P_data;
5077c478bd9Sstevel@tonic-gate     int sock = -1, i;
5087c478bd9Sstevel@tonic-gate     char haddrbuf[NI_MAXHOST];
5097c478bd9Sstevel@tonic-gate     int err;
5107c478bd9Sstevel@tonic-gate     u_short port;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate     err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf),
5137c478bd9Sstevel@tonic-gate 		      0, 0, NI_NUMERICHOST);
5147c478bd9Sstevel@tonic-gate     if (err)
5157c478bd9Sstevel@tonic-gate 	strcpy(haddrbuf, "<unprintable>");
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate     switch (addr->sa_family) {
5187c478bd9Sstevel@tonic-gate     case AF_INET:
5197c478bd9Sstevel@tonic-gate 	break;
5207c478bd9Sstevel@tonic-gate #ifdef AF_INET6
5217c478bd9Sstevel@tonic-gate     case AF_INET6:
5227c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
5237c478bd9Sstevel@tonic-gate 	break;
5247c478bd9Sstevel@tonic-gate #else
5257c478bd9Sstevel@tonic-gate 	{
5267c478bd9Sstevel@tonic-gate 	    static int first = 1;
5277c478bd9Sstevel@tonic-gate 	    if (first) {
5287c478bd9Sstevel@tonic-gate 		krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses");
5297c478bd9Sstevel@tonic-gate 		first = 0;
5307c478bd9Sstevel@tonic-gate 	    }
5317c478bd9Sstevel@tonic-gate 	    return 0;
5327c478bd9Sstevel@tonic-gate 	}
5337c478bd9Sstevel@tonic-gate #endif
5347c478bd9Sstevel@tonic-gate #endif
5357c478bd9Sstevel@tonic-gate #ifdef AF_LINK /* some BSD systems, AIX */
5367c478bd9Sstevel@tonic-gate     case AF_LINK:
5377c478bd9Sstevel@tonic-gate 	return 0;
538*56a424ccSmp #endif
539*56a424ccSmp #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */
540*56a424ccSmp     case AF_DLI:
541*56a424ccSmp 	return 0;
5427c478bd9Sstevel@tonic-gate #endif
5437c478bd9Sstevel@tonic-gate     default:
5447c478bd9Sstevel@tonic-gate 	krb5_klog_syslog (LOG_INFO,
5457c478bd9Sstevel@tonic-gate 			  "skipping unrecognized local address family %d",
5467c478bd9Sstevel@tonic-gate 			  addr->sa_family);
5477c478bd9Sstevel@tonic-gate 	return 0;
5487c478bd9Sstevel@tonic-gate     }
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate     FOREACH_ELT (udp_port_data, i, port) {
5517c478bd9Sstevel@tonic-gate 	sock = socket (addr->sa_family, SOCK_DGRAM, 0);
5527c478bd9Sstevel@tonic-gate 	if (sock == -1) {
5537c478bd9Sstevel@tonic-gate 	    data->retval = errno;
5547c478bd9Sstevel@tonic-gate 	    com_err(data->prog, data->retval,
5557c478bd9Sstevel@tonic-gate 		    gettext("Cannot create server socket for port %d address %s"),
5567c478bd9Sstevel@tonic-gate 		    port, haddrbuf);
5577c478bd9Sstevel@tonic-gate 	    return 1;
5587c478bd9Sstevel@tonic-gate 	}
5597c478bd9Sstevel@tonic-gate 	set_sa_port(addr, htons(port));
5607c478bd9Sstevel@tonic-gate 	if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) {
5617c478bd9Sstevel@tonic-gate 	    data->retval = errno;
5627c478bd9Sstevel@tonic-gate 	    com_err(data->prog, data->retval,
5637c478bd9Sstevel@tonic-gate 		    gettext("Cannot bind server socket to port %d address %s"),
5647c478bd9Sstevel@tonic-gate 		    port, haddrbuf);
5657c478bd9Sstevel@tonic-gate 	    return 1;
5667c478bd9Sstevel@tonic-gate 	}
5677c478bd9Sstevel@tonic-gate 	FD_SET (sock, &sstate.rfds);
5687c478bd9Sstevel@tonic-gate 	if (sock >= sstate.max)
5697c478bd9Sstevel@tonic-gate 	    sstate.max = sock + 1;
570*56a424ccSmp 	krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s", sock,
571*56a424ccSmp 			  paddr((struct sockaddr *)addr));
5727c478bd9Sstevel@tonic-gate 	if (add_udp_fd (data, sock) == 0)
5737c478bd9Sstevel@tonic-gate 	    return 1;
5747c478bd9Sstevel@tonic-gate     }
5757c478bd9Sstevel@tonic-gate     return 0;
5767c478bd9Sstevel@tonic-gate }
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate #if 1
5797c478bd9Sstevel@tonic-gate static void klog_handler(const void *data, size_t len)
5807c478bd9Sstevel@tonic-gate {
5817c478bd9Sstevel@tonic-gate     static char buf[BUFSIZ];
5827c478bd9Sstevel@tonic-gate     static int bufoffset;
5837c478bd9Sstevel@tonic-gate     void *p;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate #define flush_buf() \
5867c478bd9Sstevel@tonic-gate   (bufoffset						\
5877c478bd9Sstevel@tonic-gate    ? (((buf[0] == 0 || buf[0] == '\n')			\
5887c478bd9Sstevel@tonic-gate        ? (fork()==0?abort():(void)0)			\
5897c478bd9Sstevel@tonic-gate        : (void)0),					\
5907c478bd9Sstevel@tonic-gate       krb5_klog_syslog(LOG_INFO, "%s", buf),		\
5917c478bd9Sstevel@tonic-gate       memset(buf, 0, sizeof(buf)),			\
5927c478bd9Sstevel@tonic-gate       bufoffset = 0)					\
5937c478bd9Sstevel@tonic-gate    : 0)
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate     p = memchr(data, 0, len);
5967c478bd9Sstevel@tonic-gate     if (p)
5977c478bd9Sstevel@tonic-gate 	len = (const char *)p - (const char *)data;
5987c478bd9Sstevel@tonic-gate scan_for_newlines:
5997c478bd9Sstevel@tonic-gate     if (len == 0)
6007c478bd9Sstevel@tonic-gate 	return;
6017c478bd9Sstevel@tonic-gate     p = memchr(data, '\n', len);
6027c478bd9Sstevel@tonic-gate     if (p) {
6037c478bd9Sstevel@tonic-gate 	if (p != data)
6047c478bd9Sstevel@tonic-gate 	    klog_handler(data, (size_t)((const char *)p - (const char *)data));
6057c478bd9Sstevel@tonic-gate 	flush_buf();
6067c478bd9Sstevel@tonic-gate 	len -= ((const char *)p - (const char *)data) + 1;
6077c478bd9Sstevel@tonic-gate 	data = 1 + (const char *)p;
6087c478bd9Sstevel@tonic-gate 	goto scan_for_newlines;
6097c478bd9Sstevel@tonic-gate     } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) {
6107c478bd9Sstevel@tonic-gate 	size_t x = sizeof(buf) - len - 1;
6117c478bd9Sstevel@tonic-gate 	klog_handler(data, x);
6127c478bd9Sstevel@tonic-gate 	flush_buf();
6137c478bd9Sstevel@tonic-gate 	len -= x;
6147c478bd9Sstevel@tonic-gate 	data = (const char *)data + x;
6157c478bd9Sstevel@tonic-gate 	goto scan_for_newlines;
6167c478bd9Sstevel@tonic-gate     } else {
6177c478bd9Sstevel@tonic-gate 	memcpy(buf + bufoffset, data, len);
6187c478bd9Sstevel@tonic-gate 	bufoffset += len;
6197c478bd9Sstevel@tonic-gate     }
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate #endif
6227c478bd9Sstevel@tonic-gate 
623*56a424ccSmp /* XXX */
624*56a424ccSmp extern int krb5int_debug_sendto_kdc;
6257c478bd9Sstevel@tonic-gate extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate krb5_error_code
6287c478bd9Sstevel@tonic-gate setup_network(const char *prog)
6297c478bd9Sstevel@tonic-gate {
6307c478bd9Sstevel@tonic-gate     struct socksetup setup_data;
6317c478bd9Sstevel@tonic-gate     krb5_error_code retval;
6327c478bd9Sstevel@tonic-gate     char *cp;
6337c478bd9Sstevel@tonic-gate     int i, port;
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.rfds);
6367c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.wfds);
6377c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.xfds);
6387c478bd9Sstevel@tonic-gate     sstate.max = 0;
6397c478bd9Sstevel@tonic-gate 
640*56a424ccSmp /*    krb5int_debug_sendto_kdc = 1; */
6417c478bd9Sstevel@tonic-gate     krb5int_sendtokdc_debug_handler = klog_handler;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate     /* Handle each realm's ports */
6447c478bd9Sstevel@tonic-gate     for (i=0; i<kdc_numrealms; i++) {
6457c478bd9Sstevel@tonic-gate 	cp = kdc_realmlist[i]->realm_ports;
6467c478bd9Sstevel@tonic-gate 	while (cp && *cp) {
6477c478bd9Sstevel@tonic-gate 	    if (*cp == ',' || isspace((int) *cp)) {
6487c478bd9Sstevel@tonic-gate 		cp++;
6497c478bd9Sstevel@tonic-gate 		continue;
6507c478bd9Sstevel@tonic-gate 	    }
6517c478bd9Sstevel@tonic-gate 	    port = strtol(cp, &cp, 10);
6527c478bd9Sstevel@tonic-gate 	    if (cp == 0)
6537c478bd9Sstevel@tonic-gate 		break;
6547c478bd9Sstevel@tonic-gate 	    retval = add_udp_port(port);
6557c478bd9Sstevel@tonic-gate 	    if (retval)
6567c478bd9Sstevel@tonic-gate 		return retval;
6577c478bd9Sstevel@tonic-gate 	}
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	cp = kdc_realmlist[i]->realm_tcp_ports;
6607c478bd9Sstevel@tonic-gate 	while (cp && *cp) {
6617c478bd9Sstevel@tonic-gate 	    if (*cp == ',' || isspace((int) *cp)) {
6627c478bd9Sstevel@tonic-gate 		cp++;
6637c478bd9Sstevel@tonic-gate 		continue;
6647c478bd9Sstevel@tonic-gate 	    }
6657c478bd9Sstevel@tonic-gate 	    port = strtol(cp, &cp, 10);
6667c478bd9Sstevel@tonic-gate 	    if (cp == 0)
6677c478bd9Sstevel@tonic-gate 		break;
6687c478bd9Sstevel@tonic-gate 	    retval = add_tcp_port(port);
6697c478bd9Sstevel@tonic-gate 	    if (retval)
6707c478bd9Sstevel@tonic-gate 		return retval;
6717c478bd9Sstevel@tonic-gate 	}
6727c478bd9Sstevel@tonic-gate     }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate     setup_data.prog = prog;
6757c478bd9Sstevel@tonic-gate     setup_data.retval = 0;
6767c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_INFO, "setting up network...");
6777c478bd9Sstevel@tonic-gate     /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
6787c478bd9Sstevel@tonic-gate        so we might need only one UDP socket; fall back to binding
6797c478bd9Sstevel@tonic-gate        sockets on each address only if IPV6_PKTINFO isn't
6807c478bd9Sstevel@tonic-gate        supported.  */
6817c478bd9Sstevel@tonic-gate     if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) {
6827c478bd9Sstevel@tonic-gate 	return setup_data.retval;
6837c478bd9Sstevel@tonic-gate     }
6847c478bd9Sstevel@tonic-gate     setup_tcp_listener_ports(&setup_data);
6857c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
6867c478bd9Sstevel@tonic-gate     if (n_sockets == 0) {
6877c478bd9Sstevel@tonic-gate 	com_err(prog, 0, gettext("no sockets set up?"));
6887c478bd9Sstevel@tonic-gate 	exit (1);
6897c478bd9Sstevel@tonic-gate     }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate     return 0;
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
6957c478bd9Sstevel@tonic-gate {
6967c478bd9Sstevel@tonic-gate     switch (sa->sa_family) {
6977c478bd9Sstevel@tonic-gate     case AF_INET:
6987c478bd9Sstevel@tonic-gate 	faddr->address->addrtype = ADDRTYPE_INET;
6997c478bd9Sstevel@tonic-gate 	faddr->address->length = IPV4_ADDR_LEN;
7007c478bd9Sstevel@tonic-gate 	faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr;
7017c478bd9Sstevel@tonic-gate 	faddr->port = ntohs(sa2sin(sa)->sin_port);
7027c478bd9Sstevel@tonic-gate 	break;
7037c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
7047c478bd9Sstevel@tonic-gate     case AF_INET6:
7057c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) {
7067c478bd9Sstevel@tonic-gate 	    faddr->address->addrtype = ADDRTYPE_INET;
7077c478bd9Sstevel@tonic-gate 	    faddr->address->length = IPV4_ADDR_LEN;
7087c478bd9Sstevel@tonic-gate 	    /* offset to RAM address of ipv4 part of ipv6 address */
7097c478bd9Sstevel@tonic-gate 	    faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) +
7107c478bd9Sstevel@tonic-gate 		(krb5_octet *) &sa2sin6(sa)->sin6_addr;
7117c478bd9Sstevel@tonic-gate 	} else {
7127c478bd9Sstevel@tonic-gate 	    faddr->address->addrtype = ADDRTYPE_INET6;
7137c478bd9Sstevel@tonic-gate 	    faddr->address->length = IPV6_ADDR_LEN;
7147c478bd9Sstevel@tonic-gate 	    faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr;
7157c478bd9Sstevel@tonic-gate 	}
7167c478bd9Sstevel@tonic-gate 	faddr->port = ntohs(sa2sin6(sa)->sin6_port);
7177c478bd9Sstevel@tonic-gate 	break;
7187c478bd9Sstevel@tonic-gate #endif
7197c478bd9Sstevel@tonic-gate     default:
7207c478bd9Sstevel@tonic-gate 	faddr->address->addrtype = -1;
7217c478bd9Sstevel@tonic-gate 	faddr->address->length = 0;
7227c478bd9Sstevel@tonic-gate 	faddr->address->contents = 0;
7237c478bd9Sstevel@tonic-gate 	faddr->port = 0;
7247c478bd9Sstevel@tonic-gate 	break;
7257c478bd9Sstevel@tonic-gate     }
7267c478bd9Sstevel@tonic-gate }
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate static void process_packet(struct connection *conn, const char *prog,
7297c478bd9Sstevel@tonic-gate 			   int selflags)
7307c478bd9Sstevel@tonic-gate {
7317c478bd9Sstevel@tonic-gate     int cc;
7327c478bd9Sstevel@tonic-gate     socklen_t saddr_len;
7337c478bd9Sstevel@tonic-gate     krb5_fulladdr faddr;
7347c478bd9Sstevel@tonic-gate     krb5_error_code retval;
7357c478bd9Sstevel@tonic-gate     struct sockaddr_storage saddr;
7367c478bd9Sstevel@tonic-gate     krb5_address addr;
7377c478bd9Sstevel@tonic-gate     krb5_data request;
7387c478bd9Sstevel@tonic-gate     krb5_data *response;
7397c478bd9Sstevel@tonic-gate     char pktbuf[MAX_DGRAM_SIZE];
7407c478bd9Sstevel@tonic-gate     int port_fd = conn->fd;
741*56a424ccSmp 
7426cf54e34Sbugbomb     response = NULL;
7437c478bd9Sstevel@tonic-gate     saddr_len = sizeof(saddr);
7447c478bd9Sstevel@tonic-gate     cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0,
7457c478bd9Sstevel@tonic-gate 		  (struct sockaddr *)&saddr, &saddr_len);
7467c478bd9Sstevel@tonic-gate     if (cc == -1) {
7477c478bd9Sstevel@tonic-gate 	if (errno != EINTR
7487c478bd9Sstevel@tonic-gate 	    /* This is how Linux indicates that a previous
7497c478bd9Sstevel@tonic-gate 	       transmission was refused, e.g., if the client timed out
7507c478bd9Sstevel@tonic-gate 	       before getting the response packet.  */
7517c478bd9Sstevel@tonic-gate 	    && errno != ECONNREFUSED
7527c478bd9Sstevel@tonic-gate 	    )
7537c478bd9Sstevel@tonic-gate 	    com_err(prog, errno, gettext("while receiving from network"));
7547c478bd9Sstevel@tonic-gate 	return;
7557c478bd9Sstevel@tonic-gate     }
7567c478bd9Sstevel@tonic-gate     if (!cc)
7577c478bd9Sstevel@tonic-gate 	return;		/* zero-length packet? */
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate     request.length = cc;
7607c478bd9Sstevel@tonic-gate     request.data = pktbuf;
7617c478bd9Sstevel@tonic-gate     faddr.address = &addr;
7627c478bd9Sstevel@tonic-gate     init_addr(&faddr, ss2sa(&saddr));
7637c478bd9Sstevel@tonic-gate     /* this address is in net order */
764*56a424ccSmp     if ((retval = dispatch(&request, &faddr, &response))) {
7657c478bd9Sstevel@tonic-gate 	com_err(prog, retval, gettext("while dispatching (udp)"));
7667c478bd9Sstevel@tonic-gate 	return;
7677c478bd9Sstevel@tonic-gate     }
7687c478bd9Sstevel@tonic-gate     cc = sendto(port_fd, response->data, (socklen_t) response->length, 0,
7697c478bd9Sstevel@tonic-gate 		(struct sockaddr *)&saddr, saddr_len);
7707c478bd9Sstevel@tonic-gate     if (cc == -1) {
7717c478bd9Sstevel@tonic-gate 	char addrbuf[46];
7727c478bd9Sstevel@tonic-gate         krb5_free_data(kdc_context, response);
7737c478bd9Sstevel@tonic-gate 	if (inet_ntop(((struct sockaddr *)&saddr)->sa_family,
7747c478bd9Sstevel@tonic-gate 		      addr.contents, addrbuf, sizeof(addrbuf)) == 0) {
7757c478bd9Sstevel@tonic-gate 	    strcpy(addrbuf, "?");
7767c478bd9Sstevel@tonic-gate 	}
7777c478bd9Sstevel@tonic-gate 	com_err(prog, errno, gettext("while sending reply to %s/%d"),
7787c478bd9Sstevel@tonic-gate 		addrbuf, faddr.port);
7797c478bd9Sstevel@tonic-gate 	return;
7807c478bd9Sstevel@tonic-gate     }
7817c478bd9Sstevel@tonic-gate     if (cc != response->length) {
7827c478bd9Sstevel@tonic-gate 	krb5_free_data(kdc_context, response);
7837c478bd9Sstevel@tonic-gate 	com_err(prog, 0, gettext("short reply write %d vs %d\n"),
7847c478bd9Sstevel@tonic-gate 		response->length, cc);
7857c478bd9Sstevel@tonic-gate 	return;
7867c478bd9Sstevel@tonic-gate     }
7877c478bd9Sstevel@tonic-gate     krb5_free_data(kdc_context, response);
7887c478bd9Sstevel@tonic-gate     return;
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate static int tcp_data_counter;
7927c478bd9Sstevel@tonic-gate /* Solaris kerberos: getting this value from elsewhere */
7937c478bd9Sstevel@tonic-gate extern int max_tcp_data_connections;
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate static void kill_tcp_connection(struct connection *);
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate static void accept_tcp_connection(struct connection *conn, const char *prog,
7987c478bd9Sstevel@tonic-gate 				  int selflags)
7997c478bd9Sstevel@tonic-gate {
8007c478bd9Sstevel@tonic-gate     int s;
8017c478bd9Sstevel@tonic-gate     struct sockaddr_storage addr_s;
8027c478bd9Sstevel@tonic-gate     struct sockaddr *addr = (struct sockaddr *)&addr_s;
8037c478bd9Sstevel@tonic-gate     socklen_t addrlen = sizeof(addr_s);
8047c478bd9Sstevel@tonic-gate     struct socksetup sockdata;
8057c478bd9Sstevel@tonic-gate     struct connection *newconn;
8067c478bd9Sstevel@tonic-gate     char tmpbuf[10];
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate     s = accept(conn->fd, addr, &addrlen);
8097c478bd9Sstevel@tonic-gate     if (s < 0)
8107c478bd9Sstevel@tonic-gate 	return;
8117c478bd9Sstevel@tonic-gate     setnbio(s), setnolinger(s);
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate     sockdata.prog = prog;
8147c478bd9Sstevel@tonic-gate     sockdata.retval = 0;
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate     newconn = add_tcp_data_fd(&sockdata, s);
8177c478bd9Sstevel@tonic-gate     if (newconn == 0)
8187c478bd9Sstevel@tonic-gate 	return;
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate     if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
8217c478bd9Sstevel@tonic-gate 		    newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
8227c478bd9Sstevel@tonic-gate 		    tmpbuf, sizeof(tmpbuf),
8237c478bd9Sstevel@tonic-gate 		    NI_NUMERICHOST | NI_NUMERICSERV))
8247c478bd9Sstevel@tonic-gate 	strcpy(newconn->u.tcp.addrbuf, "???");
8257c478bd9Sstevel@tonic-gate     else {
8267c478bd9Sstevel@tonic-gate 	char *p, *end;
8277c478bd9Sstevel@tonic-gate 	p = newconn->u.tcp.addrbuf;
8287c478bd9Sstevel@tonic-gate 	end = p + sizeof(newconn->u.tcp.addrbuf);
8297c478bd9Sstevel@tonic-gate 	p += strlen(p);
8307c478bd9Sstevel@tonic-gate 	if (end - p > 2 + strlen(tmpbuf)) {
8317c478bd9Sstevel@tonic-gate 	    *p++ = '.';
8327c478bd9Sstevel@tonic-gate 	    strcpy(p, tmpbuf);
8337c478bd9Sstevel@tonic-gate 	}
8347c478bd9Sstevel@tonic-gate     }
835*56a424ccSmp #if 0
836*56a424ccSmp     krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s",
837*56a424ccSmp 		     s, newconn->u.tcp.addrbuf);
838*56a424ccSmp #endif
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate     newconn->u.tcp.addr_s = addr_s;
8417c478bd9Sstevel@tonic-gate     newconn->u.tcp.addrlen = addrlen;
8427c478bd9Sstevel@tonic-gate     newconn->u.tcp.bufsiz = 1024 * 1024;
8437c478bd9Sstevel@tonic-gate     newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz);
8447c478bd9Sstevel@tonic-gate     newconn->u.tcp.start_time = time(0);
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate     if (++tcp_data_counter > max_tcp_data_connections) {
8477c478bd9Sstevel@tonic-gate 	struct connection *oldest_tcp = NULL;
8487c478bd9Sstevel@tonic-gate 	struct connection *c;
8497c478bd9Sstevel@tonic-gate 	int i;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	krb5_klog_syslog(LOG_INFO, "too many connections");
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	FOREACH_ELT (connections, i, c) {
8547c478bd9Sstevel@tonic-gate 	    if (c->type != CONN_TCP)
8557c478bd9Sstevel@tonic-gate 		continue;
8567c478bd9Sstevel@tonic-gate 	    if (c == newconn)
8577c478bd9Sstevel@tonic-gate 		continue;
8587c478bd9Sstevel@tonic-gate #if 0
8597c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd,
8607c478bd9Sstevel@tonic-gate 			     c->u.tcp.start_time);
8617c478bd9Sstevel@tonic-gate #endif
8627c478bd9Sstevel@tonic-gate 	    if (oldest_tcp == NULL
8637c478bd9Sstevel@tonic-gate 		|| oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
8647c478bd9Sstevel@tonic-gate 		oldest_tcp = c;
8657c478bd9Sstevel@tonic-gate 	}
8667c478bd9Sstevel@tonic-gate 	if (oldest_tcp != NULL) {
8677c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s",
8687c478bd9Sstevel@tonic-gate 			     oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
8697c478bd9Sstevel@tonic-gate 	    kill_tcp_connection(oldest_tcp);
8707c478bd9Sstevel@tonic-gate 	    oldest_tcp = NULL;
8717c478bd9Sstevel@tonic-gate 	}
8727c478bd9Sstevel@tonic-gate     }
8737c478bd9Sstevel@tonic-gate     if (newconn->u.tcp.buffer == 0) {
8747c478bd9Sstevel@tonic-gate 	com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"),
8757c478bd9Sstevel@tonic-gate 		newconn->u.tcp.addrbuf);
8767c478bd9Sstevel@tonic-gate 	delete_fd(newconn);
8777c478bd9Sstevel@tonic-gate 	close(s);
878*56a424ccSmp 	tcp_data_counter--;
8797c478bd9Sstevel@tonic-gate 	return;
8807c478bd9Sstevel@tonic-gate     }
8817c478bd9Sstevel@tonic-gate     newconn->u.tcp.offset = 0;
8827c478bd9Sstevel@tonic-gate     newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
8837c478bd9Sstevel@tonic-gate     init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
8847c478bd9Sstevel@tonic-gate     SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
8857c478bd9Sstevel@tonic-gate     SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate     FD_SET(s, &sstate.rfds);
8887c478bd9Sstevel@tonic-gate     if (sstate.max <= s)
8897c478bd9Sstevel@tonic-gate 	sstate.max = s + 1;
8907c478bd9Sstevel@tonic-gate }
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate static void
8937c478bd9Sstevel@tonic-gate kill_tcp_connection(struct connection *conn)
8947c478bd9Sstevel@tonic-gate {
8957c478bd9Sstevel@tonic-gate     if (conn->u.tcp.response)
8967c478bd9Sstevel@tonic-gate 	krb5_free_data(kdc_context, conn->u.tcp.response);
8977c478bd9Sstevel@tonic-gate     if (conn->u.tcp.buffer)
8987c478bd9Sstevel@tonic-gate 	free(conn->u.tcp.buffer);
8997c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &sstate.rfds);
9007c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &sstate.wfds);
9017c478bd9Sstevel@tonic-gate     if (sstate.max == conn->fd + 1)
9027c478bd9Sstevel@tonic-gate 	while (sstate.max > 0
9037c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(sstate.max-1, &sstate.rfds)
9047c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(sstate.max-1, &sstate.wfds)
9057c478bd9Sstevel@tonic-gate 	       /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
9067c478bd9Sstevel@tonic-gate 	    )
9077c478bd9Sstevel@tonic-gate 	    sstate.max--;
9087c478bd9Sstevel@tonic-gate     close(conn->fd);
9097c478bd9Sstevel@tonic-gate     conn->fd = -1;
9107c478bd9Sstevel@tonic-gate     delete_fd(conn);
911*56a424ccSmp     tcp_data_counter--;
9127c478bd9Sstevel@tonic-gate }
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate static void
9157c478bd9Sstevel@tonic-gate process_tcp_connection(struct connection *conn, const char *prog, int selflags)
9167c478bd9Sstevel@tonic-gate {
9177c478bd9Sstevel@tonic-gate     if (selflags & SSF_WRITE) {
9187c478bd9Sstevel@tonic-gate 	ssize_t nwrote;
9197c478bd9Sstevel@tonic-gate 	SOCKET_WRITEV_TEMP tmp;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
9227c478bd9Sstevel@tonic-gate 			       tmp);
9237c478bd9Sstevel@tonic-gate 	if (nwrote < 0) {
9247c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate 	if (nwrote == 0)
9277c478bd9Sstevel@tonic-gate 	    /* eof */
9287c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9297c478bd9Sstevel@tonic-gate 	while (nwrote) {
9307c478bd9Sstevel@tonic-gate 	    sg_buf *sgp = conn->u.tcp.sgp;
9317c478bd9Sstevel@tonic-gate 	    if (nwrote < SG_LEN(sgp)) {
9327c478bd9Sstevel@tonic-gate 		SG_ADVANCE(sgp, nwrote);
9337c478bd9Sstevel@tonic-gate 		nwrote = 0;
9347c478bd9Sstevel@tonic-gate 	    } else {
9357c478bd9Sstevel@tonic-gate 		nwrote -= SG_LEN(sgp);
9367c478bd9Sstevel@tonic-gate 		conn->u.tcp.sgp++;
9377c478bd9Sstevel@tonic-gate 		conn->u.tcp.sgnum--;
9387c478bd9Sstevel@tonic-gate 		if (conn->u.tcp.sgnum == 0 && nwrote != 0)
9397c478bd9Sstevel@tonic-gate 		    abort();
9407c478bd9Sstevel@tonic-gate 	    }
9417c478bd9Sstevel@tonic-gate 	}
9427c478bd9Sstevel@tonic-gate 	if (conn->u.tcp.sgnum == 0) {
9437c478bd9Sstevel@tonic-gate 	    /* finished sending */
9447c478bd9Sstevel@tonic-gate 	    /* should go back to reading */
9457c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9467c478bd9Sstevel@tonic-gate 	}
9477c478bd9Sstevel@tonic-gate     } else if (selflags & SSF_READ) {
9487c478bd9Sstevel@tonic-gate 	/* Read message length and data into one big buffer, already
9497c478bd9Sstevel@tonic-gate 	   allocated at connect time.  If we have a complete message,
9507c478bd9Sstevel@tonic-gate 	   we stop reading, so we should only be here if there is no
9517c478bd9Sstevel@tonic-gate 	   data in the buffer, or only an incomplete message.  */
9527c478bd9Sstevel@tonic-gate 	size_t len;
9537c478bd9Sstevel@tonic-gate 	ssize_t nread;
9547c478bd9Sstevel@tonic-gate 	if (conn->u.tcp.offset < 4) {
9557c478bd9Sstevel@tonic-gate 	    /* msglen has not been computed */
9567c478bd9Sstevel@tonic-gate 	    /* XXX Doing at least two reads here, letting the kernel
9577c478bd9Sstevel@tonic-gate 	       worry about buffering.  It'll be faster when we add
9587c478bd9Sstevel@tonic-gate 	       code to manage the buffer here.  */
9597c478bd9Sstevel@tonic-gate 	    len = 4 - conn->u.tcp.offset;
9607c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
9617c478bd9Sstevel@tonic-gate 				conn->u.tcp.buffer + conn->u.tcp.offset, len);
9627c478bd9Sstevel@tonic-gate 	    if (nread < 0)
9637c478bd9Sstevel@tonic-gate 		/* error */
9647c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
9657c478bd9Sstevel@tonic-gate 	    if (nread == 0)
9667c478bd9Sstevel@tonic-gate 		/* eof */
9677c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
9687c478bd9Sstevel@tonic-gate 	    conn->u.tcp.offset += nread;
9697c478bd9Sstevel@tonic-gate 	    if (conn->u.tcp.offset == 4) {
9707c478bd9Sstevel@tonic-gate 		unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
9717c478bd9Sstevel@tonic-gate 		conn->u.tcp.msglen = ((p[0] << 24)
9727c478bd9Sstevel@tonic-gate 				      | (p[1] << 16)
9737c478bd9Sstevel@tonic-gate 				      | (p[2] <<  8)
9747c478bd9Sstevel@tonic-gate 				      | p[3]);
9757c478bd9Sstevel@tonic-gate 		if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
9767c478bd9Sstevel@tonic-gate 		    /* message too big */
9777c478bd9Sstevel@tonic-gate 		    krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu",
9787c478bd9Sstevel@tonic-gate 				     conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen,
9797c478bd9Sstevel@tonic-gate 				     (unsigned long) conn->u.tcp.bufsiz - 4);
9807c478bd9Sstevel@tonic-gate 		    /* XXX Should return an error.  */
9817c478bd9Sstevel@tonic-gate 		    goto kill_tcp_connection;
9827c478bd9Sstevel@tonic-gate 		}
9837c478bd9Sstevel@tonic-gate 	    }
9847c478bd9Sstevel@tonic-gate 	} else {
9857c478bd9Sstevel@tonic-gate 	    /* msglen known */
9867c478bd9Sstevel@tonic-gate 	    krb5_data request;
9877c478bd9Sstevel@tonic-gate 	    krb5_error_code err;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	    len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
9907c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
9917c478bd9Sstevel@tonic-gate 				conn->u.tcp.buffer + conn->u.tcp.offset, len);
9927c478bd9Sstevel@tonic-gate 	    if (nread < 0)
9937c478bd9Sstevel@tonic-gate 		/* error */
9947c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
9957c478bd9Sstevel@tonic-gate 	    if (nread == 0)
9967c478bd9Sstevel@tonic-gate 		/* eof */
9977c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
9987c478bd9Sstevel@tonic-gate 	    conn->u.tcp.offset += nread;
9997c478bd9Sstevel@tonic-gate 	    if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
10007c478bd9Sstevel@tonic-gate 		return;
10017c478bd9Sstevel@tonic-gate 	    /* have a complete message, and exactly one message */
10027c478bd9Sstevel@tonic-gate 	    request.length = conn->u.tcp.msglen;
10037c478bd9Sstevel@tonic-gate 	    request.data = conn->u.tcp.buffer + 4;
1004*56a424ccSmp 	    err = dispatch(&request, &conn->u.tcp.faddr,
10057c478bd9Sstevel@tonic-gate 			   &conn->u.tcp.response);
10067c478bd9Sstevel@tonic-gate 	    if (err) {
10077c478bd9Sstevel@tonic-gate 		com_err(prog, err, gettext("while dispatching (tcp)"));
10087c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
10097c478bd9Sstevel@tonic-gate 	    }
10107c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24);
10117c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16);
10127c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8);
10137c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0);
10147c478bd9Sstevel@tonic-gate 	    SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
10157c478bd9Sstevel@tonic-gate 		   conn->u.tcp.response->length);
10167c478bd9Sstevel@tonic-gate 	    conn->u.tcp.sgp = conn->u.tcp.sgbuf;
10177c478bd9Sstevel@tonic-gate 	    conn->u.tcp.sgnum = 2;
10187c478bd9Sstevel@tonic-gate 	    FD_CLR(conn->fd, &sstate.rfds);
10197c478bd9Sstevel@tonic-gate 	    FD_SET(conn->fd, &sstate.wfds);
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate     } else
10227c478bd9Sstevel@tonic-gate 	abort();
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate     return;
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate kill_tcp_connection:
10277c478bd9Sstevel@tonic-gate     kill_tcp_connection(conn);
10287c478bd9Sstevel@tonic-gate }
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate static void service_conn(struct connection *conn, const char *prog,
10317c478bd9Sstevel@tonic-gate 			 int selflags)
10327c478bd9Sstevel@tonic-gate {
10337c478bd9Sstevel@tonic-gate     conn->service(conn, prog, selflags);
10347c478bd9Sstevel@tonic-gate }
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate krb5_error_code
10377c478bd9Sstevel@tonic-gate listen_and_process(const char *prog)
10387c478bd9Sstevel@tonic-gate {
10397c478bd9Sstevel@tonic-gate     int			nfound;
10407c478bd9Sstevel@tonic-gate     struct select_state sout;
10417c478bd9Sstevel@tonic-gate     int			i, sret;
10427c478bd9Sstevel@tonic-gate     krb5_error_code	err;
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate     if (conns == (struct connection **) NULL)
10457c478bd9Sstevel@tonic-gate 	return KDC5_NONET;
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate     while (!signal_requests_exit) {
10487c478bd9Sstevel@tonic-gate 	if (signal_requests_hup) {
10497c478bd9Sstevel@tonic-gate 	    krb5_klog_reopen(kdc_context);
10507c478bd9Sstevel@tonic-gate 	    signal_requests_hup = 0;
10517c478bd9Sstevel@tonic-gate 	}
10527c478bd9Sstevel@tonic-gate 	sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
10537c478bd9Sstevel@tonic-gate 	err = krb5int_cm_call_select(&sstate, &sout, &sret);
10547c478bd9Sstevel@tonic-gate 	if (err) {
10557c478bd9Sstevel@tonic-gate 	    com_err(prog, err, gettext("while selecting for network input(1)"));
10567c478bd9Sstevel@tonic-gate 	    continue;
10577c478bd9Sstevel@tonic-gate 	}
10587c478bd9Sstevel@tonic-gate 	if (sret == -1) {
10597c478bd9Sstevel@tonic-gate 	    if (errno != EINTR)
10607c478bd9Sstevel@tonic-gate 		com_err(prog, errno, gettext("while selecting for network input(2)"));
10617c478bd9Sstevel@tonic-gate 	    continue;
10627c478bd9Sstevel@tonic-gate 	}
10637c478bd9Sstevel@tonic-gate 	nfound = sret;
10647c478bd9Sstevel@tonic-gate 	for (i=0; i<n_sockets && nfound > 0; i++) {
10657c478bd9Sstevel@tonic-gate 	    int sflags = 0;
10667c478bd9Sstevel@tonic-gate 	    if (conns[i]->fd < 0)
10677c478bd9Sstevel@tonic-gate 		abort();
10687c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i]->fd, &sout.rfds))
10697c478bd9Sstevel@tonic-gate 		sflags |= SSF_READ, nfound--;
10707c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i]->fd, &sout.wfds))
10717c478bd9Sstevel@tonic-gate 		sflags |= SSF_WRITE, nfound--;
10727c478bd9Sstevel@tonic-gate 	    if (sflags)
10737c478bd9Sstevel@tonic-gate 		service_conn(conns[i], prog, sflags);
10747c478bd9Sstevel@tonic-gate 	}
10757c478bd9Sstevel@tonic-gate     }
10767c478bd9Sstevel@tonic-gate     return 0;
10777c478bd9Sstevel@tonic-gate }
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate krb5_error_code
10807c478bd9Sstevel@tonic-gate closedown_network(const char *prog)
10817c478bd9Sstevel@tonic-gate {
10827c478bd9Sstevel@tonic-gate     int i;
10837c478bd9Sstevel@tonic-gate     struct connection *conn;
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate     if (conns == (struct connection **) NULL)
10867c478bd9Sstevel@tonic-gate 	return KDC5_NONET;
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate     FOREACH_ELT (connections, i, conn) {
10897c478bd9Sstevel@tonic-gate 	if (conn->fd >= 0)
10907c478bd9Sstevel@tonic-gate 	    (void) close(conn->fd);
10917c478bd9Sstevel@tonic-gate 	DEL (connections, i);
1092*56a424ccSmp 	/* There may also be per-connection data in the tcp structure
1093*56a424ccSmp 	   (tcp.buffer, tcp.response) that we're not freeing here.
1094*56a424ccSmp 	   That should only happen if we quit with a connection in
1095*56a424ccSmp 	   progress.  */
1096*56a424ccSmp 	free(conn);
10977c478bd9Sstevel@tonic-gate     }
10987c478bd9Sstevel@tonic-gate     FREE_SET_DATA(connections);
10997c478bd9Sstevel@tonic-gate     FREE_SET_DATA(udp_port_data);
11007c478bd9Sstevel@tonic-gate     FREE_SET_DATA(tcp_port_data);
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate     return 0;
11037c478bd9Sstevel@tonic-gate }
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate #endif /* INET */
1106