xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/network.c (revision 55fea89d)
17c478bd9Sstevel@tonic-gate /*
2159d09a2SMark Phalan  * Copyright 2008 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.
15*55fea89dSDan Cross  *
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.
29*55fea89dSDan Cross  *
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * Network code for Kerberos v5 KDC.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include "k5-int.h"
357c478bd9Sstevel@tonic-gate #include "com_err.h"
367c478bd9Sstevel@tonic-gate #include "kdc_util.h"
377c478bd9Sstevel@tonic-gate #include "extern.h"
387c478bd9Sstevel@tonic-gate #include "kdc5_err.h"
397c478bd9Sstevel@tonic-gate #include "adm_proto.h"
407c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
417c478bd9Sstevel@tonic-gate #include <syslog.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <stddef.h>
447c478bd9Sstevel@tonic-gate #include <ctype.h>
45159d09a2SMark Phalan #include "port-sockets.h"
46159d09a2SMark Phalan /* #include "socket-utils.h" */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H
497c478bd9Sstevel@tonic-gate #include <sys/types.h>
507c478bd9Sstevel@tonic-gate #include <netinet/in.h>
517c478bd9Sstevel@tonic-gate #include <sys/socket.h>
527c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_SOCKIO_H
537c478bd9Sstevel@tonic-gate /* for SIOCGIFCONF, etc. */
547c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
557c478bd9Sstevel@tonic-gate #endif
567c478bd9Sstevel@tonic-gate #include <sys/time.h>
577c478bd9Sstevel@tonic-gate #include <libintl.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #if HAVE_SYS_SELECT_H
607c478bd9Sstevel@tonic-gate #include <sys/select.h>
617c478bd9Sstevel@tonic-gate #endif
627c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
637c478bd9Sstevel@tonic-gate #include <inet/ip.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
677c478bd9Sstevel@tonic-gate #include <net/if.h>
687c478bd9Sstevel@tonic-gate #endif
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H
717c478bd9Sstevel@tonic-gate #include <sys/filio.h>		/* FIONBIO */
727c478bd9Sstevel@tonic-gate #endif
737c478bd9Sstevel@tonic-gate 
74159d09a2SMark Phalan #include "fake-addrinfo.h"
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /* Misc utility routines.  */
777c478bd9Sstevel@tonic-gate static void
set_sa_port(struct sockaddr * addr,int port)787c478bd9Sstevel@tonic-gate set_sa_port(struct sockaddr *addr, int port)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate     switch (addr->sa_family) {
817c478bd9Sstevel@tonic-gate     case AF_INET:
827c478bd9Sstevel@tonic-gate 	sa2sin(addr)->sin_port = port;
837c478bd9Sstevel@tonic-gate 	break;
847c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
857c478bd9Sstevel@tonic-gate     case AF_INET6:
867c478bd9Sstevel@tonic-gate 	sa2sin6(addr)->sin6_port = port;
877c478bd9Sstevel@tonic-gate 	break;
887c478bd9Sstevel@tonic-gate #endif
897c478bd9Sstevel@tonic-gate     default:
907c478bd9Sstevel@tonic-gate 	break;
917c478bd9Sstevel@tonic-gate     }
927c478bd9Sstevel@tonic-gate }
937c478bd9Sstevel@tonic-gate 
ipv6_enabled()9456a424ccSmp static int ipv6_enabled()
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
977c478bd9Sstevel@tonic-gate     static int result = -1;
987c478bd9Sstevel@tonic-gate     if (result == -1) {
997c478bd9Sstevel@tonic-gate 	int s;
1007c478bd9Sstevel@tonic-gate 	s = socket(AF_INET6, SOCK_STREAM, 0);
1017c478bd9Sstevel@tonic-gate 	if (s >= 0) {
1027c478bd9Sstevel@tonic-gate 	    result = 1;
1037c478bd9Sstevel@tonic-gate 	    close(s);
1047c478bd9Sstevel@tonic-gate 	} else
1057c478bd9Sstevel@tonic-gate 	    result = 0;
1067c478bd9Sstevel@tonic-gate     }
10756a424ccSmp     return result;
1087c478bd9Sstevel@tonic-gate #else
10956a424ccSmp     return 0;
1107c478bd9Sstevel@tonic-gate #endif
1117c478bd9Sstevel@tonic-gate }
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate static int
setreuseaddr(int sock,int value)1147c478bd9Sstevel@tonic-gate setreuseaddr(int sock, int value)
1157c478bd9Sstevel@tonic-gate {
1167c478bd9Sstevel@tonic-gate     return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate #if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY)
1207c478bd9Sstevel@tonic-gate static int
setv6only(int sock,int value)1217c478bd9Sstevel@tonic-gate setv6only(int sock, int value)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate     return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate #endif
1267c478bd9Sstevel@tonic-gate 
12756a424ccSmp 
paddr(struct sockaddr * sa)1287c478bd9Sstevel@tonic-gate static const char *paddr (struct sockaddr *sa)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate     static char buf[100];
1317c478bd9Sstevel@tonic-gate     char portbuf[10];
1327c478bd9Sstevel@tonic-gate     if (getnameinfo(sa, socklen(sa),
1337c478bd9Sstevel@tonic-gate 		    buf, sizeof(buf), portbuf, sizeof(portbuf),
1347c478bd9Sstevel@tonic-gate 		    NI_NUMERICHOST|NI_NUMERICSERV))
1357c478bd9Sstevel@tonic-gate 	strcpy(buf, "<unprintable>");
1367c478bd9Sstevel@tonic-gate     else {
13756a424ccSmp 	unsigned int len = sizeof(buf) - strlen(buf);
1387c478bd9Sstevel@tonic-gate 	char *p = buf + strlen(buf);
1397c478bd9Sstevel@tonic-gate 	if (len > 2+strlen(portbuf)) {
1407c478bd9Sstevel@tonic-gate 	    *p++ = '.';
1417c478bd9Sstevel@tonic-gate 	    len--;
1427c478bd9Sstevel@tonic-gate 	    strncpy(p, portbuf, len);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate     }
1457c478bd9Sstevel@tonic-gate     return buf;
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate /* KDC data.  */
1497c478bd9Sstevel@tonic-gate 
15056a424ccSmp enum kdc_conn_type { CONN_UDP, CONN_TCP_LISTENER, CONN_TCP };
15156a424ccSmp 
1527c478bd9Sstevel@tonic-gate /* Per-connection info.  */
1537c478bd9Sstevel@tonic-gate struct connection {
1547c478bd9Sstevel@tonic-gate     int fd;
15556a424ccSmp     enum kdc_conn_type type;
1567c478bd9Sstevel@tonic-gate     void (*service)(struct connection *, const char *, int);
1577c478bd9Sstevel@tonic-gate     /* Solaris Kerberos: for auditing */
1587c478bd9Sstevel@tonic-gate     in_port_t port; /* local port */
1597c478bd9Sstevel@tonic-gate     union {
1607c478bd9Sstevel@tonic-gate 	/* Type-specific information.  */
1617c478bd9Sstevel@tonic-gate 	struct {
1627c478bd9Sstevel@tonic-gate 	    int x;
1637c478bd9Sstevel@tonic-gate 	} udp;
1647c478bd9Sstevel@tonic-gate 	struct {
1657c478bd9Sstevel@tonic-gate 	    int x;
1667c478bd9Sstevel@tonic-gate 	} tcp_listener;
1677c478bd9Sstevel@tonic-gate 	struct {
1687c478bd9Sstevel@tonic-gate 	    /* connection */
1697c478bd9Sstevel@tonic-gate 	    struct sockaddr_storage addr_s;
1707c478bd9Sstevel@tonic-gate 	    socklen_t addrlen;
1717c478bd9Sstevel@tonic-gate 	    char addrbuf[56];
1727c478bd9Sstevel@tonic-gate 	    krb5_fulladdr faddr;
1737c478bd9Sstevel@tonic-gate 	    krb5_address kaddr;
1747c478bd9Sstevel@tonic-gate 	    /* incoming */
1757c478bd9Sstevel@tonic-gate 	    size_t bufsiz;
1767c478bd9Sstevel@tonic-gate 	    size_t offset;
1777c478bd9Sstevel@tonic-gate 	    char *buffer;
1787c478bd9Sstevel@tonic-gate 	    size_t msglen;
1797c478bd9Sstevel@tonic-gate 	    /* outgoing */
1807c478bd9Sstevel@tonic-gate 	    krb5_data *response;
1817c478bd9Sstevel@tonic-gate 	    unsigned char lenbuf[4];
1827c478bd9Sstevel@tonic-gate 	    sg_buf sgbuf[2];
1837c478bd9Sstevel@tonic-gate 	    sg_buf *sgp;
1847c478bd9Sstevel@tonic-gate 	    int sgnum;
1857c478bd9Sstevel@tonic-gate 	    /* crude denial-of-service avoidance support */
1867c478bd9Sstevel@tonic-gate 	    time_t start_time;
1877c478bd9Sstevel@tonic-gate 	} tcp;
1887c478bd9Sstevel@tonic-gate     } u;
1897c478bd9Sstevel@tonic-gate };
1907c478bd9Sstevel@tonic-gate 
19156a424ccSmp 
1927c478bd9Sstevel@tonic-gate #define SET(TYPE) struct { TYPE *data; int n, max; }
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate /* Start at the top and work down -- this should allow for deletions
1957c478bd9Sstevel@tonic-gate    without disrupting the iteration, since we delete by overwriting
1967c478bd9Sstevel@tonic-gate    the element to be removed with the last element.  */
1977c478bd9Sstevel@tonic-gate #define FOREACH_ELT(set,idx,vvar) \
1987c478bd9Sstevel@tonic-gate   for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate #define GROW_SET(set, incr, tmpptr) \
2017c478bd9Sstevel@tonic-gate   (((int)(set.max + incr) < set.max					\
2027c478bd9Sstevel@tonic-gate     || (((size_t)((int)(set.max + incr) * sizeof(set.data[0]))		\
2037c478bd9Sstevel@tonic-gate 	 / sizeof(set.data[0]))						\
2047c478bd9Sstevel@tonic-gate 	!= (set.max + incr)))						\
2057c478bd9Sstevel@tonic-gate    ? 0				/* overflow */				\
2067c478bd9Sstevel@tonic-gate    : ((tmpptr = realloc(set.data,					\
2077c478bd9Sstevel@tonic-gate 			(int)(set.max + incr) * sizeof(set.data[0])))	\
2087c478bd9Sstevel@tonic-gate       ? (set.data = tmpptr, set.max += incr, 1)				\
2097c478bd9Sstevel@tonic-gate       : 0))
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate /* 1 = success, 0 = failure */
2127c478bd9Sstevel@tonic-gate #define ADD(set, val, tmpptr) \
2137c478bd9Sstevel@tonic-gate   ((set.n < set.max || GROW_SET(set, 10, tmpptr))			\
2147c478bd9Sstevel@tonic-gate    ? (set.data[set.n++] = val, 1)					\
2157c478bd9Sstevel@tonic-gate    : 0)
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate #define DEL(set, idx) \
21844d74681SToomas Soome   (set.data[idx] = set.data[--set.n])
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate #define FREE_SET_DATA(set) if(set.data) free(set.data);                 \
2217c478bd9Sstevel@tonic-gate    (set.data = 0, set.max = 0)
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate /* Set<struct connection *> connections; */
2257c478bd9Sstevel@tonic-gate static SET(struct connection *) connections;
2267c478bd9Sstevel@tonic-gate #define n_sockets	connections.n
2277c478bd9Sstevel@tonic-gate #define conns		connections.data
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /* Set<u_short> udp_port_data, tcp_port_data; */
2307c478bd9Sstevel@tonic-gate static SET(u_short) udp_port_data, tcp_port_data;
2317c478bd9Sstevel@tonic-gate 
232159d09a2SMark Phalan #include "cm.h"
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate static struct select_state sstate;
2357c478bd9Sstevel@tonic-gate 
add_udp_port(int port)2367c478bd9Sstevel@tonic-gate static krb5_error_code add_udp_port(int port)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate     int	i;
2397c478bd9Sstevel@tonic-gate     void *tmp;
2407c478bd9Sstevel@tonic-gate     u_short val;
2417c478bd9Sstevel@tonic-gate     u_short s_port = port;
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate     if (s_port != port)
2447c478bd9Sstevel@tonic-gate 	return EINVAL;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate     FOREACH_ELT (udp_port_data, i, val)
2477c478bd9Sstevel@tonic-gate 	if (s_port == val)
2487c478bd9Sstevel@tonic-gate 	    return 0;
2497c478bd9Sstevel@tonic-gate     if (!ADD(udp_port_data, s_port, tmp))
2507c478bd9Sstevel@tonic-gate 	return ENOMEM;
2517c478bd9Sstevel@tonic-gate     return 0;
2527c478bd9Sstevel@tonic-gate }
2537c478bd9Sstevel@tonic-gate 
add_tcp_port(int port)2547c478bd9Sstevel@tonic-gate static krb5_error_code add_tcp_port(int port)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate     int	i;
2577c478bd9Sstevel@tonic-gate     void *tmp;
2587c478bd9Sstevel@tonic-gate     u_short val;
2597c478bd9Sstevel@tonic-gate     u_short s_port = port;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate     if (s_port != port)
2627c478bd9Sstevel@tonic-gate 	return EINVAL;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate     FOREACH_ELT (tcp_port_data, i, val)
2657c478bd9Sstevel@tonic-gate 	if (s_port == val)
2667c478bd9Sstevel@tonic-gate 	    return 0;
2677c478bd9Sstevel@tonic-gate     if (!ADD(tcp_port_data, s_port, tmp))
2687c478bd9Sstevel@tonic-gate 	return ENOMEM;
2697c478bd9Sstevel@tonic-gate     return 0;
2707c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate 
27256a424ccSmp 
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
27756a424ccSmp #include "foreachaddr.h"
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate struct socksetup {
2807c478bd9Sstevel@tonic-gate     const char *prog;
2817c478bd9Sstevel@tonic-gate     krb5_error_code retval;
2827c478bd9Sstevel@tonic-gate };
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate static struct connection *
add_fd(struct socksetup * data,int sock,enum kdc_conn_type conntype,void (* service)(struct connection *,const char *,int))28556a424ccSmp add_fd (struct socksetup *data, int sock, enum kdc_conn_type conntype,
2867c478bd9Sstevel@tonic-gate 	void (*service)(struct connection *, const char *, int))
2877c478bd9Sstevel@tonic-gate {
2887c478bd9Sstevel@tonic-gate     struct connection *newconn;
2897c478bd9Sstevel@tonic-gate     void *tmp;
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate     newconn = malloc(sizeof(*newconn));
2927c478bd9Sstevel@tonic-gate     if (newconn == 0) {
2937c478bd9Sstevel@tonic-gate 	data->retval = errno;
2947c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
2957c478bd9Sstevel@tonic-gate 		gettext("cannot allocate storage for connection info"));
2967c478bd9Sstevel@tonic-gate 	return 0;
2977c478bd9Sstevel@tonic-gate     }
2987c478bd9Sstevel@tonic-gate     if (!ADD(connections, newconn, tmp)) {
2997c478bd9Sstevel@tonic-gate 	data->retval = errno;
3007c478bd9Sstevel@tonic-gate 	com_err(data->prog, data->retval, gettext("cannot save socket info"));
3017c478bd9Sstevel@tonic-gate 	free(newconn);
3027c478bd9Sstevel@tonic-gate 	return 0;
3037c478bd9Sstevel@tonic-gate     }
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate     memset(newconn, 0, sizeof(*newconn));
3067c478bd9Sstevel@tonic-gate     newconn->type = conntype;
3077c478bd9Sstevel@tonic-gate     newconn->fd = sock;
3087c478bd9Sstevel@tonic-gate     newconn->service = service;
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 *
add_udp_fd(struct socksetup * data,int sock)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 *
add_tcp_listener_fd(struct socksetup * data,int sock)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 *
add_tcp_data_fd(struct socksetup * data,int sock)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
delete_fd(struct connection * xconn)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);
34356a424ccSmp 	    break;
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate     free(xconn);
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate static int
setnbio(int sock)3497c478bd9Sstevel@tonic-gate setnbio(int sock)
3507c478bd9Sstevel@tonic-gate {
3517c478bd9Sstevel@tonic-gate     static const int one = 1;
3527c478bd9Sstevel@tonic-gate     return ioctlsocket(sock, FIONBIO, (const void *)&one);
3537c478bd9Sstevel@tonic-gate }
35456a424ccSmp 
3557c478bd9Sstevel@tonic-gate static int
setnolinger(int s)3567c478bd9Sstevel@tonic-gate setnolinger(int s)
3577c478bd9Sstevel@tonic-gate {
3587c478bd9Sstevel@tonic-gate     static const struct linger ling = { 0, 0 };
3597c478bd9Sstevel@tonic-gate     return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
3607c478bd9Sstevel@tonic-gate }
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate /* Returns -1 or socket fd.  */
3637c478bd9Sstevel@tonic-gate static int
setup_a_tcp_listener(struct socksetup * data,struct sockaddr * addr)3647c478bd9Sstevel@tonic-gate setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr)
3657c478bd9Sstevel@tonic-gate {
3667c478bd9Sstevel@tonic-gate     int sock;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate     sock = socket(addr->sa_family, SOCK_STREAM, 0);
3697c478bd9Sstevel@tonic-gate     if (sock == -1) {
370*55fea89dSDan Cross 	com_err(data->prog, errno,
3717c478bd9Sstevel@tonic-gate 		gettext("Cannot create TCP server socket on %s"),
3727c478bd9Sstevel@tonic-gate 		paddr(addr));
3737c478bd9Sstevel@tonic-gate 	return -1;
3747c478bd9Sstevel@tonic-gate     }
3757c478bd9Sstevel@tonic-gate     /*
3767c478bd9Sstevel@tonic-gate      * Solaris Kerberos: noticed that there where bind problems for tcp sockets
3777c478bd9Sstevel@tonic-gate      * if kdc restarted quickly.  Setting SO_REUSEADDR allowed binds to succeed.
3787c478bd9Sstevel@tonic-gate      */
3797c478bd9Sstevel@tonic-gate     if (setreuseaddr(sock, 1) < 0) {
3807c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3817c478bd9Sstevel@tonic-gate 		gettext("enabling SO_REUSEADDR on TCP socket"));
3827c478bd9Sstevel@tonic-gate 	close(sock);
3837c478bd9Sstevel@tonic-gate 	return -1;
3847c478bd9Sstevel@tonic-gate     }
3857c478bd9Sstevel@tonic-gate     if (bind(sock, addr, socklen(addr)) == -1) {
3867c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
3877c478bd9Sstevel@tonic-gate 		gettext("Cannot bind TCP server socket on %s"), paddr(addr));
3887c478bd9Sstevel@tonic-gate 	close(sock);
3897c478bd9Sstevel@tonic-gate 	return -1;
3907c478bd9Sstevel@tonic-gate     }
3917c478bd9Sstevel@tonic-gate     if (listen(sock, 5) < 0) {
392*55fea89dSDan Cross 	com_err(data->prog, errno,
3937c478bd9Sstevel@tonic-gate 		gettext("Cannot listen on TCP server socket on %s"),
3947c478bd9Sstevel@tonic-gate 		paddr(addr));
3957c478bd9Sstevel@tonic-gate 	close(sock);
3967c478bd9Sstevel@tonic-gate 	return -1;
3977c478bd9Sstevel@tonic-gate     }
3987c478bd9Sstevel@tonic-gate     if (setnbio(sock)) {
3997c478bd9Sstevel@tonic-gate 	com_err(data->prog, errno,
4007c478bd9Sstevel@tonic-gate 		gettext("cannot set listening tcp socket on %s non-blocking"),
4017c478bd9Sstevel@tonic-gate 		paddr(addr));
4027c478bd9Sstevel@tonic-gate 	close(sock);
4037c478bd9Sstevel@tonic-gate 	return -1;
4047c478bd9Sstevel@tonic-gate     }
4057c478bd9Sstevel@tonic-gate     if (setnolinger(sock)) {
406*55fea89dSDan Cross 	com_err(data->prog, errno,
4077c478bd9Sstevel@tonic-gate 		gettext("disabling SO_LINGER on TCP socket on %s"),
4087c478bd9Sstevel@tonic-gate 		paddr(addr));
4097c478bd9Sstevel@tonic-gate 	close(sock);
4107c478bd9Sstevel@tonic-gate 	return -1;
4117c478bd9Sstevel@tonic-gate     }
4127c478bd9Sstevel@tonic-gate     return sock;
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate static int
setup_tcp_listener_ports(struct socksetup * data)4167c478bd9Sstevel@tonic-gate setup_tcp_listener_ports(struct socksetup *data)
4177c478bd9Sstevel@tonic-gate {
4187c478bd9Sstevel@tonic-gate     struct sockaddr_in sin4;
4197c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4207c478bd9Sstevel@tonic-gate     struct sockaddr_in6 sin6;
4217c478bd9Sstevel@tonic-gate #endif
4227c478bd9Sstevel@tonic-gate     int i, port;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate     memset(&sin4, 0, sizeof(sin4));
4257c478bd9Sstevel@tonic-gate     sin4.sin_family = AF_INET;
4267c478bd9Sstevel@tonic-gate #ifdef HAVE_SA_LEN
4277c478bd9Sstevel@tonic-gate     sin4.sin_len = sizeof(sin4);
4287c478bd9Sstevel@tonic-gate #endif
4297c478bd9Sstevel@tonic-gate     sin4.sin_addr.s_addr = INADDR_ANY;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4327c478bd9Sstevel@tonic-gate     memset(&sin6, 0, sizeof(sin6));
4337c478bd9Sstevel@tonic-gate     sin6.sin6_family = AF_INET6;
4347c478bd9Sstevel@tonic-gate #ifdef SIN6_LEN
4357c478bd9Sstevel@tonic-gate     sin6.sin6_len = sizeof(sin6);
4367c478bd9Sstevel@tonic-gate #endif
4377c478bd9Sstevel@tonic-gate     sin6.sin6_addr = in6addr_any;
4387c478bd9Sstevel@tonic-gate #endif
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate     FOREACH_ELT (tcp_port_data, i, port) {
4417c478bd9Sstevel@tonic-gate 	int s4, s6;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	set_sa_port((struct sockaddr *)&sin4, htons(port));
4447c478bd9Sstevel@tonic-gate 	if (!ipv6_enabled()) {
4457c478bd9Sstevel@tonic-gate 	    s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
4467c478bd9Sstevel@tonic-gate 	    if (s4 < 0)
4477c478bd9Sstevel@tonic-gate 		return -1;
4487c478bd9Sstevel@tonic-gate 	    s6 = -1;
4497c478bd9Sstevel@tonic-gate 	} else {
4507c478bd9Sstevel@tonic-gate #ifndef KRB5_USE_INET6
4517c478bd9Sstevel@tonic-gate 	    abort();
4527c478bd9Sstevel@tonic-gate #else
4537c478bd9Sstevel@tonic-gate 	    s4 = s6 = -1;
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	    set_sa_port((struct sockaddr *)&sin6, htons(port));
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	    s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6);
4587c478bd9Sstevel@tonic-gate 	    if (s6 < 0)
4597c478bd9Sstevel@tonic-gate 		return -1;
4607c478bd9Sstevel@tonic-gate #ifdef IPV6_V6ONLY
4617c478bd9Sstevel@tonic-gate 	    if (setv6only(s6, 0))
4627c478bd9Sstevel@tonic-gate 		com_err(data->prog, errno,
4637c478bd9Sstevel@tonic-gate 		       	gettext("setsockopt(IPV6_V6ONLY,0) failed"));
4647c478bd9Sstevel@tonic-gate #endif
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	    s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
4677c478bd9Sstevel@tonic-gate #endif /* KRB5_USE_INET6 */
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 	/* Sockets are created, prepare to listen on them.  */
4717c478bd9Sstevel@tonic-gate 	if (s4 >= 0) {
4727c478bd9Sstevel@tonic-gate 	    FD_SET(s4, &sstate.rfds);
4737c478bd9Sstevel@tonic-gate 	    if (s4 >= sstate.max)
4747c478bd9Sstevel@tonic-gate 		sstate.max = s4 + 1;
4757c478bd9Sstevel@tonic-gate 	    if (add_tcp_listener_fd(data, s4) == 0)
4767c478bd9Sstevel@tonic-gate 		close(s4);
4777c478bd9Sstevel@tonic-gate 	    else
47856a424ccSmp 		krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
47956a424ccSmp 				 s4, paddr((struct sockaddr *)&sin4));
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
4827c478bd9Sstevel@tonic-gate 	if (s6 >= 0) {
4837c478bd9Sstevel@tonic-gate 	    FD_SET(s6, &sstate.rfds);
4847c478bd9Sstevel@tonic-gate 	    if (s6 >= sstate.max)
4857c478bd9Sstevel@tonic-gate 		sstate.max = s6 + 1;
4867c478bd9Sstevel@tonic-gate 	    if (add_tcp_listener_fd(data, s6) == 0) {
4877c478bd9Sstevel@tonic-gate 		close(s6);
4887c478bd9Sstevel@tonic-gate 		s6 = -1;
4897c478bd9Sstevel@tonic-gate 	    } else
49056a424ccSmp 		krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
49156a424ccSmp 				 s6, paddr((struct sockaddr *)&sin6));
4927c478bd9Sstevel@tonic-gate 	    if (s4 < 0)
4937c478bd9Sstevel@tonic-gate 		krb5_klog_syslog(LOG_INFO,
4947c478bd9Sstevel@tonic-gate 				 "assuming IPv6 socket accepts IPv4");
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate #endif
4977c478bd9Sstevel@tonic-gate     }
4987c478bd9Sstevel@tonic-gate     return 0;
4997c478bd9Sstevel@tonic-gate }
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate static int
setup_udp_port(void * P_data,struct sockaddr * addr)5027c478bd9Sstevel@tonic-gate setup_udp_port(void *P_data, struct sockaddr *addr)
5037c478bd9Sstevel@tonic-gate {
5047c478bd9Sstevel@tonic-gate     struct socksetup *data = P_data;
5057c478bd9Sstevel@tonic-gate     int sock = -1, i;
5067c478bd9Sstevel@tonic-gate     char haddrbuf[NI_MAXHOST];
5077c478bd9Sstevel@tonic-gate     int err;
5087c478bd9Sstevel@tonic-gate     u_short port;
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate     err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf),
5117c478bd9Sstevel@tonic-gate 		      0, 0, NI_NUMERICHOST);
5127c478bd9Sstevel@tonic-gate     if (err)
5137c478bd9Sstevel@tonic-gate 	strcpy(haddrbuf, "<unprintable>");
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate     switch (addr->sa_family) {
5167c478bd9Sstevel@tonic-gate     case AF_INET:
5177c478bd9Sstevel@tonic-gate 	break;
5187c478bd9Sstevel@tonic-gate #ifdef AF_INET6
5197c478bd9Sstevel@tonic-gate     case AF_INET6:
5207c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
5217c478bd9Sstevel@tonic-gate 	break;
5227c478bd9Sstevel@tonic-gate #else
5237c478bd9Sstevel@tonic-gate 	{
5247c478bd9Sstevel@tonic-gate 	    static int first = 1;
5257c478bd9Sstevel@tonic-gate 	    if (first) {
5267c478bd9Sstevel@tonic-gate 		krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses");
5277c478bd9Sstevel@tonic-gate 		first = 0;
5287c478bd9Sstevel@tonic-gate 	    }
5297c478bd9Sstevel@tonic-gate 	    return 0;
5307c478bd9Sstevel@tonic-gate 	}
5317c478bd9Sstevel@tonic-gate #endif
5327c478bd9Sstevel@tonic-gate #endif
5337c478bd9Sstevel@tonic-gate #ifdef AF_LINK /* some BSD systems, AIX */
5347c478bd9Sstevel@tonic-gate     case AF_LINK:
5357c478bd9Sstevel@tonic-gate 	return 0;
53656a424ccSmp #endif
53756a424ccSmp #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */
53856a424ccSmp     case AF_DLI:
53956a424ccSmp 	return 0;
5407c478bd9Sstevel@tonic-gate #endif
5417c478bd9Sstevel@tonic-gate     default:
5427c478bd9Sstevel@tonic-gate 	krb5_klog_syslog (LOG_INFO,
5437c478bd9Sstevel@tonic-gate 			  "skipping unrecognized local address family %d",
5447c478bd9Sstevel@tonic-gate 			  addr->sa_family);
5457c478bd9Sstevel@tonic-gate 	return 0;
5467c478bd9Sstevel@tonic-gate     }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate     FOREACH_ELT (udp_port_data, i, port) {
5497c478bd9Sstevel@tonic-gate 	sock = socket (addr->sa_family, SOCK_DGRAM, 0);
5507c478bd9Sstevel@tonic-gate 	if (sock == -1) {
5517c478bd9Sstevel@tonic-gate 	    data->retval = errno;
5527c478bd9Sstevel@tonic-gate 	    com_err(data->prog, data->retval,
5537c478bd9Sstevel@tonic-gate 		    gettext("Cannot create server socket for port %d address %s"),
5547c478bd9Sstevel@tonic-gate 		    port, haddrbuf);
5557c478bd9Sstevel@tonic-gate 	    return 1;
5567c478bd9Sstevel@tonic-gate 	}
5577c478bd9Sstevel@tonic-gate 	set_sa_port(addr, htons(port));
5587c478bd9Sstevel@tonic-gate 	if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) {
5597c478bd9Sstevel@tonic-gate 	    data->retval = errno;
5607c478bd9Sstevel@tonic-gate 	    com_err(data->prog, data->retval,
5617c478bd9Sstevel@tonic-gate 		    gettext("Cannot bind server socket to port %d address %s"),
5627c478bd9Sstevel@tonic-gate 		    port, haddrbuf);
5637c478bd9Sstevel@tonic-gate 	    return 1;
5647c478bd9Sstevel@tonic-gate 	}
5657c478bd9Sstevel@tonic-gate 	FD_SET (sock, &sstate.rfds);
5667c478bd9Sstevel@tonic-gate 	if (sock >= sstate.max)
5677c478bd9Sstevel@tonic-gate 	    sstate.max = sock + 1;
56856a424ccSmp 	krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s", sock,
56956a424ccSmp 			  paddr((struct sockaddr *)addr));
5707c478bd9Sstevel@tonic-gate 	if (add_udp_fd (data, sock) == 0)
5717c478bd9Sstevel@tonic-gate 	    return 1;
5727c478bd9Sstevel@tonic-gate     }
5737c478bd9Sstevel@tonic-gate     return 0;
5747c478bd9Sstevel@tonic-gate }
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate #if 1
klog_handler(const void * data,size_t len)5777c478bd9Sstevel@tonic-gate static void klog_handler(const void *data, size_t len)
5787c478bd9Sstevel@tonic-gate {
5797c478bd9Sstevel@tonic-gate     static char buf[BUFSIZ];
5807c478bd9Sstevel@tonic-gate     static int bufoffset;
5817c478bd9Sstevel@tonic-gate     void *p;
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate #define flush_buf() \
5847c478bd9Sstevel@tonic-gate   (bufoffset						\
5857c478bd9Sstevel@tonic-gate    ? (((buf[0] == 0 || buf[0] == '\n')			\
5867c478bd9Sstevel@tonic-gate        ? (fork()==0?abort():(void)0)			\
5877c478bd9Sstevel@tonic-gate        : (void)0),					\
5887c478bd9Sstevel@tonic-gate       krb5_klog_syslog(LOG_INFO, "%s", buf),		\
5897c478bd9Sstevel@tonic-gate       memset(buf, 0, sizeof(buf)),			\
5907c478bd9Sstevel@tonic-gate       bufoffset = 0)					\
5917c478bd9Sstevel@tonic-gate    : 0)
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate     p = memchr(data, 0, len);
5947c478bd9Sstevel@tonic-gate     if (p)
5957c478bd9Sstevel@tonic-gate 	len = (const char *)p - (const char *)data;
5967c478bd9Sstevel@tonic-gate scan_for_newlines:
5977c478bd9Sstevel@tonic-gate     if (len == 0)
5987c478bd9Sstevel@tonic-gate 	return;
5997c478bd9Sstevel@tonic-gate     p = memchr(data, '\n', len);
6007c478bd9Sstevel@tonic-gate     if (p) {
6017c478bd9Sstevel@tonic-gate 	if (p != data)
6027c478bd9Sstevel@tonic-gate 	    klog_handler(data, (size_t)((const char *)p - (const char *)data));
6037c478bd9Sstevel@tonic-gate 	flush_buf();
6047c478bd9Sstevel@tonic-gate 	len -= ((const char *)p - (const char *)data) + 1;
6057c478bd9Sstevel@tonic-gate 	data = 1 + (const char *)p;
6067c478bd9Sstevel@tonic-gate 	goto scan_for_newlines;
6077c478bd9Sstevel@tonic-gate     } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) {
6087c478bd9Sstevel@tonic-gate 	size_t x = sizeof(buf) - len - 1;
6097c478bd9Sstevel@tonic-gate 	klog_handler(data, x);
6107c478bd9Sstevel@tonic-gate 	flush_buf();
6117c478bd9Sstevel@tonic-gate 	len -= x;
6127c478bd9Sstevel@tonic-gate 	data = (const char *)data + x;
6137c478bd9Sstevel@tonic-gate 	goto scan_for_newlines;
6147c478bd9Sstevel@tonic-gate     } else {
6157c478bd9Sstevel@tonic-gate 	memcpy(buf + bufoffset, data, len);
6167c478bd9Sstevel@tonic-gate 	bufoffset += len;
6177c478bd9Sstevel@tonic-gate     }
6187c478bd9Sstevel@tonic-gate }
6197c478bd9Sstevel@tonic-gate #endif
6207c478bd9Sstevel@tonic-gate 
62156a424ccSmp /* XXX */
62256a424ccSmp extern int krb5int_debug_sendto_kdc;
6237c478bd9Sstevel@tonic-gate extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate krb5_error_code
setup_network(const char * prog)6267c478bd9Sstevel@tonic-gate setup_network(const char *prog)
6277c478bd9Sstevel@tonic-gate {
6287c478bd9Sstevel@tonic-gate     struct socksetup setup_data;
6297c478bd9Sstevel@tonic-gate     krb5_error_code retval;
6307c478bd9Sstevel@tonic-gate     char *cp;
6317c478bd9Sstevel@tonic-gate     int i, port;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.rfds);
6347c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.wfds);
6357c478bd9Sstevel@tonic-gate     FD_ZERO(&sstate.xfds);
6367c478bd9Sstevel@tonic-gate     sstate.max = 0;
6377c478bd9Sstevel@tonic-gate 
63856a424ccSmp /*    krb5int_debug_sendto_kdc = 1; */
6397c478bd9Sstevel@tonic-gate     krb5int_sendtokdc_debug_handler = klog_handler;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate     /* Handle each realm's ports */
6427c478bd9Sstevel@tonic-gate     for (i=0; i<kdc_numrealms; i++) {
6437c478bd9Sstevel@tonic-gate 	cp = kdc_realmlist[i]->realm_ports;
6447c478bd9Sstevel@tonic-gate 	while (cp && *cp) {
6457c478bd9Sstevel@tonic-gate 	    if (*cp == ',' || isspace((int) *cp)) {
6467c478bd9Sstevel@tonic-gate 		cp++;
6477c478bd9Sstevel@tonic-gate 		continue;
6487c478bd9Sstevel@tonic-gate 	    }
6497c478bd9Sstevel@tonic-gate 	    port = strtol(cp, &cp, 10);
6507c478bd9Sstevel@tonic-gate 	    if (cp == 0)
6517c478bd9Sstevel@tonic-gate 		break;
6527c478bd9Sstevel@tonic-gate 	    retval = add_udp_port(port);
6537c478bd9Sstevel@tonic-gate 	    if (retval)
6547c478bd9Sstevel@tonic-gate 		return retval;
6557c478bd9Sstevel@tonic-gate 	}
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	cp = kdc_realmlist[i]->realm_tcp_ports;
6587c478bd9Sstevel@tonic-gate 	while (cp && *cp) {
6597c478bd9Sstevel@tonic-gate 	    if (*cp == ',' || isspace((int) *cp)) {
6607c478bd9Sstevel@tonic-gate 		cp++;
6617c478bd9Sstevel@tonic-gate 		continue;
6627c478bd9Sstevel@tonic-gate 	    }
6637c478bd9Sstevel@tonic-gate 	    port = strtol(cp, &cp, 10);
6647c478bd9Sstevel@tonic-gate 	    if (cp == 0)
6657c478bd9Sstevel@tonic-gate 		break;
6667c478bd9Sstevel@tonic-gate 	    retval = add_tcp_port(port);
6677c478bd9Sstevel@tonic-gate 	    if (retval)
6687c478bd9Sstevel@tonic-gate 		return retval;
6697c478bd9Sstevel@tonic-gate 	}
6707c478bd9Sstevel@tonic-gate     }
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate     setup_data.prog = prog;
6737c478bd9Sstevel@tonic-gate     setup_data.retval = 0;
6747c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_INFO, "setting up network...");
6757c478bd9Sstevel@tonic-gate     /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
6767c478bd9Sstevel@tonic-gate        so we might need only one UDP socket; fall back to binding
6777c478bd9Sstevel@tonic-gate        sockets on each address only if IPV6_PKTINFO isn't
6787c478bd9Sstevel@tonic-gate        supported.  */
6797c478bd9Sstevel@tonic-gate     if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) {
6807c478bd9Sstevel@tonic-gate 	return setup_data.retval;
6817c478bd9Sstevel@tonic-gate     }
6827c478bd9Sstevel@tonic-gate     setup_tcp_listener_ports(&setup_data);
6837c478bd9Sstevel@tonic-gate     krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
6847c478bd9Sstevel@tonic-gate     if (n_sockets == 0) {
6857c478bd9Sstevel@tonic-gate 	com_err(prog, 0, gettext("no sockets set up?"));
6867c478bd9Sstevel@tonic-gate 	exit (1);
6877c478bd9Sstevel@tonic-gate     }
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate     return 0;
6907c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate 
init_addr(krb5_fulladdr * faddr,struct sockaddr * sa)6927c478bd9Sstevel@tonic-gate static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
6937c478bd9Sstevel@tonic-gate {
6947c478bd9Sstevel@tonic-gate     switch (sa->sa_family) {
6957c478bd9Sstevel@tonic-gate     case AF_INET:
6967c478bd9Sstevel@tonic-gate 	faddr->address->addrtype = ADDRTYPE_INET;
6977c478bd9Sstevel@tonic-gate 	faddr->address->length = IPV4_ADDR_LEN;
6987c478bd9Sstevel@tonic-gate 	faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr;
6997c478bd9Sstevel@tonic-gate 	faddr->port = ntohs(sa2sin(sa)->sin_port);
7007c478bd9Sstevel@tonic-gate 	break;
7017c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
7027c478bd9Sstevel@tonic-gate     case AF_INET6:
7037c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) {
7047c478bd9Sstevel@tonic-gate 	    faddr->address->addrtype = ADDRTYPE_INET;
7057c478bd9Sstevel@tonic-gate 	    faddr->address->length = IPV4_ADDR_LEN;
7067c478bd9Sstevel@tonic-gate 	    /* offset to RAM address of ipv4 part of ipv6 address */
7077c478bd9Sstevel@tonic-gate 	    faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) +
7087c478bd9Sstevel@tonic-gate 		(krb5_octet *) &sa2sin6(sa)->sin6_addr;
7097c478bd9Sstevel@tonic-gate 	} else {
7107c478bd9Sstevel@tonic-gate 	    faddr->address->addrtype = ADDRTYPE_INET6;
7117c478bd9Sstevel@tonic-gate 	    faddr->address->length = IPV6_ADDR_LEN;
7127c478bd9Sstevel@tonic-gate 	    faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr;
7137c478bd9Sstevel@tonic-gate 	}
7147c478bd9Sstevel@tonic-gate 	faddr->port = ntohs(sa2sin6(sa)->sin6_port);
7157c478bd9Sstevel@tonic-gate 	break;
7167c478bd9Sstevel@tonic-gate #endif
7177c478bd9Sstevel@tonic-gate     default:
7187c478bd9Sstevel@tonic-gate 	faddr->address->addrtype = -1;
7197c478bd9Sstevel@tonic-gate 	faddr->address->length = 0;
7207c478bd9Sstevel@tonic-gate 	faddr->address->contents = 0;
7217c478bd9Sstevel@tonic-gate 	faddr->port = 0;
7227c478bd9Sstevel@tonic-gate 	break;
7237c478bd9Sstevel@tonic-gate     }
7247c478bd9Sstevel@tonic-gate }
7257c478bd9Sstevel@tonic-gate 
process_packet(struct connection * conn,const char * prog,int selflags)7267c478bd9Sstevel@tonic-gate static void process_packet(struct connection *conn, const char *prog,
7277c478bd9Sstevel@tonic-gate 			   int selflags)
7287c478bd9Sstevel@tonic-gate {
7297c478bd9Sstevel@tonic-gate     int cc;
7307c478bd9Sstevel@tonic-gate     socklen_t saddr_len;
7317c478bd9Sstevel@tonic-gate     krb5_fulladdr faddr;
7327c478bd9Sstevel@tonic-gate     krb5_error_code retval;
7337c478bd9Sstevel@tonic-gate     struct sockaddr_storage saddr;
7347c478bd9Sstevel@tonic-gate     krb5_address addr;
7357c478bd9Sstevel@tonic-gate     krb5_data request;
7367c478bd9Sstevel@tonic-gate     krb5_data *response;
7377c478bd9Sstevel@tonic-gate     char pktbuf[MAX_DGRAM_SIZE];
7387c478bd9Sstevel@tonic-gate     int port_fd = conn->fd;
73956a424ccSmp 
7406cf54e34Sbugbomb     response = NULL;
7417c478bd9Sstevel@tonic-gate     saddr_len = sizeof(saddr);
7427c478bd9Sstevel@tonic-gate     cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0,
7437c478bd9Sstevel@tonic-gate 		  (struct sockaddr *)&saddr, &saddr_len);
7447c478bd9Sstevel@tonic-gate     if (cc == -1) {
7457c478bd9Sstevel@tonic-gate 	if (errno != EINTR
7467c478bd9Sstevel@tonic-gate 	    /* This is how Linux indicates that a previous
7477c478bd9Sstevel@tonic-gate 	       transmission was refused, e.g., if the client timed out
7487c478bd9Sstevel@tonic-gate 	       before getting the response packet.  */
7497c478bd9Sstevel@tonic-gate 	    && errno != ECONNREFUSED
7507c478bd9Sstevel@tonic-gate 	    )
7517c478bd9Sstevel@tonic-gate 	    com_err(prog, errno, gettext("while receiving from network"));
7527c478bd9Sstevel@tonic-gate 	return;
7537c478bd9Sstevel@tonic-gate     }
7547c478bd9Sstevel@tonic-gate     if (!cc)
7557c478bd9Sstevel@tonic-gate 	return;		/* zero-length packet? */
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate     request.length = cc;
7587c478bd9Sstevel@tonic-gate     request.data = pktbuf;
7597c478bd9Sstevel@tonic-gate     faddr.address = &addr;
7607c478bd9Sstevel@tonic-gate     init_addr(&faddr, ss2sa(&saddr));
7617c478bd9Sstevel@tonic-gate     /* this address is in net order */
76256a424ccSmp     if ((retval = dispatch(&request, &faddr, &response))) {
7637c478bd9Sstevel@tonic-gate 	com_err(prog, retval, gettext("while dispatching (udp)"));
7647c478bd9Sstevel@tonic-gate 	return;
7657c478bd9Sstevel@tonic-gate     }
7667c478bd9Sstevel@tonic-gate     cc = sendto(port_fd, response->data, (socklen_t) response->length, 0,
7677c478bd9Sstevel@tonic-gate 		(struct sockaddr *)&saddr, saddr_len);
7687c478bd9Sstevel@tonic-gate     if (cc == -1) {
7697c478bd9Sstevel@tonic-gate 	char addrbuf[46];
7707c478bd9Sstevel@tonic-gate         krb5_free_data(kdc_context, response);
7717c478bd9Sstevel@tonic-gate 	if (inet_ntop(((struct sockaddr *)&saddr)->sa_family,
7727c478bd9Sstevel@tonic-gate 		      addr.contents, addrbuf, sizeof(addrbuf)) == 0) {
7737c478bd9Sstevel@tonic-gate 	    strcpy(addrbuf, "?");
7747c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 	com_err(prog, errno, gettext("while sending reply to %s/%d"),
7767c478bd9Sstevel@tonic-gate 		addrbuf, faddr.port);
7777c478bd9Sstevel@tonic-gate 	return;
7787c478bd9Sstevel@tonic-gate     }
7797c478bd9Sstevel@tonic-gate     if (cc != response->length) {
7807c478bd9Sstevel@tonic-gate 	krb5_free_data(kdc_context, response);
7817c478bd9Sstevel@tonic-gate 	com_err(prog, 0, gettext("short reply write %d vs %d\n"),
7827c478bd9Sstevel@tonic-gate 		response->length, cc);
7837c478bd9Sstevel@tonic-gate 	return;
7847c478bd9Sstevel@tonic-gate     }
7857c478bd9Sstevel@tonic-gate     krb5_free_data(kdc_context, response);
7867c478bd9Sstevel@tonic-gate     return;
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate static int tcp_data_counter;
7907c478bd9Sstevel@tonic-gate /* Solaris kerberos: getting this value from elsewhere */
7917c478bd9Sstevel@tonic-gate extern int max_tcp_data_connections;
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate static void kill_tcp_connection(struct connection *);
7947c478bd9Sstevel@tonic-gate 
accept_tcp_connection(struct connection * conn,const char * prog,int selflags)7957c478bd9Sstevel@tonic-gate static void accept_tcp_connection(struct connection *conn, const char *prog,
7967c478bd9Sstevel@tonic-gate 				  int selflags)
7977c478bd9Sstevel@tonic-gate {
7987c478bd9Sstevel@tonic-gate     int s;
7997c478bd9Sstevel@tonic-gate     struct sockaddr_storage addr_s;
8007c478bd9Sstevel@tonic-gate     struct sockaddr *addr = (struct sockaddr *)&addr_s;
8017c478bd9Sstevel@tonic-gate     socklen_t addrlen = sizeof(addr_s);
8027c478bd9Sstevel@tonic-gate     struct socksetup sockdata;
8037c478bd9Sstevel@tonic-gate     struct connection *newconn;
8047c478bd9Sstevel@tonic-gate     char tmpbuf[10];
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate     s = accept(conn->fd, addr, &addrlen);
8077c478bd9Sstevel@tonic-gate     if (s < 0)
8087c478bd9Sstevel@tonic-gate 	return;
8097c478bd9Sstevel@tonic-gate     setnbio(s), setnolinger(s);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate     sockdata.prog = prog;
8127c478bd9Sstevel@tonic-gate     sockdata.retval = 0;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate     newconn = add_tcp_data_fd(&sockdata, s);
8157c478bd9Sstevel@tonic-gate     if (newconn == 0)
8167c478bd9Sstevel@tonic-gate 	return;
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate     if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
8197c478bd9Sstevel@tonic-gate 		    newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
8207c478bd9Sstevel@tonic-gate 		    tmpbuf, sizeof(tmpbuf),
8217c478bd9Sstevel@tonic-gate 		    NI_NUMERICHOST | NI_NUMERICSERV))
8227c478bd9Sstevel@tonic-gate 	strcpy(newconn->u.tcp.addrbuf, "???");
8237c478bd9Sstevel@tonic-gate     else {
8247c478bd9Sstevel@tonic-gate 	char *p, *end;
8257c478bd9Sstevel@tonic-gate 	p = newconn->u.tcp.addrbuf;
8267c478bd9Sstevel@tonic-gate 	end = p + sizeof(newconn->u.tcp.addrbuf);
8277c478bd9Sstevel@tonic-gate 	p += strlen(p);
8287c478bd9Sstevel@tonic-gate 	if (end - p > 2 + strlen(tmpbuf)) {
8297c478bd9Sstevel@tonic-gate 	    *p++ = '.';
8307c478bd9Sstevel@tonic-gate 	    strcpy(p, tmpbuf);
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate     }
83356a424ccSmp #if 0
83456a424ccSmp     krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s",
83556a424ccSmp 		     s, newconn->u.tcp.addrbuf);
83656a424ccSmp #endif
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate     newconn->u.tcp.addr_s = addr_s;
8397c478bd9Sstevel@tonic-gate     newconn->u.tcp.addrlen = addrlen;
8407c478bd9Sstevel@tonic-gate     newconn->u.tcp.bufsiz = 1024 * 1024;
8417c478bd9Sstevel@tonic-gate     newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz);
8427c478bd9Sstevel@tonic-gate     newconn->u.tcp.start_time = time(0);
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate     if (++tcp_data_counter > max_tcp_data_connections) {
8457c478bd9Sstevel@tonic-gate 	struct connection *oldest_tcp = NULL;
8467c478bd9Sstevel@tonic-gate 	struct connection *c;
8477c478bd9Sstevel@tonic-gate 	int i;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	krb5_klog_syslog(LOG_INFO, "too many connections");
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	FOREACH_ELT (connections, i, c) {
8527c478bd9Sstevel@tonic-gate 	    if (c->type != CONN_TCP)
8537c478bd9Sstevel@tonic-gate 		continue;
8547c478bd9Sstevel@tonic-gate 	    if (c == newconn)
8557c478bd9Sstevel@tonic-gate 		continue;
8567c478bd9Sstevel@tonic-gate #if 0
8577c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd,
8587c478bd9Sstevel@tonic-gate 			     c->u.tcp.start_time);
8597c478bd9Sstevel@tonic-gate #endif
8607c478bd9Sstevel@tonic-gate 	    if (oldest_tcp == NULL
8617c478bd9Sstevel@tonic-gate 		|| oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
8627c478bd9Sstevel@tonic-gate 		oldest_tcp = c;
8637c478bd9Sstevel@tonic-gate 	}
8647c478bd9Sstevel@tonic-gate 	if (oldest_tcp != NULL) {
8657c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s",
8667c478bd9Sstevel@tonic-gate 			     oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
8677c478bd9Sstevel@tonic-gate 	    kill_tcp_connection(oldest_tcp);
8687c478bd9Sstevel@tonic-gate 	    oldest_tcp = NULL;
8697c478bd9Sstevel@tonic-gate 	}
8707c478bd9Sstevel@tonic-gate     }
8717c478bd9Sstevel@tonic-gate     if (newconn->u.tcp.buffer == 0) {
8727c478bd9Sstevel@tonic-gate 	com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"),
8737c478bd9Sstevel@tonic-gate 		newconn->u.tcp.addrbuf);
8747c478bd9Sstevel@tonic-gate 	delete_fd(newconn);
8757c478bd9Sstevel@tonic-gate 	close(s);
87656a424ccSmp 	tcp_data_counter--;
8777c478bd9Sstevel@tonic-gate 	return;
8787c478bd9Sstevel@tonic-gate     }
8797c478bd9Sstevel@tonic-gate     newconn->u.tcp.offset = 0;
8807c478bd9Sstevel@tonic-gate     newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
8817c478bd9Sstevel@tonic-gate     init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
8827c478bd9Sstevel@tonic-gate     SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
8837c478bd9Sstevel@tonic-gate     SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate     FD_SET(s, &sstate.rfds);
8867c478bd9Sstevel@tonic-gate     if (sstate.max <= s)
8877c478bd9Sstevel@tonic-gate 	sstate.max = s + 1;
8887c478bd9Sstevel@tonic-gate }
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate static void
kill_tcp_connection(struct connection * conn)8917c478bd9Sstevel@tonic-gate kill_tcp_connection(struct connection *conn)
8927c478bd9Sstevel@tonic-gate {
8937c478bd9Sstevel@tonic-gate     if (conn->u.tcp.response)
8947c478bd9Sstevel@tonic-gate 	krb5_free_data(kdc_context, conn->u.tcp.response);
8957c478bd9Sstevel@tonic-gate     if (conn->u.tcp.buffer)
8967c478bd9Sstevel@tonic-gate 	free(conn->u.tcp.buffer);
8977c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &sstate.rfds);
8987c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &sstate.wfds);
8997c478bd9Sstevel@tonic-gate     if (sstate.max == conn->fd + 1)
9007c478bd9Sstevel@tonic-gate 	while (sstate.max > 0
9017c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(sstate.max-1, &sstate.rfds)
9027c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(sstate.max-1, &sstate.wfds)
9037c478bd9Sstevel@tonic-gate 	       /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
9047c478bd9Sstevel@tonic-gate 	    )
9057c478bd9Sstevel@tonic-gate 	    sstate.max--;
9067c478bd9Sstevel@tonic-gate     close(conn->fd);
9077c478bd9Sstevel@tonic-gate     conn->fd = -1;
9087c478bd9Sstevel@tonic-gate     delete_fd(conn);
90956a424ccSmp     tcp_data_counter--;
9107c478bd9Sstevel@tonic-gate }
9117c478bd9Sstevel@tonic-gate 
912159d09a2SMark Phalan static krb5_error_code
make_toolong_error(krb5_data ** out)913159d09a2SMark Phalan make_toolong_error (krb5_data **out)
914159d09a2SMark Phalan {
915159d09a2SMark Phalan     krb5_error errpkt;
916159d09a2SMark Phalan     krb5_error_code retval;
917159d09a2SMark Phalan     krb5_data *scratch;
918159d09a2SMark Phalan 
919159d09a2SMark Phalan     retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
920159d09a2SMark Phalan     if (retval)
921159d09a2SMark Phalan 	return retval;
922159d09a2SMark Phalan     errpkt.error = KRB_ERR_FIELD_TOOLONG;
923159d09a2SMark Phalan     errpkt.server = tgs_server;
924159d09a2SMark Phalan     errpkt.client = NULL;
925159d09a2SMark Phalan     errpkt.cusec = 0;
926159d09a2SMark Phalan     errpkt.ctime = 0;
927159d09a2SMark Phalan     errpkt.text.length = 0;
928159d09a2SMark Phalan     errpkt.text.data = 0;
929159d09a2SMark Phalan     errpkt.e_data.length = 0;
930159d09a2SMark Phalan     errpkt.e_data.data = 0;
931159d09a2SMark Phalan     scratch = malloc(sizeof(*scratch));
932159d09a2SMark Phalan     if (scratch == NULL)
933159d09a2SMark Phalan 	return ENOMEM;
934159d09a2SMark Phalan     retval = krb5_mk_error(kdc_context, &errpkt, scratch);
935159d09a2SMark Phalan     if (retval) {
936159d09a2SMark Phalan 	free(scratch);
937159d09a2SMark Phalan 	return retval;
938159d09a2SMark Phalan     }
939159d09a2SMark Phalan 
940159d09a2SMark Phalan     *out = scratch;
941159d09a2SMark Phalan     return 0;
942159d09a2SMark Phalan }
943159d09a2SMark Phalan 
9447c478bd9Sstevel@tonic-gate static void
process_tcp_connection(struct connection * conn,const char * prog,int selflags)9457c478bd9Sstevel@tonic-gate process_tcp_connection(struct connection *conn, const char *prog, int selflags)
9467c478bd9Sstevel@tonic-gate {
9477c478bd9Sstevel@tonic-gate     if (selflags & SSF_WRITE) {
9487c478bd9Sstevel@tonic-gate 	ssize_t nwrote;
9497c478bd9Sstevel@tonic-gate 	SOCKET_WRITEV_TEMP tmp;
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 	nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
9527c478bd9Sstevel@tonic-gate 			       tmp);
9537c478bd9Sstevel@tonic-gate 	if (nwrote < 0) {
9547c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 	if (nwrote == 0)
9577c478bd9Sstevel@tonic-gate 	    /* eof */
9587c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9597c478bd9Sstevel@tonic-gate 	while (nwrote) {
9607c478bd9Sstevel@tonic-gate 	    sg_buf *sgp = conn->u.tcp.sgp;
9617c478bd9Sstevel@tonic-gate 	    if (nwrote < SG_LEN(sgp)) {
9627c478bd9Sstevel@tonic-gate 		SG_ADVANCE(sgp, nwrote);
9637c478bd9Sstevel@tonic-gate 		nwrote = 0;
9647c478bd9Sstevel@tonic-gate 	    } else {
9657c478bd9Sstevel@tonic-gate 		nwrote -= SG_LEN(sgp);
9667c478bd9Sstevel@tonic-gate 		conn->u.tcp.sgp++;
9677c478bd9Sstevel@tonic-gate 		conn->u.tcp.sgnum--;
9687c478bd9Sstevel@tonic-gate 		if (conn->u.tcp.sgnum == 0 && nwrote != 0)
9697c478bd9Sstevel@tonic-gate 		    abort();
9707c478bd9Sstevel@tonic-gate 	    }
9717c478bd9Sstevel@tonic-gate 	}
9727c478bd9Sstevel@tonic-gate 	if (conn->u.tcp.sgnum == 0) {
9737c478bd9Sstevel@tonic-gate 	    /* finished sending */
974159d09a2SMark Phalan 	    /* We should go back to reading, though if we sent a
975159d09a2SMark Phalan 	       FIELD_TOOLONG error in reply to a length with the high
976159d09a2SMark Phalan 	       bit set, RFC 4120 says we have to close the TCP
977159d09a2SMark Phalan 	       stream.  */
9787c478bd9Sstevel@tonic-gate 	    goto kill_tcp_connection;
9797c478bd9Sstevel@tonic-gate 	}
9807c478bd9Sstevel@tonic-gate     } else if (selflags & SSF_READ) {
9817c478bd9Sstevel@tonic-gate 	/* Read message length and data into one big buffer, already
9827c478bd9Sstevel@tonic-gate 	   allocated at connect time.  If we have a complete message,
9837c478bd9Sstevel@tonic-gate 	   we stop reading, so we should only be here if there is no
9847c478bd9Sstevel@tonic-gate 	   data in the buffer, or only an incomplete message.  */
9857c478bd9Sstevel@tonic-gate 	size_t len;
9867c478bd9Sstevel@tonic-gate 	ssize_t nread;
9877c478bd9Sstevel@tonic-gate 	if (conn->u.tcp.offset < 4) {
9887c478bd9Sstevel@tonic-gate 	    /* msglen has not been computed */
9897c478bd9Sstevel@tonic-gate 	    /* XXX Doing at least two reads here, letting the kernel
9907c478bd9Sstevel@tonic-gate 	       worry about buffering.  It'll be faster when we add
9917c478bd9Sstevel@tonic-gate 	       code to manage the buffer here.  */
9927c478bd9Sstevel@tonic-gate 	    len = 4 - conn->u.tcp.offset;
9937c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
9947c478bd9Sstevel@tonic-gate 				conn->u.tcp.buffer + conn->u.tcp.offset, len);
9957c478bd9Sstevel@tonic-gate 	    if (nread < 0)
9967c478bd9Sstevel@tonic-gate 		/* error */
9977c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
9987c478bd9Sstevel@tonic-gate 	    if (nread == 0)
9997c478bd9Sstevel@tonic-gate 		/* eof */
10007c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
10017c478bd9Sstevel@tonic-gate 	    conn->u.tcp.offset += nread;
10027c478bd9Sstevel@tonic-gate 	    if (conn->u.tcp.offset == 4) {
10037c478bd9Sstevel@tonic-gate 		unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
10047c478bd9Sstevel@tonic-gate 		conn->u.tcp.msglen = ((p[0] << 24)
10057c478bd9Sstevel@tonic-gate 				      | (p[1] << 16)
10067c478bd9Sstevel@tonic-gate 				      | (p[2] <<  8)
10077c478bd9Sstevel@tonic-gate 				      | p[3]);
10087c478bd9Sstevel@tonic-gate 		if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
1009159d09a2SMark Phalan 		    krb5_error_code err;
10107c478bd9Sstevel@tonic-gate 		    /* message too big */
10117c478bd9Sstevel@tonic-gate 		    krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu",
10127c478bd9Sstevel@tonic-gate 				     conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen,
10137c478bd9Sstevel@tonic-gate 				     (unsigned long) conn->u.tcp.bufsiz - 4);
10147c478bd9Sstevel@tonic-gate 		    /* XXX Should return an error.  */
1015159d09a2SMark Phalan 		    err = make_toolong_error (&conn->u.tcp.response);
1016159d09a2SMark Phalan 		    if (err) {
1017159d09a2SMark Phalan 			krb5_klog_syslog(LOG_ERR,
1018159d09a2SMark Phalan 					 "error constructing KRB_ERR_FIELD_TOOLONG error! %s",
1019159d09a2SMark Phalan 					 error_message(err));
1020159d09a2SMark Phalan 			goto kill_tcp_connection;
1021159d09a2SMark Phalan 		    }
1022159d09a2SMark Phalan 		    goto have_response;
10237c478bd9Sstevel@tonic-gate 		}
10247c478bd9Sstevel@tonic-gate 	    }
10257c478bd9Sstevel@tonic-gate 	} else {
10267c478bd9Sstevel@tonic-gate 	    /* msglen known */
10277c478bd9Sstevel@tonic-gate 	    krb5_data request;
10287c478bd9Sstevel@tonic-gate 	    krb5_error_code err;
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	    len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
10317c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
10327c478bd9Sstevel@tonic-gate 				conn->u.tcp.buffer + conn->u.tcp.offset, len);
10337c478bd9Sstevel@tonic-gate 	    if (nread < 0)
10347c478bd9Sstevel@tonic-gate 		/* error */
10357c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
10367c478bd9Sstevel@tonic-gate 	    if (nread == 0)
10377c478bd9Sstevel@tonic-gate 		/* eof */
10387c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
10397c478bd9Sstevel@tonic-gate 	    conn->u.tcp.offset += nread;
10407c478bd9Sstevel@tonic-gate 	    if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
10417c478bd9Sstevel@tonic-gate 		return;
10427c478bd9Sstevel@tonic-gate 	    /* have a complete message, and exactly one message */
10437c478bd9Sstevel@tonic-gate 	    request.length = conn->u.tcp.msglen;
10447c478bd9Sstevel@tonic-gate 	    request.data = conn->u.tcp.buffer + 4;
104556a424ccSmp 	    err = dispatch(&request, &conn->u.tcp.faddr,
10467c478bd9Sstevel@tonic-gate 			   &conn->u.tcp.response);
10477c478bd9Sstevel@tonic-gate 	    if (err) {
10487c478bd9Sstevel@tonic-gate 		com_err(prog, err, gettext("while dispatching (tcp)"));
10497c478bd9Sstevel@tonic-gate 		goto kill_tcp_connection;
10507c478bd9Sstevel@tonic-gate 	    }
1051159d09a2SMark Phalan 	have_response:
10527c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24);
10537c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16);
10547c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8);
10557c478bd9Sstevel@tonic-gate 	    conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0);
10567c478bd9Sstevel@tonic-gate 	    SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
10577c478bd9Sstevel@tonic-gate 		   conn->u.tcp.response->length);
10587c478bd9Sstevel@tonic-gate 	    conn->u.tcp.sgp = conn->u.tcp.sgbuf;
10597c478bd9Sstevel@tonic-gate 	    conn->u.tcp.sgnum = 2;
10607c478bd9Sstevel@tonic-gate 	    FD_CLR(conn->fd, &sstate.rfds);
10617c478bd9Sstevel@tonic-gate 	    FD_SET(conn->fd, &sstate.wfds);
10627c478bd9Sstevel@tonic-gate 	}
10637c478bd9Sstevel@tonic-gate     } else
10647c478bd9Sstevel@tonic-gate 	abort();
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate     return;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate kill_tcp_connection:
10697c478bd9Sstevel@tonic-gate     kill_tcp_connection(conn);
10707c478bd9Sstevel@tonic-gate }
10717c478bd9Sstevel@tonic-gate 
service_conn(struct connection * conn,const char * prog,int selflags)10727c478bd9Sstevel@tonic-gate static void service_conn(struct connection *conn, const char *prog,
10737c478bd9Sstevel@tonic-gate 			 int selflags)
10747c478bd9Sstevel@tonic-gate {
10757c478bd9Sstevel@tonic-gate     conn->service(conn, prog, selflags);
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate krb5_error_code
listen_and_process(const char * prog)10797c478bd9Sstevel@tonic-gate listen_and_process(const char *prog)
10807c478bd9Sstevel@tonic-gate {
10817c478bd9Sstevel@tonic-gate     int			nfound;
1082159d09a2SMark Phalan     /* This struct contains 3 fd_set objects; on some platforms, they
1083159d09a2SMark Phalan        can be rather large.  Making this static avoids putting all
1084159d09a2SMark Phalan        that junk on the stack.  */
1085159d09a2SMark Phalan     static struct select_state sout;
10867c478bd9Sstevel@tonic-gate     int			i, sret;
10877c478bd9Sstevel@tonic-gate     krb5_error_code	err;
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate     if (conns == (struct connection **) NULL)
10907c478bd9Sstevel@tonic-gate 	return KDC5_NONET;
1091*55fea89dSDan Cross 
10927c478bd9Sstevel@tonic-gate     while (!signal_requests_exit) {
10937c478bd9Sstevel@tonic-gate 	if (signal_requests_hup) {
10947c478bd9Sstevel@tonic-gate 	    krb5_klog_reopen(kdc_context);
10957c478bd9Sstevel@tonic-gate 	    signal_requests_hup = 0;
10967c478bd9Sstevel@tonic-gate 	}
10977c478bd9Sstevel@tonic-gate 	sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
10987c478bd9Sstevel@tonic-gate 	err = krb5int_cm_call_select(&sstate, &sout, &sret);
10997c478bd9Sstevel@tonic-gate 	if (err) {
11007c478bd9Sstevel@tonic-gate 	    com_err(prog, err, gettext("while selecting for network input(1)"));
11017c478bd9Sstevel@tonic-gate 	    continue;
11027c478bd9Sstevel@tonic-gate 	}
11037c478bd9Sstevel@tonic-gate 	if (sret == -1) {
11047c478bd9Sstevel@tonic-gate 	    if (errno != EINTR)
11057c478bd9Sstevel@tonic-gate 		com_err(prog, errno, gettext("while selecting for network input(2)"));
11067c478bd9Sstevel@tonic-gate 	    continue;
11077c478bd9Sstevel@tonic-gate 	}
11087c478bd9Sstevel@tonic-gate 	nfound = sret;
11097c478bd9Sstevel@tonic-gate 	for (i=0; i<n_sockets && nfound > 0; i++) {
11107c478bd9Sstevel@tonic-gate 	    int sflags = 0;
11117c478bd9Sstevel@tonic-gate 	    if (conns[i]->fd < 0)
11127c478bd9Sstevel@tonic-gate 		abort();
11137c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i]->fd, &sout.rfds))
11147c478bd9Sstevel@tonic-gate 		sflags |= SSF_READ, nfound--;
11157c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i]->fd, &sout.wfds))
11167c478bd9Sstevel@tonic-gate 		sflags |= SSF_WRITE, nfound--;
11177c478bd9Sstevel@tonic-gate 	    if (sflags)
11187c478bd9Sstevel@tonic-gate 		service_conn(conns[i], prog, sflags);
11197c478bd9Sstevel@tonic-gate 	}
11207c478bd9Sstevel@tonic-gate     }
11217c478bd9Sstevel@tonic-gate     return 0;
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate krb5_error_code
closedown_network(const char * prog)11257c478bd9Sstevel@tonic-gate closedown_network(const char *prog)
11267c478bd9Sstevel@tonic-gate {
11277c478bd9Sstevel@tonic-gate     int i;
11287c478bd9Sstevel@tonic-gate     struct connection *conn;
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate     if (conns == (struct connection **) NULL)
11317c478bd9Sstevel@tonic-gate 	return KDC5_NONET;
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate     FOREACH_ELT (connections, i, conn) {
11347c478bd9Sstevel@tonic-gate 	if (conn->fd >= 0)
11357c478bd9Sstevel@tonic-gate 	    (void) close(conn->fd);
11367c478bd9Sstevel@tonic-gate 	DEL (connections, i);
113756a424ccSmp 	/* There may also be per-connection data in the tcp structure
113856a424ccSmp 	   (tcp.buffer, tcp.response) that we're not freeing here.
113956a424ccSmp 	   That should only happen if we quit with a connection in
114056a424ccSmp 	   progress.  */
114156a424ccSmp 	free(conn);
11427c478bd9Sstevel@tonic-gate     }
11437c478bd9Sstevel@tonic-gate     FREE_SET_DATA(connections);
11447c478bd9Sstevel@tonic-gate     FREE_SET_DATA(udp_port_data);
11457c478bd9Sstevel@tonic-gate     FREE_SET_DATA(tcp_port_data);
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate     return 0;
11487c478bd9Sstevel@tonic-gate }
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate #endif /* INET */
1151