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