17c478bd9Sstevel@tonic-gate /*
2*505d05c7Sgtb  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * lib/krb5/os/sendto_kdc.c
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * Copyright 1990,1991,2001,2002 by the Massachusetts Institute of Technology.
107c478bd9Sstevel@tonic-gate  * All Rights Reserved.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
137c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
147c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
157c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
167c478bd9Sstevel@tonic-gate  *
177c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
187c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
197c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
207c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
217c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
227c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
237c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
247c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
257c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
267c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
277c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
287c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
297c478bd9Sstevel@tonic-gate  * or implied warranty.
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * Send packet to KDC for realm; wait for response, retransmitting
337c478bd9Sstevel@tonic-gate  * as necessary.
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #define NEED_SOCKETS
377c478bd9Sstevel@tonic-gate #define NEED_LOWLEVEL_IO
387c478bd9Sstevel@tonic-gate #include <fake-addrinfo.h>
397c478bd9Sstevel@tonic-gate #include <k5-int.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H
427c478bd9Sstevel@tonic-gate #include <sys/time.h>
437c478bd9Sstevel@tonic-gate #else
447c478bd9Sstevel@tonic-gate #include <time.h>
457c478bd9Sstevel@tonic-gate #endif
467c478bd9Sstevel@tonic-gate #include "os-proto.h"
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #ifdef _AIX
497c478bd9Sstevel@tonic-gate #include <sys/select.h>
507c478bd9Sstevel@tonic-gate #endif
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /* For FIONBIO.  */
537c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
547c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H
557c478bd9Sstevel@tonic-gate #include <sys/filio.h>
567c478bd9Sstevel@tonic-gate #endif
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #define MAX_PASS		    3
597c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */
607c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT	 1465 */
617c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT		32700 /* could probably do 64K-epsilon ? */
627c478bd9Sstevel@tonic-gate 
63*505d05c7Sgtb krb5_error_code krb5int_sendto(krb5_context, const krb5_data *,
64*505d05c7Sgtb 			    const struct addrlist *, krb5_data *,
65*505d05c7Sgtb 			    struct sockaddr_storage *,
66*505d05c7Sgtb 			    socklen_t *, int *);
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /* Solaris kerberos: leaving this here because other code depends on this. */
697c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len)
707c478bd9Sstevel@tonic-gate {
717c478bd9Sstevel@tonic-gate     fwrite(data, 1, len, stderr);
727c478bd9Sstevel@tonic-gate     /* stderr is unbuffered */
737c478bd9Sstevel@tonic-gate }
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate  * Solaris Kerberos: only including the debug stuff if DEBUG defined outside
797c478bd9Sstevel@tonic-gate  * this file.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate #ifdef  DEBUG
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024];
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate static void putstr(const char *str)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate     /* Solaris kerberos: build the string which will be passed to syslog later */
907c478bd9Sstevel@tonic-gate     strlcat(global_err_str, str, sizeof (global_err_str));
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint
947c478bd9Sstevel@tonic-gate #define dperror dprint
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate #include <com_err.h>
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static void
997c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...)
1007c478bd9Sstevel@tonic-gate {
1017c478bd9Sstevel@tonic-gate     va_list args;
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate     /* Temporaries for variable arguments, etc.  */
1047c478bd9Sstevel@tonic-gate     krb5_error_code kerr;
1057c478bd9Sstevel@tonic-gate     int err;
1067c478bd9Sstevel@tonic-gate     fd_set *rfds, *wfds, *xfds;
1077c478bd9Sstevel@tonic-gate     int i;
1087c478bd9Sstevel@tonic-gate     int maxfd;
1097c478bd9Sstevel@tonic-gate     struct timeval *tv;
1107c478bd9Sstevel@tonic-gate     struct addrinfo *ai;
1117c478bd9Sstevel@tonic-gate     const krb5_data *d;
1127c478bd9Sstevel@tonic-gate     char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
1137c478bd9Sstevel@tonic-gate     const char *p;
1147c478bd9Sstevel@tonic-gate     char tmpbuf[NI_MAXHOST + NI_MAXSERV + 30];
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate     /*
1177c478bd9Sstevel@tonic-gate      * Solaris kerberos: modified this function to create a string to pass to
1187c478bd9Sstevel@tonic-gate      * syslog()
1197c478bd9Sstevel@tonic-gate      */
1207c478bd9Sstevel@tonic-gate     global_err_str[0] = NULL;
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate     va_start(args, fmt);
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate #define putf(FMT,X)	(sprintf(tmpbuf,FMT,X),putstr(tmpbuf))
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate     for (; *fmt; fmt++) {
1277c478bd9Sstevel@tonic-gate 	if (*fmt != '%') {
1287c478bd9Sstevel@tonic-gate 	    /* Possible optimization: Look for % and print all chars
1297c478bd9Sstevel@tonic-gate 	       up to it in one call.  */
1307c478bd9Sstevel@tonic-gate 	    putf("%c", *fmt);
1317c478bd9Sstevel@tonic-gate 	    continue;
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 	/* After this, always processing a '%' sequence.  */
1347c478bd9Sstevel@tonic-gate 	fmt++;
1357c478bd9Sstevel@tonic-gate 	switch (*fmt) {
1367c478bd9Sstevel@tonic-gate 	case 0:
1377c478bd9Sstevel@tonic-gate 	default:
1387c478bd9Sstevel@tonic-gate 	    abort();
1397c478bd9Sstevel@tonic-gate 	case 'E':
1407c478bd9Sstevel@tonic-gate 	    /* %E => krb5_error_code */
1417c478bd9Sstevel@tonic-gate 	    kerr = va_arg(args, krb5_error_code);
1427c478bd9Sstevel@tonic-gate 	    sprintf(tmpbuf, "%lu/", (unsigned long) kerr);
1437c478bd9Sstevel@tonic-gate 	    putstr(tmpbuf);
1447c478bd9Sstevel@tonic-gate 	    p = error_message(kerr);
1457c478bd9Sstevel@tonic-gate 	    putstr(p);
1467c478bd9Sstevel@tonic-gate 	    break;
1477c478bd9Sstevel@tonic-gate 	case 'm':
1487c478bd9Sstevel@tonic-gate 	    /* %m => errno value (int) */
1497c478bd9Sstevel@tonic-gate 	    /* Like syslog's %m except the errno value is passed in
1507c478bd9Sstevel@tonic-gate 	       rather than the current value.  */
1517c478bd9Sstevel@tonic-gate 	    err = va_arg(args, int);
1527c478bd9Sstevel@tonic-gate 	    putf("%d/", err);
1537c478bd9Sstevel@tonic-gate 	    p = strerror(err);
1547c478bd9Sstevel@tonic-gate 	    putstr(p);
1557c478bd9Sstevel@tonic-gate 	    break;
1567c478bd9Sstevel@tonic-gate 	case 'F':
1577c478bd9Sstevel@tonic-gate 	    /* %F => fd_set *, fd_set *, fd_set *, int */
1587c478bd9Sstevel@tonic-gate 	    rfds = va_arg(args, fd_set *);
1597c478bd9Sstevel@tonic-gate 	    wfds = va_arg(args, fd_set *);
1607c478bd9Sstevel@tonic-gate 	    xfds = va_arg(args, fd_set *);
1617c478bd9Sstevel@tonic-gate 	    maxfd = va_arg(args, int);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	    for (i = 0; i < maxfd; i++) {
1647c478bd9Sstevel@tonic-gate 		int r = FD_ISSET(i, rfds);
1657c478bd9Sstevel@tonic-gate 		int w = wfds && FD_ISSET(i, wfds);
1667c478bd9Sstevel@tonic-gate 		int x = xfds && FD_ISSET(i, xfds);
1677c478bd9Sstevel@tonic-gate 		if (r || w || x) {
1687c478bd9Sstevel@tonic-gate 		    putf(" %d", i);
1697c478bd9Sstevel@tonic-gate 		    if (r)
1707c478bd9Sstevel@tonic-gate 			putstr("r");
1717c478bd9Sstevel@tonic-gate 		    if (w)
1727c478bd9Sstevel@tonic-gate 			putstr("w");
1737c478bd9Sstevel@tonic-gate 		    if (x)
1747c478bd9Sstevel@tonic-gate 			putstr("x");
1757c478bd9Sstevel@tonic-gate 		}
1767c478bd9Sstevel@tonic-gate 	    }
1777c478bd9Sstevel@tonic-gate 	    putstr(" ");
1787c478bd9Sstevel@tonic-gate 	    break;
1797c478bd9Sstevel@tonic-gate 	case 's':
1807c478bd9Sstevel@tonic-gate 	    /* %s => char * */
1817c478bd9Sstevel@tonic-gate 	    p = va_arg(args, const char *);
1827c478bd9Sstevel@tonic-gate 	    putstr(p);
1837c478bd9Sstevel@tonic-gate 	    break;
1847c478bd9Sstevel@tonic-gate 	case 't':
1857c478bd9Sstevel@tonic-gate 	    /* %t => struct timeval * */
1867c478bd9Sstevel@tonic-gate 	    tv = va_arg(args, struct timeval *);
1877c478bd9Sstevel@tonic-gate 	    if (tv) {
1887c478bd9Sstevel@tonic-gate 		sprintf(tmpbuf, "%ld.%06ld",
1897c478bd9Sstevel@tonic-gate 			(long) tv->tv_sec, (long) tv->tv_usec);
1907c478bd9Sstevel@tonic-gate 		putstr(tmpbuf);
1917c478bd9Sstevel@tonic-gate 	    } else
1927c478bd9Sstevel@tonic-gate 		putstr("never");
1937c478bd9Sstevel@tonic-gate 	    break;
1947c478bd9Sstevel@tonic-gate 	case 'd':
1957c478bd9Sstevel@tonic-gate 	    /* %d => int */
1967c478bd9Sstevel@tonic-gate 	    putf("%d", va_arg(args, int));
1977c478bd9Sstevel@tonic-gate 	    break;
1987c478bd9Sstevel@tonic-gate 	case 'p':
1997c478bd9Sstevel@tonic-gate 	    /* %p => pointer */
2007c478bd9Sstevel@tonic-gate 	    putf("%p", va_arg(args, void*));
2017c478bd9Sstevel@tonic-gate 	    break;
2027c478bd9Sstevel@tonic-gate 	case 'A':
2037c478bd9Sstevel@tonic-gate 	    /* %A => addrinfo */
2047c478bd9Sstevel@tonic-gate 	    ai = va_arg(args, struct addrinfo *);
2057c478bd9Sstevel@tonic-gate 	    if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen,
2067c478bd9Sstevel@tonic-gate 				  addrbuf, sizeof (addrbuf),
2077c478bd9Sstevel@tonic-gate 				  portbuf, sizeof (portbuf),
2087c478bd9Sstevel@tonic-gate 				  NI_NUMERICHOST | NI_NUMERICSERV))
2097c478bd9Sstevel@tonic-gate 		strcpy (addrbuf, "??"), strcpy (portbuf, "??");
2107c478bd9Sstevel@tonic-gate 	    sprintf(tmpbuf, "%s %s.%s",
2117c478bd9Sstevel@tonic-gate 		    (ai->ai_socktype == SOCK_DGRAM
2127c478bd9Sstevel@tonic-gate 		     ? "udp"
2137c478bd9Sstevel@tonic-gate 		     : ai->ai_socktype == SOCK_STREAM
2147c478bd9Sstevel@tonic-gate 		     ? "tcp"
2157c478bd9Sstevel@tonic-gate 		     : "???"),
2167c478bd9Sstevel@tonic-gate 		    addrbuf, portbuf);
2177c478bd9Sstevel@tonic-gate 	    putstr(tmpbuf);
2187c478bd9Sstevel@tonic-gate 	    break;
2197c478bd9Sstevel@tonic-gate 	case 'D':
2207c478bd9Sstevel@tonic-gate 	    /* %D => krb5_data * */
2217c478bd9Sstevel@tonic-gate 	    d = va_arg(args, krb5_data *);
2227c478bd9Sstevel@tonic-gate 	    p = d->data;
2237c478bd9Sstevel@tonic-gate 	    putstr("0x");
2247c478bd9Sstevel@tonic-gate 	    for (i = 0; i < d->length; i++) {
2257c478bd9Sstevel@tonic-gate 		putf("%.2x", *p++);
2267c478bd9Sstevel@tonic-gate 	    }
2277c478bd9Sstevel@tonic-gate 	    break;
2287c478bd9Sstevel@tonic-gate 	}
2297c478bd9Sstevel@tonic-gate     }
2307c478bd9Sstevel@tonic-gate     va_end(args);
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate     /* Solaris kerberos: use syslog() for debug output */
2337c478bd9Sstevel@tonic-gate     syslog(LOG_DEBUG, global_err_str);
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate #else
2377c478bd9Sstevel@tonic-gate #define dprint (void)
2387c478bd9Sstevel@tonic-gate #define dperror(MSG) ((void)(MSG))
2397c478bd9Sstevel@tonic-gate #endif
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate static int
2427c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src)
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate     int err, i;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate #ifdef DEBUG
2477c478bd9Sstevel@tonic-gate     /*LINTED*/
2487c478bd9Sstevel@tonic-gate     dprint("merging addrlists:\n\tlist1: ");
2497c478bd9Sstevel@tonic-gate     for (i = 0; i < dest->naddrs; i++)
2507c478bd9Sstevel@tonic-gate 	/*LINTED*/
2517c478bd9Sstevel@tonic-gate 	dprint(" %A", dest->addrs[i]);
2527c478bd9Sstevel@tonic-gate     /*LINTED*/
2537c478bd9Sstevel@tonic-gate     dprint("\n\tlist2: ");
2547c478bd9Sstevel@tonic-gate     for (i = 0; i < src->naddrs; i++)
2557c478bd9Sstevel@tonic-gate 	/*LINTED*/
2567c478bd9Sstevel@tonic-gate 	dprint(" %A", src->addrs[i]);
2577c478bd9Sstevel@tonic-gate     /*LINTED*/
2587c478bd9Sstevel@tonic-gate     dprint("\n");
2597c478bd9Sstevel@tonic-gate #endif
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate     err = krb5int_grow_addrlist (dest, src->naddrs);
2627c478bd9Sstevel@tonic-gate     if (err)
2637c478bd9Sstevel@tonic-gate 	return err;
2647c478bd9Sstevel@tonic-gate     for (i = 0; i < src->naddrs; i++) {
2657c478bd9Sstevel@tonic-gate 	dest->addrs[dest->naddrs + i] = src->addrs[i];
2667c478bd9Sstevel@tonic-gate 	src->addrs[i] = 0;
2677c478bd9Sstevel@tonic-gate     }
2687c478bd9Sstevel@tonic-gate     dest->naddrs += i;
2697c478bd9Sstevel@tonic-gate     src->naddrs = 0;
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate #ifdef DEBUG
2727c478bd9Sstevel@tonic-gate     /*LINTED*/
2737c478bd9Sstevel@tonic-gate     dprint("\tout:   ");
2747c478bd9Sstevel@tonic-gate     for (i = 0; i < dest->naddrs; i++)
2757c478bd9Sstevel@tonic-gate 	/*LINTED*/
2767c478bd9Sstevel@tonic-gate 	dprint(" %A", dest->addrs[i]);
2777c478bd9Sstevel@tonic-gate     /*LINTED*/
2787c478bd9Sstevel@tonic-gate     dprint("\n");
2797c478bd9Sstevel@tonic-gate #endif
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate     return 0;
2827c478bd9Sstevel@tonic-gate }
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate /*
2857c478bd9Sstevel@tonic-gate  * send the formatted request 'message' to a KDC for realm 'realm' and
2867c478bd9Sstevel@tonic-gate  * return the response (if any) in 'reply'.
2877c478bd9Sstevel@tonic-gate  *
2887c478bd9Sstevel@tonic-gate  * If the message is sent and a response is received, 0 is returned,
2897c478bd9Sstevel@tonic-gate  * otherwise an error code is returned.
2907c478bd9Sstevel@tonic-gate  *
2917c478bd9Sstevel@tonic-gate  * The storage for 'reply' is allocated and should be freed by the caller
2927c478bd9Sstevel@tonic-gate  * when finished.
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate krb5_error_code
2967c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message,
2977c478bd9Sstevel@tonic-gate 		 const krb5_data *realm, krb5_data *reply,
298*505d05c7Sgtb 		 int *use_master, int tcp_only)
2997c478bd9Sstevel@tonic-gate {
3007c478bd9Sstevel@tonic-gate     krb5_error_code retval;
3017c478bd9Sstevel@tonic-gate     struct addrlist addrs;
302*505d05c7Sgtb     int socktype1 = 0, socktype2 = 0, addr_used;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate     /*
3057c478bd9Sstevel@tonic-gate      * find KDC location(s) for realm
3067c478bd9Sstevel@tonic-gate      */
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate     /*
3097c478bd9Sstevel@tonic-gate      * BUG: This code won't return "interesting" errors (e.g., out of mem,
3107c478bd9Sstevel@tonic-gate      * bad config file) from locate_kdc.  KRB5_REALM_CANT_RESOLVE can be
3117c478bd9Sstevel@tonic-gate      * ignored from one query of two, but if only one query is done, or
3127c478bd9Sstevel@tonic-gate      * both return that error, it should be returned to the caller.  Also,
3137c478bd9Sstevel@tonic-gate      * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
3147c478bd9Sstevel@tonic-gate      * should probably be returned as well.
3157c478bd9Sstevel@tonic-gate      */
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate     /*LINTED*/
3187c478bd9Sstevel@tonic-gate     dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
3197c478bd9Sstevel@tonic-gate     /*LINTED*/
320*505d05c7Sgtb 	   message->length, message->data, realm, *use_master, tcp_only);
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate     /*
3237c478bd9Sstevel@tonic-gate      * Solaris Kerberos: keep it simple by not supporting a udp_preference_limit
3247c478bd9Sstevel@tonic-gate      */
3257c478bd9Sstevel@tonic-gate #if 0 /************** Begin IFDEF'ed OUT *******************************/
3267c478bd9Sstevel@tonic-gate     if (!tcp_only && context->udp_pref_limit < 0) {
3277c478bd9Sstevel@tonic-gate 	int tmp;
3287c478bd9Sstevel@tonic-gate 	retval = profile_get_integer(context->profile,
3297c478bd9Sstevel@tonic-gate 				     "libdefaults", "udp_preference_limit", 0,
3307c478bd9Sstevel@tonic-gate 				     DEFAULT_UDP_PREF_LIMIT, &tmp);
3317c478bd9Sstevel@tonic-gate 	if (retval)
3327c478bd9Sstevel@tonic-gate 	    return retval;
3337c478bd9Sstevel@tonic-gate 	if (tmp < 0)
3347c478bd9Sstevel@tonic-gate 	    tmp = DEFAULT_UDP_PREF_LIMIT;
3357c478bd9Sstevel@tonic-gate 	else if (tmp > HARD_UDP_LIMIT) {
3367c478bd9Sstevel@tonic-gate 	    /* In the unlikely case that a *really* big value is
3377c478bd9Sstevel@tonic-gate 	       given, let 'em use as big as we think we can
3387c478bd9Sstevel@tonic-gate 	       support.  */
3397c478bd9Sstevel@tonic-gate 	    tmp = HARD_UDP_LIMIT;
3407c478bd9Sstevel@tonic-gate 	}
3417c478bd9Sstevel@tonic-gate 	context->udp_pref_limit = tmp;
3427c478bd9Sstevel@tonic-gate     }
3437c478bd9Sstevel@tonic-gate #endif /**************** END IFDEF'ed OUT *******************************/
3447c478bd9Sstevel@tonic-gate 
345*505d05c7Sgtb     retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate     if (tcp_only)
3487c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_STREAM, socktype2 = 0;
3497c478bd9Sstevel@tonic-gate     else if (message->length <= context->udp_pref_limit)
3507c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
3517c478bd9Sstevel@tonic-gate     else
3527c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
3537c478bd9Sstevel@tonic-gate 
354*505d05c7Sgtb     retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
3557c478bd9Sstevel@tonic-gate     if (socktype2) {
3567c478bd9Sstevel@tonic-gate 	struct addrlist addrs2;
3577c478bd9Sstevel@tonic-gate 
358*505d05c7Sgtb 	retval = krb5_locate_kdc(context, realm, &addrs2, *use_master,
3597c478bd9Sstevel@tonic-gate 				 socktype2, 0);
3607c478bd9Sstevel@tonic-gate 	if (retval == 0) {
3617c478bd9Sstevel@tonic-gate 	    (void) merge_addrlists(&addrs, &addrs2);
3627c478bd9Sstevel@tonic-gate 	    krb5int_free_addrlist(&addrs2);
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate     }
3657c478bd9Sstevel@tonic-gate     if (addrs.naddrs > 0) {
366*505d05c7Sgtb         retval = krb5int_sendto (context, message, &addrs, reply, 0, 0,
367*505d05c7Sgtb 		&addr_used);
368*505d05c7Sgtb 	if (retval == 0) {
369*505d05c7Sgtb             /*
370*505d05c7Sgtb 	     * Set use_master to 1 if we ended up talking to a master when
371*505d05c7Sgtb 	     * didn't explicitly request to
372*505d05c7Sgtb 	     */
373*505d05c7Sgtb 
374*505d05c7Sgtb 	    if (*use_master == 0) {
375*505d05c7Sgtb 	        struct addrlist addrs3;
376*505d05c7Sgtb 		retval = krb5_locate_kdc(context, realm, &addrs3, 1,
377*505d05c7Sgtb 					addrs.addrs[addr_used]->ai_socktype,
378*505d05c7Sgtb 					addrs.addrs[addr_used]->ai_family);
379*505d05c7Sgtb 		if (retval == 0) {
380*505d05c7Sgtb 		    int i;
381*505d05c7Sgtb 		    for (i = 0; i < addrs3.naddrs; i++) {
382*505d05c7Sgtb 			if (addrs.addrs[addr_used]->ai_addrlen ==
383*505d05c7Sgtb 			    addrs3.addrs[i]->ai_addrlen &&
384*505d05c7Sgtb 			    memcmp(addrs.addrs[addr_used]->ai_addr,
385*505d05c7Sgtb 				addrs3.addrs[i]->ai_addr,
386*505d05c7Sgtb 				addrs.addrs[addr_used]->ai_addrlen) == 0) {
387*505d05c7Sgtb 				*use_master = 1;
388*505d05c7Sgtb 				break;
389*505d05c7Sgtb 			}
390*505d05c7Sgtb 		    }
391*505d05c7Sgtb 		    krb5int_free_addrlist (&addrs3);
392*505d05c7Sgtb 		}
393*505d05c7Sgtb 	    }
394*505d05c7Sgtb 	    krb5int_free_addrlist (&addrs);
3957c478bd9Sstevel@tonic-gate 	    return 0;
396*505d05c7Sgtb 	}
397*505d05c7Sgtb 	krb5int_free_addrlist (&addrs);
3987c478bd9Sstevel@tonic-gate     }
3997c478bd9Sstevel@tonic-gate     return retval;
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate  * Notes:
4057c478bd9Sstevel@tonic-gate  *
4067c478bd9Sstevel@tonic-gate  * Getting "connection refused" on a connected UDP socket causes
4077c478bd9Sstevel@tonic-gate  * select to indicate write capability on UNIX, but only shows up
4087c478bd9Sstevel@tonic-gate  * as an exception on Windows.  (I don't think any UNIX system flags
4097c478bd9Sstevel@tonic-gate  * the error as an exception.)  So we check for both, or make it
4107c478bd9Sstevel@tonic-gate  * system-specific.
4117c478bd9Sstevel@tonic-gate  *
4127c478bd9Sstevel@tonic-gate  * Always watch for responses from *any* of the servers.  Eventually
4137c478bd9Sstevel@tonic-gate  * fix the UDP code to do the same.
4147c478bd9Sstevel@tonic-gate  *
4157c478bd9Sstevel@tonic-gate  * To do:
4167c478bd9Sstevel@tonic-gate  * - TCP NOPUSH/CORK socket options?
4177c478bd9Sstevel@tonic-gate  * - error codes that don't suck
4187c478bd9Sstevel@tonic-gate  * - getsockopt(SO_ERROR) to check connect status
4197c478bd9Sstevel@tonic-gate  * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
4207c478bd9Sstevel@tonic-gate  *   connections already in progress
4217c478bd9Sstevel@tonic-gate  */
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate #include <cm.h>
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate static const char *const state_strings[] = {
4267c478bd9Sstevel@tonic-gate     "INITIALIZING", "CONNECTING", "WRITING", "READING", "FAILED"
4277c478bd9Sstevel@tonic-gate };
4287c478bd9Sstevel@tonic-gate enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
4297c478bd9Sstevel@tonic-gate struct incoming_krb5_message {
4307c478bd9Sstevel@tonic-gate     size_t bufsizebytes_read;
4317c478bd9Sstevel@tonic-gate     size_t bufsize;
4327c478bd9Sstevel@tonic-gate     char *buf;
4337c478bd9Sstevel@tonic-gate     char *pos;
4347c478bd9Sstevel@tonic-gate     unsigned char bufsizebytes[4];
4357c478bd9Sstevel@tonic-gate     size_t n_left;
4367c478bd9Sstevel@tonic-gate };
4377c478bd9Sstevel@tonic-gate struct conn_state {
4387c478bd9Sstevel@tonic-gate     SOCKET fd;
4397c478bd9Sstevel@tonic-gate     krb5_error_code err;
4407c478bd9Sstevel@tonic-gate     enum conn_states state;
4417c478bd9Sstevel@tonic-gate     unsigned int is_udp : 1;
4427c478bd9Sstevel@tonic-gate     int (*service)(struct conn_state *, struct select_state *, int);
4437c478bd9Sstevel@tonic-gate     struct addrinfo *addr;
4447c478bd9Sstevel@tonic-gate     struct {
4457c478bd9Sstevel@tonic-gate 	struct {
4467c478bd9Sstevel@tonic-gate 	    sg_buf sgbuf[2];
4477c478bd9Sstevel@tonic-gate 	    sg_buf *sgp;
4487c478bd9Sstevel@tonic-gate 	    int sg_count;
4497c478bd9Sstevel@tonic-gate 	} out;
4507c478bd9Sstevel@tonic-gate 	struct incoming_krb5_message in;
4517c478bd9Sstevel@tonic-gate     } x;
4527c478bd9Sstevel@tonic-gate };
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp)
4557c478bd9Sstevel@tonic-gate {
4567c478bd9Sstevel@tonic-gate     if (gettimeofday(tvp, 0)) {
4577c478bd9Sstevel@tonic-gate 	dperror("gettimeofday");
4587c478bd9Sstevel@tonic-gate 	return errno;
4597c478bd9Sstevel@tonic-gate     }
4607c478bd9Sstevel@tonic-gate     return 0;
4617c478bd9Sstevel@tonic-gate }
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate /*
4647c478bd9Sstevel@tonic-gate  * Call select and return results.
4657c478bd9Sstevel@tonic-gate  * Input: interesting file descriptors and absolute timeout
4667c478bd9Sstevel@tonic-gate  * Output: select return value (-1 or num fds ready) and fd_sets
4677c478bd9Sstevel@tonic-gate  * Return: 0 (for i/o available or timeout) or error code.
4687c478bd9Sstevel@tonic-gate  */
4697c478bd9Sstevel@tonic-gate krb5_error_code
4707c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in,
4717c478bd9Sstevel@tonic-gate 			struct select_state *out, int *sret)
4727c478bd9Sstevel@tonic-gate {
4737c478bd9Sstevel@tonic-gate     struct timeval now, *timo;
4747c478bd9Sstevel@tonic-gate     krb5_error_code e;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate     *out = *in;
4777c478bd9Sstevel@tonic-gate     e = getcurtime(&now);
4787c478bd9Sstevel@tonic-gate     if (e)
4797c478bd9Sstevel@tonic-gate 	return e;
4807c478bd9Sstevel@tonic-gate     if (out->end_time.tv_sec == 0)
4817c478bd9Sstevel@tonic-gate 	timo = 0;
4827c478bd9Sstevel@tonic-gate     else {
4837c478bd9Sstevel@tonic-gate 	timo = &out->end_time;
4847c478bd9Sstevel@tonic-gate 	out->end_time.tv_sec -= now.tv_sec;
4857c478bd9Sstevel@tonic-gate 	out->end_time.tv_usec -= now.tv_usec;
4867c478bd9Sstevel@tonic-gate 	if (out->end_time.tv_usec < 0) {
4877c478bd9Sstevel@tonic-gate 	    out->end_time.tv_usec += 1000000;
4887c478bd9Sstevel@tonic-gate 	    out->end_time.tv_sec--;
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 	if (out->end_time.tv_sec < 0) {
4917c478bd9Sstevel@tonic-gate 	    *sret = 0;
4927c478bd9Sstevel@tonic-gate 	    return 0;
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate     }
4957c478bd9Sstevel@tonic-gate     /*LINTED*/
4967c478bd9Sstevel@tonic-gate     dprint("selecting on max=%d sockets [%F] timeout %t\n",
4977c478bd9Sstevel@tonic-gate 	    /*LINTED*/
4987c478bd9Sstevel@tonic-gate 	   out->max, &out->rfds, &out->wfds, &out->xfds, out->max, timo);
4997c478bd9Sstevel@tonic-gate     *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
5007c478bd9Sstevel@tonic-gate     e = SOCKET_ERRNO;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate #ifdef DEBUG
5037c478bd9Sstevel@tonic-gate     /*LINTED*/
5047c478bd9Sstevel@tonic-gate     dprint("select returns %d", *sret);
5057c478bd9Sstevel@tonic-gate     if (*sret < 0)
5067c478bd9Sstevel@tonic-gate 	/*LINTED*/
5077c478bd9Sstevel@tonic-gate 	dprint(", error = %E\n", e);
5087c478bd9Sstevel@tonic-gate     else if (*sret == 0)
5097c478bd9Sstevel@tonic-gate 	/*LINTED*/
5107c478bd9Sstevel@tonic-gate 	dprint(" (timeout)\n");
5117c478bd9Sstevel@tonic-gate     else
5127c478bd9Sstevel@tonic-gate 	/*LINTED*/
5137c478bd9Sstevel@tonic-gate 	dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
5147c478bd9Sstevel@tonic-gate #endif
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate     if (*sret < 0)
5177c478bd9Sstevel@tonic-gate 	return e;
5187c478bd9Sstevel@tonic-gate     return 0;
5197c478bd9Sstevel@tonic-gate }
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn,
5227c478bd9Sstevel@tonic-gate 			   struct select_state *selstate, int ssflags);
5237c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn,
5247c478bd9Sstevel@tonic-gate 			   struct select_state *selstate, int ssflags);
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate static int
5287c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai,
5297c478bd9Sstevel@tonic-gate 		  const krb5_data *message, unsigned char *message_len_buf,
5307c478bd9Sstevel@tonic-gate 		  char **udpbufp)
5317c478bd9Sstevel@tonic-gate {
5327c478bd9Sstevel@tonic-gate     state->state = INITIALIZING;
5337c478bd9Sstevel@tonic-gate     state->err = 0;
5347c478bd9Sstevel@tonic-gate     state->x.out.sgp = state->x.out.sgbuf;
5357c478bd9Sstevel@tonic-gate     state->addr = ai;
5367c478bd9Sstevel@tonic-gate     state->fd = INVALID_SOCKET;
5377c478bd9Sstevel@tonic-gate     SG_SET(&state->x.out.sgbuf[1], 0, 0);
5387c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_STREAM) {
5397c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
5407c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
5417c478bd9Sstevel@tonic-gate 	state->x.out.sg_count = 2;
5427c478bd9Sstevel@tonic-gate 	state->is_udp = 0;
5437c478bd9Sstevel@tonic-gate 	state->service = service_tcp_fd;
5447c478bd9Sstevel@tonic-gate     } else {
5457c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
5467c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[1], 0, 0);
5477c478bd9Sstevel@tonic-gate 	state->x.out.sg_count = 1;
5487c478bd9Sstevel@tonic-gate 	state->is_udp = 1;
5497c478bd9Sstevel@tonic-gate 	state->service = service_udp_fd;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	if (*udpbufp == 0) {
5527c478bd9Sstevel@tonic-gate 	    *udpbufp = malloc(krb5_max_dgram_size);
5537c478bd9Sstevel@tonic-gate 	    if (*udpbufp == 0) {
5547c478bd9Sstevel@tonic-gate 		dperror("malloc(krb5_max_dgram_size)");
5557c478bd9Sstevel@tonic-gate 		(void) closesocket(state->fd);
5567c478bd9Sstevel@tonic-gate 		state->fd = INVALID_SOCKET;
5577c478bd9Sstevel@tonic-gate 		state->state = FAILED;
5587c478bd9Sstevel@tonic-gate 		return 1;
5597c478bd9Sstevel@tonic-gate 	    }
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate 	state->x.in.buf = *udpbufp;
5627c478bd9Sstevel@tonic-gate 	state->x.in.bufsize = krb5_max_dgram_size;
5637c478bd9Sstevel@tonic-gate     }
5647c478bd9Sstevel@tonic-gate     return 0;
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate static int
5687c478bd9Sstevel@tonic-gate start_connection (struct conn_state *state, struct select_state *selstate)
5697c478bd9Sstevel@tonic-gate {
5707c478bd9Sstevel@tonic-gate     int fd, e;
5717c478bd9Sstevel@tonic-gate     struct addrinfo *ai = state->addr;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate     /*LINTED*/
5747c478bd9Sstevel@tonic-gate     dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
5757c478bd9Sstevel@tonic-gate 	   /*LINTED*/
5767c478bd9Sstevel@tonic-gate 	   ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
5777c478bd9Sstevel@tonic-gate     fd = socket(ai->ai_family, ai->ai_socktype, 0);
5787c478bd9Sstevel@tonic-gate     if (fd == INVALID_SOCKET) {
5797c478bd9Sstevel@tonic-gate 	state->err = SOCKET_ERRNO;
5807c478bd9Sstevel@tonic-gate 	/*LINTED*/
5817c478bd9Sstevel@tonic-gate 	dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
5827c478bd9Sstevel@tonic-gate 	return -1;		/* try other hosts */
5837c478bd9Sstevel@tonic-gate     }
5847c478bd9Sstevel@tonic-gate     /* Make it non-blocking.  */
5857c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_STREAM) {
5867c478bd9Sstevel@tonic-gate 	static const int one = 1;
5877c478bd9Sstevel@tonic-gate 	static const struct linger lopt = { 0, 0 };
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	if (ioctlsocket(fd, FIONBIO, (const void *) &one))
5907c478bd9Sstevel@tonic-gate 	    dperror("sendto_kdc: ioctl(FIONBIO)");
5917c478bd9Sstevel@tonic-gate 	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
5927c478bd9Sstevel@tonic-gate 	    dperror("sendto_kdc: setsockopt(SO_LINGER)");
5937c478bd9Sstevel@tonic-gate     }
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate     /* Start connecting to KDC.  */
5967c478bd9Sstevel@tonic-gate     /*LINTED*/
5977c478bd9Sstevel@tonic-gate     dprint(" fd %d; connecting to %A...\n", fd, ai);
5987c478bd9Sstevel@tonic-gate     e = connect(fd, ai->ai_addr, ai->ai_addrlen);
5997c478bd9Sstevel@tonic-gate     if (e != 0) {
6007c478bd9Sstevel@tonic-gate 	/*
6017c478bd9Sstevel@tonic-gate 	 * This is the path that should be followed for non-blocking
6027c478bd9Sstevel@tonic-gate 	 * connections.
6037c478bd9Sstevel@tonic-gate 	 */
6047c478bd9Sstevel@tonic-gate 	if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
6057c478bd9Sstevel@tonic-gate 	    state->state = CONNECTING;
6067c478bd9Sstevel@tonic-gate 	} else {
6077c478bd9Sstevel@tonic-gate 	    /*LINTED*/
6087c478bd9Sstevel@tonic-gate 	    dprint("connect failed: %m\n", SOCKET_ERRNO);
6097c478bd9Sstevel@tonic-gate 	    state->err = SOCKET_ERRNO;
6107c478bd9Sstevel@tonic-gate 	    state->state = FAILED;
6117c478bd9Sstevel@tonic-gate 	    return -2;
6127c478bd9Sstevel@tonic-gate 	}
6137c478bd9Sstevel@tonic-gate     } else {
6147c478bd9Sstevel@tonic-gate 	/*
6157c478bd9Sstevel@tonic-gate 	 * Connect returned zero even though we tried to make it
6167c478bd9Sstevel@tonic-gate 	 * non-blocking, which should have caused it to return before
6177c478bd9Sstevel@tonic-gate 	 * finishing the connection.  Oh well.  Someone's network
6187c478bd9Sstevel@tonic-gate 	 * stack is broken, but if they gave us a connection, use it.
6197c478bd9Sstevel@tonic-gate 	 */
6207c478bd9Sstevel@tonic-gate 	state->state = WRITING;
6217c478bd9Sstevel@tonic-gate     }
6227c478bd9Sstevel@tonic-gate     /*LINTED*/
6237c478bd9Sstevel@tonic-gate     dprint("new state = %s\n", state_strings[state->state]);
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate     state->fd = fd;
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_DGRAM) {
6287c478bd9Sstevel@tonic-gate 	/* Send it now.  */
6297c478bd9Sstevel@tonic-gate 	int ret;
6307c478bd9Sstevel@tonic-gate 	sg_buf *sg = &state->x.out.sgbuf[0];
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 	/*LINTED*/
6337c478bd9Sstevel@tonic-gate 	dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
6347c478bd9Sstevel@tonic-gate 	ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
6357c478bd9Sstevel@tonic-gate 	if (ret != SG_LEN(sg)) {
6367c478bd9Sstevel@tonic-gate 	    dperror("sendto");
6377c478bd9Sstevel@tonic-gate 	    (void) closesocket(state->fd);
6387c478bd9Sstevel@tonic-gate 	    state->fd = INVALID_SOCKET;
6397c478bd9Sstevel@tonic-gate 	    state->state = FAILED;
6407c478bd9Sstevel@tonic-gate 	    return -3;
6417c478bd9Sstevel@tonic-gate 	} else {
6427c478bd9Sstevel@tonic-gate 	    state->state = READING;
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate     }
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate     FD_SET(state->fd, &selstate->rfds);
6477c478bd9Sstevel@tonic-gate     if (state->state == CONNECTING || state->state == WRITING)
6487c478bd9Sstevel@tonic-gate 	FD_SET(state->fd, &selstate->wfds);
6497c478bd9Sstevel@tonic-gate     FD_SET(state->fd, &selstate->xfds);
6507c478bd9Sstevel@tonic-gate     if (selstate->max <= state->fd)
6517c478bd9Sstevel@tonic-gate 	selstate->max = state->fd + 1;
6527c478bd9Sstevel@tonic-gate     selstate->nfds++;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate     /*LINTED*/
6557c478bd9Sstevel@tonic-gate     dprint("new select vectors: %F\n",
6567c478bd9Sstevel@tonic-gate 	   /*LINTED*/
6577c478bd9Sstevel@tonic-gate 	   &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate     return 0;
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise.
6637c478bd9Sstevel@tonic-gate    If 0 is returned, the caller should delay waiting for a response.
6647c478bd9Sstevel@tonic-gate    Otherwise, the caller should immediately move on to process the
6657c478bd9Sstevel@tonic-gate    next connection.  */
6667c478bd9Sstevel@tonic-gate static int
6677c478bd9Sstevel@tonic-gate maybe_send (struct conn_state *conn, struct select_state *selstate)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate     sg_buf *sg;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate     /*LINTED*/
6727c478bd9Sstevel@tonic-gate     dprint("maybe_send(@%p) state=%s type=%s\n", conn,
6737c478bd9Sstevel@tonic-gate 	   /*LINTED*/
6747c478bd9Sstevel@tonic-gate 	   state_strings[conn->state], conn->is_udp ? "udp" : "tcp");
6757c478bd9Sstevel@tonic-gate     if (conn->state == INITIALIZING)
6767c478bd9Sstevel@tonic-gate 	return start_connection(conn, selstate);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate     /* Did we already shut down this channel?  */
6797c478bd9Sstevel@tonic-gate     if (conn->state == FAILED) {
6807c478bd9Sstevel@tonic-gate 	dprint("connection already closed\n");
6817c478bd9Sstevel@tonic-gate 	return -1;
6827c478bd9Sstevel@tonic-gate     }
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate     if (conn->addr->ai_socktype == SOCK_STREAM) {
6857c478bd9Sstevel@tonic-gate 	dprint("skipping stream socket\n");
6867c478bd9Sstevel@tonic-gate 	/* The select callback will handle flushing any data we
6877c478bd9Sstevel@tonic-gate 	   haven't written yet, and we only write it once.  */
6887c478bd9Sstevel@tonic-gate 	return -1;
6897c478bd9Sstevel@tonic-gate     }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate     /* UDP - Send message, possibly for the first time, possibly a
6927c478bd9Sstevel@tonic-gate        retransmit if a previous attempt timed out.  */
6937c478bd9Sstevel@tonic-gate     sg = &conn->x.out.sgbuf[0];
6947c478bd9Sstevel@tonic-gate     /*LINTED*/
6957c478bd9Sstevel@tonic-gate     dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
6967c478bd9Sstevel@tonic-gate     if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) {
6977c478bd9Sstevel@tonic-gate 	dperror("send");
6987c478bd9Sstevel@tonic-gate 	/* Keep connection alive, we'll try again next pass.
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	   Is this likely to catch any errors we didn't get from the
7017c478bd9Sstevel@tonic-gate 	   select callbacks?  */
7027c478bd9Sstevel@tonic-gate 	return -1;
7037c478bd9Sstevel@tonic-gate     }
7047c478bd9Sstevel@tonic-gate     /* Yay, it worked.  */
7057c478bd9Sstevel@tonic-gate     return 0;
7067c478bd9Sstevel@tonic-gate }
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate static void
7097c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
7107c478bd9Sstevel@tonic-gate {
7117c478bd9Sstevel@tonic-gate     conn->state = FAILED;
7127c478bd9Sstevel@tonic-gate     shutdown(conn->fd, SHUTDOWN_BOTH);
7137c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->rfds);
7147c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->wfds);
7157c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->xfds);
7167c478bd9Sstevel@tonic-gate     conn->err = err;
7177c478bd9Sstevel@tonic-gate     /*LINTED*/
7187c478bd9Sstevel@tonic-gate     dprint("abandoning connection %d: %m\n", conn->fd, err);
7197c478bd9Sstevel@tonic-gate     /* Fix up max fd for next select call.  */
7207c478bd9Sstevel@tonic-gate     if (selstate->max == 1 + conn->fd) {
7217c478bd9Sstevel@tonic-gate 	while (selstate->max > 0
7227c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->rfds)
7237c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->wfds)
7247c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->xfds))
7257c478bd9Sstevel@tonic-gate 	    selstate->max--;
7267c478bd9Sstevel@tonic-gate 	/*LINTED*/
7277c478bd9Sstevel@tonic-gate 	dprint("new max_fd + 1 is %d\n", selstate->max);
7287c478bd9Sstevel@tonic-gate     }
7297c478bd9Sstevel@tonic-gate     selstate->nfds--;
7307c478bd9Sstevel@tonic-gate }
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit
7337c478bd9Sstevel@tonic-gate    its loop.  This happens in two cases: We have a complete message,
7347c478bd9Sstevel@tonic-gate    or the socket has closed and no others are open.  */
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate static int
7377c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
7387c478bd9Sstevel@tonic-gate 		int ssflags)
7397c478bd9Sstevel@tonic-gate {
7407c478bd9Sstevel@tonic-gate     krb5_error_code e = 0;
7417c478bd9Sstevel@tonic-gate     int nwritten, nread;
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate     if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
7447c478bd9Sstevel@tonic-gate 	abort();
7457c478bd9Sstevel@tonic-gate     switch (conn->state) {
7467c478bd9Sstevel@tonic-gate 	SOCKET_WRITEV_TEMP tmp;
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate     case CONNECTING:
7497c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_READ) {
7507c478bd9Sstevel@tonic-gate 	    /* Bad -- the KDC shouldn't be sending to us first.  */
7517c478bd9Sstevel@tonic-gate 	    e = EINVAL /* ?? */;
7527c478bd9Sstevel@tonic-gate 	kill_conn:
7537c478bd9Sstevel@tonic-gate 	    kill_conn(conn, selstate, e);
7547c478bd9Sstevel@tonic-gate 	    if (e == EINVAL) {
7557c478bd9Sstevel@tonic-gate 		closesocket(conn->fd);
7567c478bd9Sstevel@tonic-gate 		conn->fd = INVALID_SOCKET;
7577c478bd9Sstevel@tonic-gate 	    }
7587c478bd9Sstevel@tonic-gate 	    return e == 0;
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION) {
7617c478bd9Sstevel@tonic-gate 	handle_exception:
7627c478bd9Sstevel@tonic-gate 	    e = 1;		/* need only be non-zero */
7637c478bd9Sstevel@tonic-gate 	    goto kill_conn;
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	/*
7677c478bd9Sstevel@tonic-gate 	 * Connect finished -- but did it succeed or fail?
7687c478bd9Sstevel@tonic-gate 	 * UNIX sets can_write if failed.
7697c478bd9Sstevel@tonic-gate 	 * Try writing, I guess, and find out.
7707c478bd9Sstevel@tonic-gate 	 */
7717c478bd9Sstevel@tonic-gate 	conn->state = WRITING;
7727c478bd9Sstevel@tonic-gate 	goto try_writing;
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate     case WRITING:
7757c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_READ) {
7767c478bd9Sstevel@tonic-gate 	    e = E2BIG;
7777c478bd9Sstevel@tonic-gate 	    /* Bad -- the KDC shouldn't be sending anything yet.  */
7787c478bd9Sstevel@tonic-gate 	    goto kill_conn;
7797c478bd9Sstevel@tonic-gate 	}
7807c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION)
7817c478bd9Sstevel@tonic-gate 	    goto handle_exception;
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate     try_writing:
7847c478bd9Sstevel@tonic-gate 	/*LINTED*/
7857c478bd9Sstevel@tonic-gate 	dprint("trying to writev %d (%d bytes) to fd %d\n",
7867c478bd9Sstevel@tonic-gate 		/*LINTED*/
7877c478bd9Sstevel@tonic-gate 	       conn->x.out.sg_count,
7887c478bd9Sstevel@tonic-gate 	       ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
7897c478bd9Sstevel@tonic-gate 		/*LINTED*/
7907c478bd9Sstevel@tonic-gate 		+ SG_LEN(&conn->x.out.sgp[0])),
7917c478bd9Sstevel@tonic-gate 	       conn->fd);
7927c478bd9Sstevel@tonic-gate 	nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
7937c478bd9Sstevel@tonic-gate 				 conn->x.out.sg_count, tmp);
7947c478bd9Sstevel@tonic-gate 	if (nwritten < 0) {
7957c478bd9Sstevel@tonic-gate 	    e = SOCKET_ERRNO;
7967c478bd9Sstevel@tonic-gate 	    /*LINTED*/
7977c478bd9Sstevel@tonic-gate 	    dprint("failed: %m\n", e);
7987c478bd9Sstevel@tonic-gate 	    goto kill_conn;
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 	/*LINTED*/
8017c478bd9Sstevel@tonic-gate 	dprint("wrote %d bytes\n", nwritten);
8027c478bd9Sstevel@tonic-gate 	while (nwritten) {
8037c478bd9Sstevel@tonic-gate 	    sg_buf *sgp = conn->x.out.sgp;
8047c478bd9Sstevel@tonic-gate 	    if (nwritten < SG_LEN(sgp)) {
8057c478bd9Sstevel@tonic-gate 		/*LINTED*/
8067c478bd9Sstevel@tonic-gate 		SG_ADVANCE(sgp, nwritten);
8077c478bd9Sstevel@tonic-gate 		nwritten = 0;
8087c478bd9Sstevel@tonic-gate 	    } else {
8097c478bd9Sstevel@tonic-gate 		nwritten -= SG_LEN(conn->x.out.sgp);
8107c478bd9Sstevel@tonic-gate 		conn->x.out.sgp++;
8117c478bd9Sstevel@tonic-gate 		conn->x.out.sg_count--;
8127c478bd9Sstevel@tonic-gate 		if (conn->x.out.sg_count == 0 && nwritten != 0)
8137c478bd9Sstevel@tonic-gate 		    /* Wrote more than we wanted to?  */
8147c478bd9Sstevel@tonic-gate 		    abort();
8157c478bd9Sstevel@tonic-gate 	    }
8167c478bd9Sstevel@tonic-gate 	}
8177c478bd9Sstevel@tonic-gate 	if (conn->x.out.sg_count == 0) {
8187c478bd9Sstevel@tonic-gate 	    /* Done writing, switch to reading.  */
8197c478bd9Sstevel@tonic-gate 	    /* Don't call shutdown at this point because
8207c478bd9Sstevel@tonic-gate 	     * some implementations cannot deal with half-closed connections.*/
8217c478bd9Sstevel@tonic-gate 	    FD_CLR(conn->fd, &selstate->wfds);
8227c478bd9Sstevel@tonic-gate 	    /* Q: How do we detect failures to send the remaining data
8237c478bd9Sstevel@tonic-gate 	       to the remote side, since we're in non-blocking mode?
8247c478bd9Sstevel@tonic-gate 	       Will we always get errors on the reading side?  */
8257c478bd9Sstevel@tonic-gate 	    /*LINTED*/
8267c478bd9Sstevel@tonic-gate 	    dprint("switching fd %d to READING\n", conn->fd);
8277c478bd9Sstevel@tonic-gate 	    conn->state = READING;
8287c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsizebytes_read = 0;
8297c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsize = 0;
8307c478bd9Sstevel@tonic-gate 	    conn->x.in.buf = 0;
8317c478bd9Sstevel@tonic-gate 	    conn->x.in.pos = 0;
8327c478bd9Sstevel@tonic-gate 	    conn->x.in.n_left = 0;
8337c478bd9Sstevel@tonic-gate 	}
8347c478bd9Sstevel@tonic-gate 	return 0;
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate     case READING:
8377c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION) {
8387c478bd9Sstevel@tonic-gate 	    if (conn->x.in.buf) {
8397c478bd9Sstevel@tonic-gate 		free(conn->x.in.buf);
8407c478bd9Sstevel@tonic-gate 		conn->x.in.buf = 0;
8417c478bd9Sstevel@tonic-gate 	    }
8427c478bd9Sstevel@tonic-gate 	    goto handle_exception;
8437c478bd9Sstevel@tonic-gate 	}
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate 	if (conn->x.in.bufsizebytes_read == 4) {
8467c478bd9Sstevel@tonic-gate 	    /* Reading data.  */
8477c478bd9Sstevel@tonic-gate 	    /*LINTED*/
8487c478bd9Sstevel@tonic-gate 	    dprint("reading %d bytes of data from fd %d\n",
8497c478bd9Sstevel@tonic-gate 		   (int) conn->x.in.n_left, conn->fd);
8507c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
8517c478bd9Sstevel@tonic-gate 	    if (nread <= 0) {
8527c478bd9Sstevel@tonic-gate 		e = nread ? SOCKET_ERRNO : ECONNRESET;
8537c478bd9Sstevel@tonic-gate 		free(conn->x.in.buf);
8547c478bd9Sstevel@tonic-gate 		conn->x.in.buf = 0;
8557c478bd9Sstevel@tonic-gate 		goto kill_conn;
8567c478bd9Sstevel@tonic-gate 	    }
8577c478bd9Sstevel@tonic-gate 	    conn->x.in.n_left -= nread;
8587c478bd9Sstevel@tonic-gate 	    conn->x.in.pos += nread;
8597c478bd9Sstevel@tonic-gate 	    if ((long)conn->x.in.n_left <= 0) {
8607c478bd9Sstevel@tonic-gate 		/* We win!  */
8617c478bd9Sstevel@tonic-gate 		return 1;
8627c478bd9Sstevel@tonic-gate 	    }
8637c478bd9Sstevel@tonic-gate 	} else {
8647c478bd9Sstevel@tonic-gate 	    /* Reading length.  */
8657c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
8667c478bd9Sstevel@tonic-gate 				conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
8677c478bd9Sstevel@tonic-gate 				4 - conn->x.in.bufsizebytes_read);
8687c478bd9Sstevel@tonic-gate 	    if (nread < 0) {
8697c478bd9Sstevel@tonic-gate 		e = SOCKET_ERRNO;
8707c478bd9Sstevel@tonic-gate 		goto kill_conn;
8717c478bd9Sstevel@tonic-gate 	    }
8727c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsizebytes_read += nread;
8737c478bd9Sstevel@tonic-gate 	    if (conn->x.in.bufsizebytes_read == 4) {
8747c478bd9Sstevel@tonic-gate 		unsigned long len;
8757c478bd9Sstevel@tonic-gate 		len = conn->x.in.bufsizebytes[0];
8767c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[1];
8777c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[2];
8787c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[3];
8797c478bd9Sstevel@tonic-gate 		/*LINTED*/
8807c478bd9Sstevel@tonic-gate 		dprint("received length on fd %d is %d\n", conn->fd, (int)len);
8817c478bd9Sstevel@tonic-gate 		/* Arbitrary 1M cap.  */
8827c478bd9Sstevel@tonic-gate 		if (len > 1 * 1024 * 1024) {
8837c478bd9Sstevel@tonic-gate 		    e = E2BIG;
8847c478bd9Sstevel@tonic-gate 		    goto kill_conn;
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 		conn->x.in.bufsize = conn->x.in.n_left = len;
8877c478bd9Sstevel@tonic-gate 		conn->x.in.buf = conn->x.in.pos = malloc(len);
8887c478bd9Sstevel@tonic-gate 		/*LINTED*/
8897c478bd9Sstevel@tonic-gate 		dprint("allocated %d byte buffer at %p\n", (int) len,
8907c478bd9Sstevel@tonic-gate 		       conn->x.in.buf);
8917c478bd9Sstevel@tonic-gate 		if (conn->x.in.buf == 0) {
8927c478bd9Sstevel@tonic-gate 		    /* allocation failure */
8937c478bd9Sstevel@tonic-gate 		    e = errno;
8947c478bd9Sstevel@tonic-gate 		    goto kill_conn;
8957c478bd9Sstevel@tonic-gate 		}
8967c478bd9Sstevel@tonic-gate 	    }
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 	break;
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate     default:
9017c478bd9Sstevel@tonic-gate 	abort();
9027c478bd9Sstevel@tonic-gate     }
9037c478bd9Sstevel@tonic-gate     return 0;
9047c478bd9Sstevel@tonic-gate }
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate static int
9077c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate,
9087c478bd9Sstevel@tonic-gate 	       int ssflags)
9097c478bd9Sstevel@tonic-gate {
9107c478bd9Sstevel@tonic-gate     int nread;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate     if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
9137c478bd9Sstevel@tonic-gate 	abort();
9147c478bd9Sstevel@tonic-gate     if (conn->state != READING)
9157c478bd9Sstevel@tonic-gate 	abort();
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate     nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
9187c478bd9Sstevel@tonic-gate     if (nread < 0) {
9197c478bd9Sstevel@tonic-gate 	kill_conn(conn, selstate, SOCKET_ERRNO);
9207c478bd9Sstevel@tonic-gate 	return 0;
9217c478bd9Sstevel@tonic-gate     }
9227c478bd9Sstevel@tonic-gate     conn->x.in.pos = conn->x.in.buf + nread;
9237c478bd9Sstevel@tonic-gate     return 1;
9247c478bd9Sstevel@tonic-gate }
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate static int
9277c478bd9Sstevel@tonic-gate service_fds (struct select_state *selstate,
9287c478bd9Sstevel@tonic-gate 	     struct conn_state *conns, size_t n_conns, int *winning_conn)
9297c478bd9Sstevel@tonic-gate {
9307c478bd9Sstevel@tonic-gate     int e, selret;
9317c478bd9Sstevel@tonic-gate     struct select_state sel_results;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate     e = 0;
9347c478bd9Sstevel@tonic-gate     while (selstate->nfds > 0
9357c478bd9Sstevel@tonic-gate 	   && (e = krb5int_cm_call_select(selstate, &sel_results, &selret)) == 0) {
9367c478bd9Sstevel@tonic-gate 	int i;
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	/*LINTED*/
9397c478bd9Sstevel@tonic-gate 	dprint("service_fds examining results, selret=%d\n", selret);
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	if (selret == 0)
9427c478bd9Sstevel@tonic-gate 	    /* Timeout, return to caller.  */
9437c478bd9Sstevel@tonic-gate 	    return 0;
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	/* Got something on a socket, process it.  */
9467c478bd9Sstevel@tonic-gate 	for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) {
9477c478bd9Sstevel@tonic-gate 	    int ssflags;
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 	    if (conns[i].fd == INVALID_SOCKET)
9507c478bd9Sstevel@tonic-gate 		continue;
9517c478bd9Sstevel@tonic-gate 	    ssflags = 0;
9527c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i].fd, &sel_results.rfds))
9537c478bd9Sstevel@tonic-gate 		ssflags |= SSF_READ, selret--;
9547c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i].fd, &sel_results.wfds))
9557c478bd9Sstevel@tonic-gate 		ssflags |= SSF_WRITE, selret--;
9567c478bd9Sstevel@tonic-gate 	    if (FD_ISSET(conns[i].fd, &sel_results.xfds))
9577c478bd9Sstevel@tonic-gate 		ssflags |= SSF_EXCEPTION, selret--;
9587c478bd9Sstevel@tonic-gate 	    if (!ssflags)
9597c478bd9Sstevel@tonic-gate 		continue;
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	    /*LINTED*/
9627c478bd9Sstevel@tonic-gate 	    dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
9637c478bd9Sstevel@tonic-gate 		    /*LINTED*/
9647c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_READ) ? "r" : "",
9657c478bd9Sstevel@tonic-gate 		    /*LINTED*/
9667c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_WRITE) ? "w" : "",
9677c478bd9Sstevel@tonic-gate 		    /*LINTED*/
9687c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_EXCEPTION) ? "x" : "",
9697c478bd9Sstevel@tonic-gate 		    /*LINTED*/
9707c478bd9Sstevel@tonic-gate 		   conns[i].fd, conns[i].addr,
9717c478bd9Sstevel@tonic-gate 		   state_strings[(int) conns[i].state]);
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	    if (conns[i].service (&conns[i], selstate, ssflags)) {
9747c478bd9Sstevel@tonic-gate 		dprint("fd service routine says we're done\n");
9757c478bd9Sstevel@tonic-gate 		*winning_conn = i;
9767c478bd9Sstevel@tonic-gate 		return 1;
9777c478bd9Sstevel@tonic-gate 	    }
9787c478bd9Sstevel@tonic-gate 	}
9797c478bd9Sstevel@tonic-gate     }
9807c478bd9Sstevel@tonic-gate     if (e != 0) {
9817c478bd9Sstevel@tonic-gate 	/*LINTED*/
9827c478bd9Sstevel@tonic-gate 	dprint("select returned %m\n", e);
9837c478bd9Sstevel@tonic-gate 	*winning_conn = -1;
9847c478bd9Sstevel@tonic-gate 	return 1;
9857c478bd9Sstevel@tonic-gate     }
9867c478bd9Sstevel@tonic-gate     return 0;
9877c478bd9Sstevel@tonic-gate }
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate /*
9907c478bd9Sstevel@tonic-gate  * Current worst-case timeout behavior:
9917c478bd9Sstevel@tonic-gate  *
9927c478bd9Sstevel@tonic-gate  * First pass, 1s per udp or tcp server, plus 2s at end.
9937c478bd9Sstevel@tonic-gate  * Second pass, 1s per udp server, plus 4s.
9947c478bd9Sstevel@tonic-gate  * Third pass, 1s per udp server, plus 8s.
9957c478bd9Sstevel@tonic-gate  * Fourth => 16s, etc.
9967c478bd9Sstevel@tonic-gate  *
9977c478bd9Sstevel@tonic-gate  * Restated:
9987c478bd9Sstevel@tonic-gate  * Per UDP server, 1s per pass.
9997c478bd9Sstevel@tonic-gate  * Per TCP server, 1s.
10007c478bd9Sstevel@tonic-gate  * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
10017c478bd9Sstevel@tonic-gate  *
10027c478bd9Sstevel@tonic-gate  * Total = 2**(P+1) + U*P + T - 2.
10037c478bd9Sstevel@tonic-gate  *
10047c478bd9Sstevel@tonic-gate  * If P=3, Total = 3*U + T + 14.
10057c478bd9Sstevel@tonic-gate  * If P=4, Total = 4*U + T + 30.
10067c478bd9Sstevel@tonic-gate  *
10077c478bd9Sstevel@tonic-gate  * Note that if you try to reach two ports (e.g., both 88 and 750) on
10087c478bd9Sstevel@tonic-gate  * one server, it counts as two.
10097c478bd9Sstevel@tonic-gate  */
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate krb5_error_code
10127c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10137c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message,
10147c478bd9Sstevel@tonic-gate 		const struct addrlist *addrs, krb5_data *reply,
1015*505d05c7Sgtb 		struct sockaddr_storage *localaddr, socklen_t *localaddrlen,
1016*505d05c7Sgtb 		int *addr_used)
10177c478bd9Sstevel@tonic-gate {
10187c478bd9Sstevel@tonic-gate     int i, pass;
10197c478bd9Sstevel@tonic-gate     int delay_this_pass = 2;
10207c478bd9Sstevel@tonic-gate     krb5_error_code retval;
10217c478bd9Sstevel@tonic-gate     struct conn_state *conns;
10227c478bd9Sstevel@tonic-gate     size_t n_conns, host;
10237c478bd9Sstevel@tonic-gate     struct select_state select_state;
10247c478bd9Sstevel@tonic-gate     struct timeval now;
10257c478bd9Sstevel@tonic-gate     int winning_conn = -1, e = 0;
10267c478bd9Sstevel@tonic-gate     unsigned char message_len_buf[4];
10277c478bd9Sstevel@tonic-gate     char *udpbuf = 0;
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate     /*LINTED*/
10307c478bd9Sstevel@tonic-gate     dprint("krb5int_sendto(message=%d@%p)\n", message->length, message->data);
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate     reply->data = 0;
10337c478bd9Sstevel@tonic-gate     reply->length = 0;
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate     n_conns = addrs->naddrs;
10367c478bd9Sstevel@tonic-gate     conns = malloc(n_conns * sizeof(struct conn_state));
10377c478bd9Sstevel@tonic-gate     if (conns == NULL) {
10387c478bd9Sstevel@tonic-gate 	return ENOMEM;
10397c478bd9Sstevel@tonic-gate     }
10407c478bd9Sstevel@tonic-gate     memset(conns, 0, n_conns * sizeof(conns[i]));
10417c478bd9Sstevel@tonic-gate     for (i = 0; i < n_conns; i++) {
10427c478bd9Sstevel@tonic-gate 	conns[i].fd = INVALID_SOCKET;
10437c478bd9Sstevel@tonic-gate     }
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate     select_state.max = 0;
10467c478bd9Sstevel@tonic-gate     select_state.nfds = 0;
10477c478bd9Sstevel@tonic-gate     FD_ZERO(&select_state.rfds);
10487c478bd9Sstevel@tonic-gate     FD_ZERO(&select_state.wfds);
10497c478bd9Sstevel@tonic-gate     FD_ZERO(&select_state.xfds);
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate     message_len_buf[0] = (message->length >> 24) & 0xff;
10527c478bd9Sstevel@tonic-gate     message_len_buf[1] = (message->length >> 16) & 0xff;
10537c478bd9Sstevel@tonic-gate     message_len_buf[2] = (message->length >>  8) & 0xff;
10547c478bd9Sstevel@tonic-gate     message_len_buf[3] =  message->length        & 0xff;
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate     /* Set up connections.  */
10577c478bd9Sstevel@tonic-gate     for (host = 0; host < n_conns; host++) {
10587c478bd9Sstevel@tonic-gate 	retval = setup_connection (&conns[host], addrs->addrs[host],
10597c478bd9Sstevel@tonic-gate 				   message, message_len_buf, &udpbuf);
10607c478bd9Sstevel@tonic-gate 	if (retval)
10617c478bd9Sstevel@tonic-gate 	    continue;
10627c478bd9Sstevel@tonic-gate     }
10637c478bd9Sstevel@tonic-gate     for (pass = 0; pass < MAX_PASS; pass++) {
10647c478bd9Sstevel@tonic-gate 	/* Possible optimization: Make only one pass if TCP only.
10657c478bd9Sstevel@tonic-gate 	   Stop making passes if all UDP ports are closed down.  */
10667c478bd9Sstevel@tonic-gate 	/*LINTED*/
10677c478bd9Sstevel@tonic-gate 	dprint("pass %d delay=%d\n", pass, delay_this_pass);
10687c478bd9Sstevel@tonic-gate 	for (host = 0; host < n_conns; host++) {
10697c478bd9Sstevel@tonic-gate 	    /*LINTED*/
10707c478bd9Sstevel@tonic-gate 	    dprint("host %d\n", host);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	    /* Send to the host, wait for a response, then move on. */
10737c478bd9Sstevel@tonic-gate 	    if (maybe_send(&conns[host], &select_state))
10747c478bd9Sstevel@tonic-gate 		continue;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	    retval = getcurtime(&now);
10777c478bd9Sstevel@tonic-gate 	    if (retval)
10787c478bd9Sstevel@tonic-gate 		goto egress;
10797c478bd9Sstevel@tonic-gate 	    select_state.end_time = now;
10807c478bd9Sstevel@tonic-gate 	    select_state.end_time.tv_sec += 1;
10817c478bd9Sstevel@tonic-gate 	    e = service_fds(&select_state, conns, host+1, &winning_conn);
10827c478bd9Sstevel@tonic-gate 	    if (e)
10837c478bd9Sstevel@tonic-gate 		break;
10847c478bd9Sstevel@tonic-gate 	    if (pass > 0 && select_state.nfds == 0)
10857c478bd9Sstevel@tonic-gate 		/*
10867c478bd9Sstevel@tonic-gate 		 * After the first pass, if we close all fds, break
10877c478bd9Sstevel@tonic-gate 		 * out right away.  During the first pass, it's okay,
10887c478bd9Sstevel@tonic-gate 		 * we're probably about to open another connection.
10897c478bd9Sstevel@tonic-gate 		 */
10907c478bd9Sstevel@tonic-gate 		break;
10917c478bd9Sstevel@tonic-gate 	}
10927c478bd9Sstevel@tonic-gate 	if (e)
10937c478bd9Sstevel@tonic-gate 	    break;
10947c478bd9Sstevel@tonic-gate 	retval = getcurtime(&now);
10957c478bd9Sstevel@tonic-gate 	if (retval)
10967c478bd9Sstevel@tonic-gate 	    goto egress;
10977c478bd9Sstevel@tonic-gate 	/* Possible optimization: Find a way to integrate this select
10987c478bd9Sstevel@tonic-gate 	   call with the last one from the above loop, if the loop
10997c478bd9Sstevel@tonic-gate 	   actually calls select.  */
11007c478bd9Sstevel@tonic-gate 	select_state.end_time.tv_sec += delay_this_pass;
11017c478bd9Sstevel@tonic-gate 	e = service_fds(&select_state, conns, host+1, &winning_conn);
11027c478bd9Sstevel@tonic-gate 	if (e)
11037c478bd9Sstevel@tonic-gate 	    break;
11047c478bd9Sstevel@tonic-gate 	if (select_state.nfds == 0)
11057c478bd9Sstevel@tonic-gate 	    break;
11067c478bd9Sstevel@tonic-gate 	delay_this_pass *= 2;
11077c478bd9Sstevel@tonic-gate     }
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate     if (select_state.nfds == 0) {
11107c478bd9Sstevel@tonic-gate 	/* No addresses?  */
11117c478bd9Sstevel@tonic-gate 	retval = KRB5_KDC_UNREACH;
11127c478bd9Sstevel@tonic-gate 	goto egress;
11137c478bd9Sstevel@tonic-gate     }
11147c478bd9Sstevel@tonic-gate     if (e == 0 || winning_conn < 0) {
11157c478bd9Sstevel@tonic-gate 	retval = KRB5_KDC_UNREACH;
11167c478bd9Sstevel@tonic-gate 	goto egress;
11177c478bd9Sstevel@tonic-gate     }
11187c478bd9Sstevel@tonic-gate     /* Success!  */
11197c478bd9Sstevel@tonic-gate     reply->data = conns[winning_conn].x.in.buf;
11207c478bd9Sstevel@tonic-gate     reply->length = (conns[winning_conn].x.in.pos
11217c478bd9Sstevel@tonic-gate 		     - conns[winning_conn].x.in.buf);
11227c478bd9Sstevel@tonic-gate     /*LINTED*/
1123*505d05c7Sgtb     dprint("returning %d bytes in buffer %p (winning_conn=%d)\n",
1124*505d05c7Sgtb 	(int) reply->length, reply->data, winning_conn);
11257c478bd9Sstevel@tonic-gate     retval = 0;
11267c478bd9Sstevel@tonic-gate     conns[winning_conn].x.in.buf = 0;
1127*505d05c7Sgtb     if (addr_used)
1128*505d05c7Sgtb 	    *addr_used = winning_conn;
11297c478bd9Sstevel@tonic-gate     if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
11307c478bd9Sstevel@tonic-gate 	(void) getsockname(conns[winning_conn].fd, (struct sockaddr *)localaddr,
11317c478bd9Sstevel@tonic-gate 			   localaddrlen);
11327c478bd9Sstevel@tonic-gate egress:
11337c478bd9Sstevel@tonic-gate     for (i = 0; i < n_conns; i++) {
11347c478bd9Sstevel@tonic-gate 	if (conns[i].fd != INVALID_SOCKET)
11357c478bd9Sstevel@tonic-gate 	    close(conns[i].fd);
11367c478bd9Sstevel@tonic-gate 	if (conns[i].state == READING
11377c478bd9Sstevel@tonic-gate 	    && conns[i].x.in.buf != 0
11387c478bd9Sstevel@tonic-gate 	    && conns[i].x.in.buf != udpbuf)
11397c478bd9Sstevel@tonic-gate 	    free(conns[i].x.in.buf);
11407c478bd9Sstevel@tonic-gate     }
11417c478bd9Sstevel@tonic-gate     free(conns);
11427c478bd9Sstevel@tonic-gate     if (reply->data != udpbuf)
11437c478bd9Sstevel@tonic-gate 	free(udpbuf);
11447c478bd9Sstevel@tonic-gate     return retval;
11457c478bd9Sstevel@tonic-gate }
1146