17c478bd9Sstevel@tonic-gate /*
2*56bbb0b2SPeter Shoults  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate  */
47c478bd9Sstevel@tonic-gate /*
57c478bd9Sstevel@tonic-gate  * lib/krb5/os/sendto_kdc.c
67c478bd9Sstevel@tonic-gate  *
7159d09a2SMark Phalan  * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology.
87c478bd9Sstevel@tonic-gate  * All Rights Reserved.
97c478bd9Sstevel@tonic-gate  *
107c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
117c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
127c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
137c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
147c478bd9Sstevel@tonic-gate  *
157c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
167c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
177c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
187c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
197c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
207c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
217c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
227c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
237c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
247c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
257c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
267c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
277c478bd9Sstevel@tonic-gate  * or implied warranty.
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * Send packet to KDC for realm; wait for response, retransmitting
317c478bd9Sstevel@tonic-gate  * as necessary.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
34159d09a2SMark Phalan #include "fake-addrinfo.h"
35159d09a2SMark Phalan #include "k5-int.h"
36159d09a2SMark Phalan 
37159d09a2SMark Phalan /* Solaris Kerberos */
38159d09a2SMark Phalan #include <syslog.h>
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H
417c478bd9Sstevel@tonic-gate #include <sys/time.h>
427c478bd9Sstevel@tonic-gate #else
437c478bd9Sstevel@tonic-gate #include <time.h>
447c478bd9Sstevel@tonic-gate #endif
457c478bd9Sstevel@tonic-gate #include "os-proto.h"
46159d09a2SMark Phalan #ifdef _WIN32
47159d09a2SMark Phalan #include <sys/timeb.h>
48159d09a2SMark Phalan #endif
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #ifdef _AIX
517c478bd9Sstevel@tonic-gate #include <sys/select.h>
527c478bd9Sstevel@tonic-gate #endif
537c478bd9Sstevel@tonic-gate 
54159d09a2SMark Phalan #ifndef _WIN32
557c478bd9Sstevel@tonic-gate /* For FIONBIO.  */
567c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
577c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H
587c478bd9Sstevel@tonic-gate #include <sys/filio.h>
597c478bd9Sstevel@tonic-gate #endif
60159d09a2SMark Phalan #endif
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #define MAX_PASS		    3
637c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */
647c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT	 1465 */
657c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT		32700 /* could probably do 64K-epsilon ? */
667c478bd9Sstevel@tonic-gate 
67159d09a2SMark Phalan #undef DEBUG
68159d09a2SMark Phalan 
69159d09a2SMark Phalan #ifdef DEBUG
70159d09a2SMark Phalan int krb5int_debug_sendto_kdc = 0;
71159d09a2SMark Phalan #define debug krb5int_debug_sendto_kdc
72159d09a2SMark Phalan 
737c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len)
747c478bd9Sstevel@tonic-gate {
75159d09a2SMark Phalan #if 0
76159d09a2SMark Phalan     FILE *logfile;
77159d09a2SMark Phalan     logfile = fopen("/tmp/sendto_kdc.log", "a");
78159d09a2SMark Phalan     if (logfile == NULL)
79159d09a2SMark Phalan 	return;
80159d09a2SMark Phalan     fwrite(data, 1, len, logfile);
81159d09a2SMark Phalan     fclose(logfile);
82159d09a2SMark Phalan #else
837c478bd9Sstevel@tonic-gate     fwrite(data, 1, len, stderr);
847c478bd9Sstevel@tonic-gate     /* stderr is unbuffered */
85159d09a2SMark Phalan #endif
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * Solaris Kerberos: only including the debug stuff if DEBUG defined outside
927c478bd9Sstevel@tonic-gate  * this file.
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024];
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */
97159d09a2SMark Phalan #if 0
98159d09a2SMark Phalan static void put(const void *ptr, size_t len)
99159d09a2SMark Phalan {
100159d09a2SMark Phalan     (*krb5int_sendtokdc_debug_handler)(ptr, len);
101159d09a2SMark Phalan }
102159d09a2SMark Phalan #endif
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate static void putstr(const char *str)
1057c478bd9Sstevel@tonic-gate {
1067c478bd9Sstevel@tonic-gate     /* Solaris kerberos: build the string which will be passed to syslog later */
1077c478bd9Sstevel@tonic-gate     strlcat(global_err_str, str, sizeof (global_err_str));
1087c478bd9Sstevel@tonic-gate }
109159d09a2SMark Phalan #else
110159d09a2SMark Phalan void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0;
111159d09a2SMark Phalan #endif
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint
114159d09a2SMark Phalan  void
1157c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...)
1167c478bd9Sstevel@tonic-gate {
117159d09a2SMark Phalan #ifdef DEBUG
1187c478bd9Sstevel@tonic-gate     va_list args;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate     /* Temporaries for variable arguments, etc.  */
1217c478bd9Sstevel@tonic-gate     krb5_error_code kerr;
1227c478bd9Sstevel@tonic-gate     int err;
1237c478bd9Sstevel@tonic-gate     fd_set *rfds, *wfds, *xfds;
1247c478bd9Sstevel@tonic-gate     int i;
1257c478bd9Sstevel@tonic-gate     int maxfd;
1267c478bd9Sstevel@tonic-gate     struct timeval *tv;
1277c478bd9Sstevel@tonic-gate     struct addrinfo *ai;
1287c478bd9Sstevel@tonic-gate     const krb5_data *d;
1297c478bd9Sstevel@tonic-gate     char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
1307c478bd9Sstevel@tonic-gate     const char *p;
131159d09a2SMark Phalan #ifndef max
132159d09a2SMark Phalan #define max(a,b) ((a) > (b) ? (a) : (b))
133159d09a2SMark Phalan #endif
134159d09a2SMark Phalan     char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)];
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate     /*
1377c478bd9Sstevel@tonic-gate      * Solaris kerberos: modified this function to create a string to pass to
1387c478bd9Sstevel@tonic-gate      * syslog()
1397c478bd9Sstevel@tonic-gate      */
1407c478bd9Sstevel@tonic-gate     global_err_str[0] = NULL;
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate     va_start(args, fmt);
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate #define putf(FMT,X)	(sprintf(tmpbuf,FMT,X),putstr(tmpbuf))
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate     for (; *fmt; fmt++) {
1477c478bd9Sstevel@tonic-gate 	if (*fmt != '%') {
1487c478bd9Sstevel@tonic-gate 	    /* Possible optimization: Look for % and print all chars
1497c478bd9Sstevel@tonic-gate 	       up to it in one call.  */
1507c478bd9Sstevel@tonic-gate 	    putf("%c", *fmt);
1517c478bd9Sstevel@tonic-gate 	    continue;
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 	/* After this, always processing a '%' sequence.  */
1547c478bd9Sstevel@tonic-gate 	fmt++;
1557c478bd9Sstevel@tonic-gate 	switch (*fmt) {
1567c478bd9Sstevel@tonic-gate 	case 0:
1577c478bd9Sstevel@tonic-gate 	default:
1587c478bd9Sstevel@tonic-gate 	    abort();
1597c478bd9Sstevel@tonic-gate 	case 'E':
1607c478bd9Sstevel@tonic-gate 	    /* %E => krb5_error_code */
1617c478bd9Sstevel@tonic-gate 	    kerr = va_arg(args, krb5_error_code);
1627c478bd9Sstevel@tonic-gate 	    sprintf(tmpbuf, "%lu/", (unsigned long) kerr);
1637c478bd9Sstevel@tonic-gate 	    putstr(tmpbuf);
1647c478bd9Sstevel@tonic-gate 	    p = error_message(kerr);
1657c478bd9Sstevel@tonic-gate 	    putstr(p);
1667c478bd9Sstevel@tonic-gate 	    break;
1677c478bd9Sstevel@tonic-gate 	case 'm':
1687c478bd9Sstevel@tonic-gate 	    /* %m => errno value (int) */
1697c478bd9Sstevel@tonic-gate 	    /* Like syslog's %m except the errno value is passed in
1707c478bd9Sstevel@tonic-gate 	       rather than the current value.  */
1717c478bd9Sstevel@tonic-gate 	    err = va_arg(args, int);
1727c478bd9Sstevel@tonic-gate 	    putf("%d/", err);
173159d09a2SMark Phalan 	    p = NULL;
174159d09a2SMark Phalan #ifdef HAVE_STRERROR_R
175159d09a2SMark Phalan 	    if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
176159d09a2SMark Phalan 		p = tmpbuf;
177159d09a2SMark Phalan #endif
178159d09a2SMark Phalan 	    if (p == NULL)
179159d09a2SMark Phalan 		p = strerror(err);
1807c478bd9Sstevel@tonic-gate 	    putstr(p);
1817c478bd9Sstevel@tonic-gate 	    break;
1827c478bd9Sstevel@tonic-gate 	case 'F':
1837c478bd9Sstevel@tonic-gate 	    /* %F => fd_set *, fd_set *, fd_set *, int */
1847c478bd9Sstevel@tonic-gate 	    rfds = va_arg(args, fd_set *);
1857c478bd9Sstevel@tonic-gate 	    wfds = va_arg(args, fd_set *);
1867c478bd9Sstevel@tonic-gate 	    xfds = va_arg(args, fd_set *);
1877c478bd9Sstevel@tonic-gate 	    maxfd = va_arg(args, int);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	    for (i = 0; i < maxfd; i++) {
1907c478bd9Sstevel@tonic-gate 		int r = FD_ISSET(i, rfds);
1917c478bd9Sstevel@tonic-gate 		int w = wfds && FD_ISSET(i, wfds);
1927c478bd9Sstevel@tonic-gate 		int x = xfds && FD_ISSET(i, xfds);
1937c478bd9Sstevel@tonic-gate 		if (r || w || x) {
1947c478bd9Sstevel@tonic-gate 		    putf(" %d", i);
1957c478bd9Sstevel@tonic-gate 		    if (r)
1967c478bd9Sstevel@tonic-gate 			putstr("r");
1977c478bd9Sstevel@tonic-gate 		    if (w)
1987c478bd9Sstevel@tonic-gate 			putstr("w");
1997c478bd9Sstevel@tonic-gate 		    if (x)
2007c478bd9Sstevel@tonic-gate 			putstr("x");
2017c478bd9Sstevel@tonic-gate 		}
2027c478bd9Sstevel@tonic-gate 	    }
2037c478bd9Sstevel@tonic-gate 	    putstr(" ");
2047c478bd9Sstevel@tonic-gate 	    break;
2057c478bd9Sstevel@tonic-gate 	case 's':
2067c478bd9Sstevel@tonic-gate 	    /* %s => char * */
2077c478bd9Sstevel@tonic-gate 	    p = va_arg(args, const char *);
2087c478bd9Sstevel@tonic-gate 	    putstr(p);
2097c478bd9Sstevel@tonic-gate 	    break;
2107c478bd9Sstevel@tonic-gate 	case 't':
2117c478bd9Sstevel@tonic-gate 	    /* %t => struct timeval * */
2127c478bd9Sstevel@tonic-gate 	    tv = va_arg(args, struct timeval *);
2137c478bd9Sstevel@tonic-gate 	    if (tv) {
2147c478bd9Sstevel@tonic-gate 		sprintf(tmpbuf, "%ld.%06ld",
2157c478bd9Sstevel@tonic-gate 			(long) tv->tv_sec, (long) tv->tv_usec);
2167c478bd9Sstevel@tonic-gate 		putstr(tmpbuf);
2177c478bd9Sstevel@tonic-gate 	    } else
2187c478bd9Sstevel@tonic-gate 		putstr("never");
2197c478bd9Sstevel@tonic-gate 	    break;
2207c478bd9Sstevel@tonic-gate 	case 'd':
2217c478bd9Sstevel@tonic-gate 	    /* %d => int */
2227c478bd9Sstevel@tonic-gate 	    putf("%d", va_arg(args, int));
2237c478bd9Sstevel@tonic-gate 	    break;
2247c478bd9Sstevel@tonic-gate 	case 'p':
2257c478bd9Sstevel@tonic-gate 	    /* %p => pointer */
2267c478bd9Sstevel@tonic-gate 	    putf("%p", va_arg(args, void*));
2277c478bd9Sstevel@tonic-gate 	    break;
2287c478bd9Sstevel@tonic-gate 	case 'A':
2297c478bd9Sstevel@tonic-gate 	    /* %A => addrinfo */
2307c478bd9Sstevel@tonic-gate 	    ai = va_arg(args, struct addrinfo *);
231159d09a2SMark Phalan 	    if (ai->ai_socktype == SOCK_DGRAM)
232159d09a2SMark Phalan 		strcpy(tmpbuf, "dgram");
233159d09a2SMark Phalan 	    else if (ai->ai_socktype == SOCK_STREAM)
234159d09a2SMark Phalan 		strcpy(tmpbuf, "stream");
235159d09a2SMark Phalan 	    else
236159d09a2SMark Phalan 		sprintf(tmpbuf, "socktype%d", ai->ai_socktype);
2377c478bd9Sstevel@tonic-gate 	    if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen,
2387c478bd9Sstevel@tonic-gate 				  addrbuf, sizeof (addrbuf),
2397c478bd9Sstevel@tonic-gate 				  portbuf, sizeof (portbuf),
240159d09a2SMark Phalan 				  NI_NUMERICHOST | NI_NUMERICSERV)) {
241159d09a2SMark Phalan 		if (ai->ai_addr->sa_family == AF_UNSPEC)
242159d09a2SMark Phalan 		    strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC");
243159d09a2SMark Phalan 		else
244159d09a2SMark Phalan 		    sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family);
245159d09a2SMark Phalan 	    } else
246159d09a2SMark Phalan 		sprintf(tmpbuf + strlen(tmpbuf), " %s.%s", addrbuf, portbuf);
2477c478bd9Sstevel@tonic-gate 	    putstr(tmpbuf);
2487c478bd9Sstevel@tonic-gate 	    break;
2497c478bd9Sstevel@tonic-gate 	case 'D':
2507c478bd9Sstevel@tonic-gate 	    /* %D => krb5_data * */
2517c478bd9Sstevel@tonic-gate 	    d = va_arg(args, krb5_data *);
252159d09a2SMark Phalan 	    /* Solaris Kerberos */
2537c478bd9Sstevel@tonic-gate 	    p = d->data;
2547c478bd9Sstevel@tonic-gate 	    putstr("0x");
2557c478bd9Sstevel@tonic-gate 	    for (i = 0; i < d->length; i++) {
2567c478bd9Sstevel@tonic-gate 		putf("%.2x", *p++);
2577c478bd9Sstevel@tonic-gate 	    }
2587c478bd9Sstevel@tonic-gate 	    break;
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate     }
2617c478bd9Sstevel@tonic-gate     va_end(args);
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate     /* Solaris kerberos: use syslog() for debug output */
2647c478bd9Sstevel@tonic-gate     syslog(LOG_DEBUG, global_err_str);
265159d09a2SMark Phalan #endif
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate 
268159d09a2SMark Phalan #define print_addrlist krb5int_print_addrlist
269159d09a2SMark Phalan static void
270159d09a2SMark Phalan print_addrlist (const struct addrlist *a)
271159d09a2SMark Phalan {
272159d09a2SMark Phalan     int i;
273159d09a2SMark Phalan     dprint("%d{", a->naddrs);
274159d09a2SMark Phalan     for (i = 0; i < a->naddrs; i++)
275159d09a2SMark Phalan 	dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai);
276159d09a2SMark Phalan     dprint("}");
277159d09a2SMark Phalan }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate static int
2807c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src)
2817c478bd9Sstevel@tonic-gate {
282159d09a2SMark Phalan     /* Wouldn't it be nice if we could filter out duplicates?  The
283159d09a2SMark Phalan        alloc/free handling makes that pretty difficult though.  */
2847c478bd9Sstevel@tonic-gate     int err, i;
2857c478bd9Sstevel@tonic-gate 
286159d09a2SMark Phalan /* Solaris Kerberos */
2877c478bd9Sstevel@tonic-gate #ifdef DEBUG
2887c478bd9Sstevel@tonic-gate     /*LINTED*/
2897c478bd9Sstevel@tonic-gate     dprint("merging addrlists:\n\tlist1: ");
2907c478bd9Sstevel@tonic-gate     for (i = 0; i < dest->naddrs; i++)
2917c478bd9Sstevel@tonic-gate 	/*LINTED*/
292159d09a2SMark Phalan 	dprint(" %A", dest->addrs[i].ai);
2937c478bd9Sstevel@tonic-gate     /*LINTED*/
2947c478bd9Sstevel@tonic-gate     dprint("\n\tlist2: ");
2957c478bd9Sstevel@tonic-gate     for (i = 0; i < src->naddrs; i++)
2967c478bd9Sstevel@tonic-gate 	/*LINTED*/
297159d09a2SMark Phalan 	dprint(" %A", src->addrs[i].ai);
2987c478bd9Sstevel@tonic-gate     /*LINTED*/
2997c478bd9Sstevel@tonic-gate     dprint("\n");
3007c478bd9Sstevel@tonic-gate #endif
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate     err = krb5int_grow_addrlist (dest, src->naddrs);
3037c478bd9Sstevel@tonic-gate     if (err)
3047c478bd9Sstevel@tonic-gate 	return err;
3057c478bd9Sstevel@tonic-gate     for (i = 0; i < src->naddrs; i++) {
3067c478bd9Sstevel@tonic-gate 	dest->addrs[dest->naddrs + i] = src->addrs[i];
307159d09a2SMark Phalan 	src->addrs[i].ai = 0;
308159d09a2SMark Phalan 	src->addrs[i].freefn = 0;
3097c478bd9Sstevel@tonic-gate     }
3107c478bd9Sstevel@tonic-gate     dest->naddrs += i;
3117c478bd9Sstevel@tonic-gate     src->naddrs = 0;
3127c478bd9Sstevel@tonic-gate 
313159d09a2SMark Phalan /* Solaris Kerberos */
3147c478bd9Sstevel@tonic-gate #ifdef DEBUG
3157c478bd9Sstevel@tonic-gate     /*LINTED*/
3167c478bd9Sstevel@tonic-gate     dprint("\tout:   ");
3177c478bd9Sstevel@tonic-gate     for (i = 0; i < dest->naddrs; i++)
3187c478bd9Sstevel@tonic-gate 	/*LINTED*/
319159d09a2SMark Phalan 	dprint(" %A", dest->addrs[i].ai);
3207c478bd9Sstevel@tonic-gate     /*LINTED*/
3217c478bd9Sstevel@tonic-gate     dprint("\n");
3227c478bd9Sstevel@tonic-gate #endif
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate     return 0;
3257c478bd9Sstevel@tonic-gate }
3267c478bd9Sstevel@tonic-gate 
327159d09a2SMark Phalan static int
328159d09a2SMark Phalan in_addrlist (struct addrinfo *thisaddr, struct addrlist *list)
329159d09a2SMark Phalan {
330159d09a2SMark Phalan     int i;
331159d09a2SMark Phalan     for (i = 0; i < list->naddrs; i++) {
332159d09a2SMark Phalan 	if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen
333159d09a2SMark Phalan 	    && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr,
334159d09a2SMark Phalan 		       thisaddr->ai_addrlen))
335159d09a2SMark Phalan 	    return 1;
336159d09a2SMark Phalan     }
337159d09a2SMark Phalan     return 0;
338159d09a2SMark Phalan }
339159d09a2SMark Phalan 
340159d09a2SMark Phalan static int
341159d09a2SMark Phalan check_for_svc_unavailable (krb5_context context,
342159d09a2SMark Phalan 			   const krb5_data *reply,
343159d09a2SMark Phalan 			   void *msg_handler_data)
344159d09a2SMark Phalan {
345159d09a2SMark Phalan     krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
346159d09a2SMark Phalan 
347159d09a2SMark Phalan     *retval = 0;
348159d09a2SMark Phalan 
349159d09a2SMark Phalan     if (krb5_is_krb_error(reply)) {
350159d09a2SMark Phalan 	krb5_error *err_reply;
351159d09a2SMark Phalan 
352159d09a2SMark Phalan 	if (decode_krb5_error(reply, &err_reply) == 0) {
353159d09a2SMark Phalan 	    *retval = err_reply->error;
354159d09a2SMark Phalan 	    krb5_free_error(context, err_reply);
355159d09a2SMark Phalan 
356159d09a2SMark Phalan 	    /* Returning 0 means continue to next KDC */
357159d09a2SMark Phalan 	    return (*retval != KDC_ERR_SVC_UNAVAILABLE);
358159d09a2SMark Phalan 	}
359159d09a2SMark Phalan     }
360159d09a2SMark Phalan 
361159d09a2SMark Phalan     return 1;
362159d09a2SMark Phalan }
363159d09a2SMark Phalan 
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate  * send the formatted request 'message' to a KDC for realm 'realm' and
3667c478bd9Sstevel@tonic-gate  * return the response (if any) in 'reply'.
3677c478bd9Sstevel@tonic-gate  *
3687c478bd9Sstevel@tonic-gate  * If the message is sent and a response is received, 0 is returned,
3697c478bd9Sstevel@tonic-gate  * otherwise an error code is returned.
3707c478bd9Sstevel@tonic-gate  *
3717c478bd9Sstevel@tonic-gate  * The storage for 'reply' is allocated and should be freed by the caller
3727c478bd9Sstevel@tonic-gate  * when finished.
3737c478bd9Sstevel@tonic-gate  */
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate krb5_error_code
3767c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message,
3777c478bd9Sstevel@tonic-gate 		 const krb5_data *realm, krb5_data *reply,
378505d05c7Sgtb 		 int *use_master, int tcp_only)
3797c478bd9Sstevel@tonic-gate {
380159d09a2SMark Phalan     krb5_error_code retval, retval2;
381*56bbb0b2SPeter Shoults     struct addrlist addrs = ADDRLIST_INIT;	/* Solaris Kerberos */
382505d05c7Sgtb     int socktype1 = 0, socktype2 = 0, addr_used;
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate     /*
3857c478bd9Sstevel@tonic-gate      * find KDC location(s) for realm
3867c478bd9Sstevel@tonic-gate      */
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate     /*
3897c478bd9Sstevel@tonic-gate      * BUG: This code won't return "interesting" errors (e.g., out of mem,
3907c478bd9Sstevel@tonic-gate      * bad config file) from locate_kdc.  KRB5_REALM_CANT_RESOLVE can be
3917c478bd9Sstevel@tonic-gate      * ignored from one query of two, but if only one query is done, or
3927c478bd9Sstevel@tonic-gate      * both return that error, it should be returned to the caller.  Also,
3937c478bd9Sstevel@tonic-gate      * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
3947c478bd9Sstevel@tonic-gate      * should probably be returned as well.
3957c478bd9Sstevel@tonic-gate      */
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate     /*LINTED*/
3987c478bd9Sstevel@tonic-gate     dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
3997c478bd9Sstevel@tonic-gate     /*LINTED*/
400505d05c7Sgtb 	   message->length, message->data, realm, *use_master, tcp_only);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate     if (!tcp_only && context->udp_pref_limit < 0) {
4037c478bd9Sstevel@tonic-gate 	int tmp;
4047c478bd9Sstevel@tonic-gate 	retval = profile_get_integer(context->profile,
4057c478bd9Sstevel@tonic-gate 				     "libdefaults", "udp_preference_limit", 0,
4067c478bd9Sstevel@tonic-gate 				     DEFAULT_UDP_PREF_LIMIT, &tmp);
4077c478bd9Sstevel@tonic-gate 	if (retval)
4087c478bd9Sstevel@tonic-gate 	    return retval;
4097c478bd9Sstevel@tonic-gate 	if (tmp < 0)
4107c478bd9Sstevel@tonic-gate 	    tmp = DEFAULT_UDP_PREF_LIMIT;
41156a424ccSmp 	else if (tmp > HARD_UDP_LIMIT)
4127c478bd9Sstevel@tonic-gate 	    /* In the unlikely case that a *really* big value is
4137c478bd9Sstevel@tonic-gate 	       given, let 'em use as big as we think we can
4147c478bd9Sstevel@tonic-gate 	       support.  */
4157c478bd9Sstevel@tonic-gate 	    tmp = HARD_UDP_LIMIT;
4167c478bd9Sstevel@tonic-gate 	context->udp_pref_limit = tmp;
4177c478bd9Sstevel@tonic-gate     }
4187c478bd9Sstevel@tonic-gate 
419505d05c7Sgtb     retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate     if (tcp_only)
4227c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_STREAM, socktype2 = 0;
4237c478bd9Sstevel@tonic-gate     else if (message->length <= context->udp_pref_limit)
4247c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
4257c478bd9Sstevel@tonic-gate     else
4267c478bd9Sstevel@tonic-gate 	socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
4277c478bd9Sstevel@tonic-gate 
428505d05c7Sgtb     retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
4297c478bd9Sstevel@tonic-gate     if (socktype2) {
4307c478bd9Sstevel@tonic-gate 	struct addrlist addrs2;
4317c478bd9Sstevel@tonic-gate 
432159d09a2SMark Phalan 	retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
433159d09a2SMark Phalan 				  socktype2, 0);
434159d09a2SMark Phalan #if 0
435159d09a2SMark Phalan 	if (retval2 == 0) {
436159d09a2SMark Phalan 	    (void) merge_addrlists(&addrs, &addrs2);
437159d09a2SMark Phalan 	    krb5int_free_addrlist(&addrs2);
438159d09a2SMark Phalan 	    retval = 0;
439159d09a2SMark Phalan 	} else if (retval == KRB5_REALM_CANT_RESOLVE) {
440159d09a2SMark Phalan 	    retval = retval2;
441159d09a2SMark Phalan 	}
442159d09a2SMark Phalan #else
443159d09a2SMark Phalan 	retval = retval2;
4447c478bd9Sstevel@tonic-gate 	if (retval == 0) {
4457c478bd9Sstevel@tonic-gate 	    (void) merge_addrlists(&addrs, &addrs2);
4467c478bd9Sstevel@tonic-gate 	    krb5int_free_addrlist(&addrs2);
4477c478bd9Sstevel@tonic-gate 	}
448159d09a2SMark Phalan #endif
4497c478bd9Sstevel@tonic-gate     }
450159d09a2SMark Phalan 
4517c478bd9Sstevel@tonic-gate     if (addrs.naddrs > 0) {
452159d09a2SMark Phalan 	krb5_error_code err = 0;
453159d09a2SMark Phalan 
454159d09a2SMark Phalan         retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
455159d09a2SMark Phalan 				 0, 0, &addr_used, check_for_svc_unavailable, &err);
456159d09a2SMark Phalan 	switch (retval) {
457159d09a2SMark Phalan 	case 0:
458505d05c7Sgtb             /*
459159d09a2SMark Phalan              * Set use_master to 1 if we ended up talking to a master when
460159d09a2SMark Phalan              * we didn't explicitly request to
461159d09a2SMark Phalan              */
462159d09a2SMark Phalan             if (*use_master == 0) {
463159d09a2SMark Phalan                 struct addrlist addrs3;
464159d09a2SMark Phalan                 retval = krb5_locate_kdc(context, realm, &addrs3, 1,
465159d09a2SMark Phalan                                          addrs.addrs[addr_used].ai->ai_socktype,
466159d09a2SMark Phalan                                          addrs.addrs[addr_used].ai->ai_family);
467159d09a2SMark Phalan                 if (retval == 0) {
468159d09a2SMark Phalan 		    if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
469159d09a2SMark Phalan 			*use_master = 1;
470159d09a2SMark Phalan                     krb5int_free_addrlist (&addrs3);
471159d09a2SMark Phalan                 }
472159d09a2SMark Phalan             }
473159d09a2SMark Phalan             krb5int_free_addrlist (&addrs);
474159d09a2SMark Phalan             return 0;
475159d09a2SMark Phalan 	default:
476159d09a2SMark Phalan 	    break;
477159d09a2SMark Phalan 	    /* Cases here are for constructing useful error messages.  */
478159d09a2SMark Phalan 	case KRB5_KDC_UNREACH:
479159d09a2SMark Phalan 	    if (err == KDC_ERR_SVC_UNAVAILABLE) {
480159d09a2SMark Phalan 		retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
481159d09a2SMark Phalan 	    } else {
482159d09a2SMark Phalan 		krb5_set_error_message(context, retval,
483159d09a2SMark Phalan 				       "Cannot contact any KDC for realm '%.*s'",
484159d09a2SMark Phalan 				       realm->length, realm->data);
485505d05c7Sgtb 	    }
486159d09a2SMark Phalan 	    break;
487505d05c7Sgtb 	}
488159d09a2SMark Phalan         krb5int_free_addrlist (&addrs);
4897c478bd9Sstevel@tonic-gate     }
4907c478bd9Sstevel@tonic-gate     return retval;
4917c478bd9Sstevel@tonic-gate }
4927c478bd9Sstevel@tonic-gate 
493159d09a2SMark Phalan #ifdef DEBUG
494159d09a2SMark Phalan 
495159d09a2SMark Phalan #ifdef _WIN32
496159d09a2SMark Phalan #define dperror(MSG) \
497159d09a2SMark Phalan 	 dprint("%s: an error occurred ... "			\
498159d09a2SMark Phalan 		"\tline=%d errno=%m socketerrno=%m\n",		\
499159d09a2SMark Phalan 		(MSG), __LINE__, errno, SOCKET_ERRNO)
500159d09a2SMark Phalan #else
501159d09a2SMark Phalan #define dperror(MSG) dprint("%s: %m\n", MSG, errno)
502159d09a2SMark Phalan #endif
503159d09a2SMark Phalan #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0)
504159d09a2SMark Phalan 
505159d09a2SMark Phalan #else /* ! DEBUG */
506159d09a2SMark Phalan 
507159d09a2SMark Phalan #define dperror(MSG) ((void)(MSG))
508159d09a2SMark Phalan #define dfprintf(ARGLIST) ((void)0)
509159d09a2SMark Phalan 
510159d09a2SMark Phalan #endif
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate /*
5137c478bd9Sstevel@tonic-gate  * Notes:
5147c478bd9Sstevel@tonic-gate  *
5157c478bd9Sstevel@tonic-gate  * Getting "connection refused" on a connected UDP socket causes
5167c478bd9Sstevel@tonic-gate  * select to indicate write capability on UNIX, but only shows up
5177c478bd9Sstevel@tonic-gate  * as an exception on Windows.  (I don't think any UNIX system flags
5187c478bd9Sstevel@tonic-gate  * the error as an exception.)  So we check for both, or make it
5197c478bd9Sstevel@tonic-gate  * system-specific.
5207c478bd9Sstevel@tonic-gate  *
5217c478bd9Sstevel@tonic-gate  * Always watch for responses from *any* of the servers.  Eventually
5227c478bd9Sstevel@tonic-gate  * fix the UDP code to do the same.
5237c478bd9Sstevel@tonic-gate  *
5247c478bd9Sstevel@tonic-gate  * To do:
5257c478bd9Sstevel@tonic-gate  * - TCP NOPUSH/CORK socket options?
5267c478bd9Sstevel@tonic-gate  * - error codes that don't suck
5277c478bd9Sstevel@tonic-gate  * - getsockopt(SO_ERROR) to check connect status
5287c478bd9Sstevel@tonic-gate  * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
5297c478bd9Sstevel@tonic-gate  *   connections already in progress
5307c478bd9Sstevel@tonic-gate  */
5317c478bd9Sstevel@tonic-gate 
532159d09a2SMark Phalan #include "cm.h"
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp)
5357c478bd9Sstevel@tonic-gate {
536159d09a2SMark Phalan #ifdef _WIN32
537159d09a2SMark Phalan     struct _timeb tb;
538159d09a2SMark Phalan     _ftime(&tb);
539159d09a2SMark Phalan     tvp->tv_sec = tb.time;
540159d09a2SMark Phalan     tvp->tv_usec = tb.millitm * 1000;
541159d09a2SMark Phalan     /* Can _ftime fail?  */
542159d09a2SMark Phalan     return 0;
543159d09a2SMark Phalan #else
5447c478bd9Sstevel@tonic-gate     if (gettimeofday(tvp, 0)) {
5457c478bd9Sstevel@tonic-gate 	dperror("gettimeofday");
5467c478bd9Sstevel@tonic-gate 	return errno;
5477c478bd9Sstevel@tonic-gate     }
5487c478bd9Sstevel@tonic-gate     return 0;
549159d09a2SMark Phalan #endif
5507c478bd9Sstevel@tonic-gate }
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate /*
5537c478bd9Sstevel@tonic-gate  * Call select and return results.
5547c478bd9Sstevel@tonic-gate  * Input: interesting file descriptors and absolute timeout
5557c478bd9Sstevel@tonic-gate  * Output: select return value (-1 or num fds ready) and fd_sets
5567c478bd9Sstevel@tonic-gate  * Return: 0 (for i/o available or timeout) or error code.
5577c478bd9Sstevel@tonic-gate  */
5587c478bd9Sstevel@tonic-gate krb5_error_code
5597c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in,
5607c478bd9Sstevel@tonic-gate 			struct select_state *out, int *sret)
5617c478bd9Sstevel@tonic-gate {
5627c478bd9Sstevel@tonic-gate     struct timeval now, *timo;
5637c478bd9Sstevel@tonic-gate     krb5_error_code e;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate     *out = *in;
5667c478bd9Sstevel@tonic-gate     e = getcurtime(&now);
5677c478bd9Sstevel@tonic-gate     if (e)
5687c478bd9Sstevel@tonic-gate 	return e;
5697c478bd9Sstevel@tonic-gate     if (out->end_time.tv_sec == 0)
5707c478bd9Sstevel@tonic-gate 	timo = 0;
5717c478bd9Sstevel@tonic-gate     else {
5727c478bd9Sstevel@tonic-gate 	timo = &out->end_time;
5737c478bd9Sstevel@tonic-gate 	out->end_time.tv_sec -= now.tv_sec;
5747c478bd9Sstevel@tonic-gate 	out->end_time.tv_usec -= now.tv_usec;
5757c478bd9Sstevel@tonic-gate 	if (out->end_time.tv_usec < 0) {
5767c478bd9Sstevel@tonic-gate 	    out->end_time.tv_usec += 1000000;
5777c478bd9Sstevel@tonic-gate 	    out->end_time.tv_sec--;
5787c478bd9Sstevel@tonic-gate 	}
5797c478bd9Sstevel@tonic-gate 	if (out->end_time.tv_sec < 0) {
5807c478bd9Sstevel@tonic-gate 	    *sret = 0;
5817c478bd9Sstevel@tonic-gate 	    return 0;
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate     }
5847c478bd9Sstevel@tonic-gate     /*LINTED*/
5857c478bd9Sstevel@tonic-gate     dprint("selecting on max=%d sockets [%F] timeout %t\n",
5867c478bd9Sstevel@tonic-gate 	    /*LINTED*/
587159d09a2SMark Phalan 	   out->max,
588159d09a2SMark Phalan 	   &out->rfds, &out->wfds, &out->xfds, out->max,
589159d09a2SMark Phalan 	   timo);
5907c478bd9Sstevel@tonic-gate     *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
5917c478bd9Sstevel@tonic-gate     e = SOCKET_ERRNO;
5927c478bd9Sstevel@tonic-gate 
593159d09a2SMark Phalan /* Solaris Kerberos */
5947c478bd9Sstevel@tonic-gate #ifdef DEBUG
5957c478bd9Sstevel@tonic-gate     /*LINTED*/
5967c478bd9Sstevel@tonic-gate     dprint("select returns %d", *sret);
5977c478bd9Sstevel@tonic-gate     if (*sret < 0)
5987c478bd9Sstevel@tonic-gate 	/*LINTED*/
5997c478bd9Sstevel@tonic-gate 	dprint(", error = %E\n", e);
6007c478bd9Sstevel@tonic-gate     else if (*sret == 0)
6017c478bd9Sstevel@tonic-gate 	/*LINTED*/
6027c478bd9Sstevel@tonic-gate 	dprint(" (timeout)\n");
6037c478bd9Sstevel@tonic-gate     else
6047c478bd9Sstevel@tonic-gate 	/*LINTED*/
6057c478bd9Sstevel@tonic-gate 	dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
6067c478bd9Sstevel@tonic-gate #endif
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate     if (*sret < 0)
6097c478bd9Sstevel@tonic-gate 	return e;
6107c478bd9Sstevel@tonic-gate     return 0;
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn,
6147c478bd9Sstevel@tonic-gate 			   struct select_state *selstate, int ssflags);
6157c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn,
6167c478bd9Sstevel@tonic-gate 			   struct select_state *selstate, int ssflags);
6177c478bd9Sstevel@tonic-gate 
618159d09a2SMark Phalan static void
619159d09a2SMark Phalan set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
620159d09a2SMark Phalan {
621159d09a2SMark Phalan     if (!message || message->length == 0)
622159d09a2SMark Phalan 	return;
623159d09a2SMark Phalan 
624159d09a2SMark Phalan     if (!state->is_udp) {
625159d09a2SMark Phalan 
626159d09a2SMark Phalan 	state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff;
627159d09a2SMark Phalan 	state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff;
628159d09a2SMark Phalan 	state->x.out.msg_len_buf[2] = (message->length >>  8) & 0xff;
629159d09a2SMark Phalan 	state->x.out.msg_len_buf[3] =  message->length        & 0xff;
630159d09a2SMark Phalan 
631159d09a2SMark Phalan 	SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
632159d09a2SMark Phalan 	SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
633159d09a2SMark Phalan    	state->x.out.sg_count = 2;
634159d09a2SMark Phalan 
635159d09a2SMark Phalan     } else {
636159d09a2SMark Phalan 
637159d09a2SMark Phalan 	SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
638159d09a2SMark Phalan 	SG_SET(&state->x.out.sgbuf[1], 0, 0);
639159d09a2SMark Phalan 	state->x.out.sg_count = 1;
640159d09a2SMark Phalan 
641159d09a2SMark Phalan     }
642159d09a2SMark Phalan }
643159d09a2SMark Phalan 
644159d09a2SMark Phalan 
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate static int
6477c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai,
648159d09a2SMark Phalan 		  const krb5_data *message, char **udpbufp)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate     state->state = INITIALIZING;
6517c478bd9Sstevel@tonic-gate     state->err = 0;
6527c478bd9Sstevel@tonic-gate     state->x.out.sgp = state->x.out.sgbuf;
6537c478bd9Sstevel@tonic-gate     state->addr = ai;
6547c478bd9Sstevel@tonic-gate     state->fd = INVALID_SOCKET;
6557c478bd9Sstevel@tonic-gate     SG_SET(&state->x.out.sgbuf[1], 0, 0);
6567c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_STREAM) {
657159d09a2SMark Phalan 	/*
6587c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
6597c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
6607c478bd9Sstevel@tonic-gate 	state->x.out.sg_count = 2;
661159d09a2SMark Phalan 	*/
662159d09a2SMark Phalan 
6637c478bd9Sstevel@tonic-gate 	state->is_udp = 0;
6647c478bd9Sstevel@tonic-gate 	state->service = service_tcp_fd;
665159d09a2SMark Phalan 	set_conn_state_msg_length (state, message);
6667c478bd9Sstevel@tonic-gate     } else {
667159d09a2SMark Phalan 	/*
6687c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
6697c478bd9Sstevel@tonic-gate 	SG_SET(&state->x.out.sgbuf[1], 0, 0);
6707c478bd9Sstevel@tonic-gate 	state->x.out.sg_count = 1;
671159d09a2SMark Phalan 	*/
672159d09a2SMark Phalan 
6737c478bd9Sstevel@tonic-gate 	state->is_udp = 1;
6747c478bd9Sstevel@tonic-gate 	state->service = service_udp_fd;
675159d09a2SMark Phalan 	set_conn_state_msg_length (state, message);
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	if (*udpbufp == 0) {
6787c478bd9Sstevel@tonic-gate 	    *udpbufp = malloc(krb5_max_dgram_size);
6797c478bd9Sstevel@tonic-gate 	    if (*udpbufp == 0) {
6807c478bd9Sstevel@tonic-gate 		dperror("malloc(krb5_max_dgram_size)");
6817c478bd9Sstevel@tonic-gate 		(void) closesocket(state->fd);
6827c478bd9Sstevel@tonic-gate 		state->fd = INVALID_SOCKET;
6837c478bd9Sstevel@tonic-gate 		state->state = FAILED;
6847c478bd9Sstevel@tonic-gate 		return 1;
6857c478bd9Sstevel@tonic-gate 	    }
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 	state->x.in.buf = *udpbufp;
6887c478bd9Sstevel@tonic-gate 	state->x.in.bufsize = krb5_max_dgram_size;
6897c478bd9Sstevel@tonic-gate     }
6907c478bd9Sstevel@tonic-gate     return 0;
6917c478bd9Sstevel@tonic-gate }
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate static int
694159d09a2SMark Phalan start_connection (struct conn_state *state,
695159d09a2SMark Phalan 		  struct select_state *selstate,
696159d09a2SMark Phalan 		  struct sendto_callback_info* callback_info,
697159d09a2SMark Phalan                   krb5_data* callback_buffer)
6987c478bd9Sstevel@tonic-gate {
6997c478bd9Sstevel@tonic-gate     int fd, e;
7007c478bd9Sstevel@tonic-gate     struct addrinfo *ai = state->addr;
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate     /*LINTED*/
7037c478bd9Sstevel@tonic-gate     dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
7047c478bd9Sstevel@tonic-gate 	   /*LINTED*/
7057c478bd9Sstevel@tonic-gate 	   ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
7067c478bd9Sstevel@tonic-gate     fd = socket(ai->ai_family, ai->ai_socktype, 0);
7077c478bd9Sstevel@tonic-gate     if (fd == INVALID_SOCKET) {
7087c478bd9Sstevel@tonic-gate 	state->err = SOCKET_ERRNO;
7097c478bd9Sstevel@tonic-gate 	/*LINTED*/
7107c478bd9Sstevel@tonic-gate 	dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
7117c478bd9Sstevel@tonic-gate 	return -1;		/* try other hosts */
7127c478bd9Sstevel@tonic-gate     }
7137c478bd9Sstevel@tonic-gate     /* Make it non-blocking.  */
7147c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_STREAM) {
7157c478bd9Sstevel@tonic-gate 	static const int one = 1;
7167c478bd9Sstevel@tonic-gate 	static const struct linger lopt = { 0, 0 };
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	if (ioctlsocket(fd, FIONBIO, (const void *) &one))
7197c478bd9Sstevel@tonic-gate 	    dperror("sendto_kdc: ioctl(FIONBIO)");
7207c478bd9Sstevel@tonic-gate 	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
7217c478bd9Sstevel@tonic-gate 	    dperror("sendto_kdc: setsockopt(SO_LINGER)");
7227c478bd9Sstevel@tonic-gate     }
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate     /* Start connecting to KDC.  */
7257c478bd9Sstevel@tonic-gate     /*LINTED*/
7267c478bd9Sstevel@tonic-gate     dprint(" fd %d; connecting to %A...\n", fd, ai);
7277c478bd9Sstevel@tonic-gate     e = connect(fd, ai->ai_addr, ai->ai_addrlen);
7287c478bd9Sstevel@tonic-gate     if (e != 0) {
7297c478bd9Sstevel@tonic-gate 	/*
7307c478bd9Sstevel@tonic-gate 	 * This is the path that should be followed for non-blocking
7317c478bd9Sstevel@tonic-gate 	 * connections.
7327c478bd9Sstevel@tonic-gate 	 */
7337c478bd9Sstevel@tonic-gate 	if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
7347c478bd9Sstevel@tonic-gate 	    state->state = CONNECTING;
735159d09a2SMark Phalan 	    state->fd = fd;
7367c478bd9Sstevel@tonic-gate 	} else {
7377c478bd9Sstevel@tonic-gate 	    /*LINTED*/
7387c478bd9Sstevel@tonic-gate 	    dprint("connect failed: %m\n", SOCKET_ERRNO);
739159d09a2SMark Phalan 	    (void) closesocket(fd);
7407c478bd9Sstevel@tonic-gate 	    state->err = SOCKET_ERRNO;
7417c478bd9Sstevel@tonic-gate 	    state->state = FAILED;
7427c478bd9Sstevel@tonic-gate 	    return -2;
7437c478bd9Sstevel@tonic-gate 	}
7447c478bd9Sstevel@tonic-gate     } else {
7457c478bd9Sstevel@tonic-gate 	/*
7467c478bd9Sstevel@tonic-gate 	 * Connect returned zero even though we tried to make it
7477c478bd9Sstevel@tonic-gate 	 * non-blocking, which should have caused it to return before
7487c478bd9Sstevel@tonic-gate 	 * finishing the connection.  Oh well.  Someone's network
7497c478bd9Sstevel@tonic-gate 	 * stack is broken, but if they gave us a connection, use it.
7507c478bd9Sstevel@tonic-gate 	 */
7517c478bd9Sstevel@tonic-gate 	state->state = WRITING;
752159d09a2SMark Phalan 	state->fd = fd;
7537c478bd9Sstevel@tonic-gate     }
7547c478bd9Sstevel@tonic-gate     /*LINTED*/
7557c478bd9Sstevel@tonic-gate     dprint("new state = %s\n", state_strings[state->state]);
7567c478bd9Sstevel@tonic-gate 
757159d09a2SMark Phalan 
758159d09a2SMark Phalan     /*
759159d09a2SMark Phalan      * Here's where KPASSWD callback gets the socket information it needs for
760159d09a2SMark Phalan      * a kpasswd request
761159d09a2SMark Phalan      */
762159d09a2SMark Phalan     if (callback_info) {
763159d09a2SMark Phalan 
764159d09a2SMark Phalan 	e = callback_info->pfn_callback(state,
765159d09a2SMark Phalan 					callback_info->context,
766159d09a2SMark Phalan 					callback_buffer);
767159d09a2SMark Phalan 	if (e != 0) {
768159d09a2SMark Phalan 	    dprint("callback failed: %m\n", e);
769159d09a2SMark Phalan 	    (void) closesocket(fd);
770159d09a2SMark Phalan 	    state->err = e;
771159d09a2SMark Phalan 	    state->fd = INVALID_SOCKET;
772159d09a2SMark Phalan 	    state->state = FAILED;
773159d09a2SMark Phalan 	    return -3;
774159d09a2SMark Phalan 	}
775159d09a2SMark Phalan 
776159d09a2SMark Phalan 	dprint("callback %p (message=%d@%p)\n",
777159d09a2SMark Phalan 	       state,
778159d09a2SMark Phalan 	       callback_buffer->length,
779159d09a2SMark Phalan 	       callback_buffer->data);
780159d09a2SMark Phalan 
781159d09a2SMark Phalan 	set_conn_state_msg_length( state, callback_buffer );
782159d09a2SMark Phalan     }
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate     if (ai->ai_socktype == SOCK_DGRAM) {
7857c478bd9Sstevel@tonic-gate 	/* Send it now.  */
7867c478bd9Sstevel@tonic-gate 	int ret;
7877c478bd9Sstevel@tonic-gate 	sg_buf *sg = &state->x.out.sgbuf[0];
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	/*LINTED*/
7907c478bd9Sstevel@tonic-gate 	dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
7917c478bd9Sstevel@tonic-gate 	ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
7927c478bd9Sstevel@tonic-gate 	if (ret != SG_LEN(sg)) {
7937c478bd9Sstevel@tonic-gate 	    dperror("sendto");
7947c478bd9Sstevel@tonic-gate 	    (void) closesocket(state->fd);
7957c478bd9Sstevel@tonic-gate 	    state->fd = INVALID_SOCKET;
7967c478bd9Sstevel@tonic-gate 	    state->state = FAILED;
797159d09a2SMark Phalan 	    return -4;
7987c478bd9Sstevel@tonic-gate 	} else {
7997c478bd9Sstevel@tonic-gate 	    state->state = READING;
8007c478bd9Sstevel@tonic-gate 	}
8017c478bd9Sstevel@tonic-gate     }
802159d09a2SMark Phalan #ifdef DEBUG
803159d09a2SMark Phalan     if (debug) {
804159d09a2SMark Phalan 	struct sockaddr_storage ss;
805159d09a2SMark Phalan 	socklen_t sslen = sizeof(ss);
806159d09a2SMark Phalan 	if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
807159d09a2SMark Phalan 	    struct addrinfo hack_ai;
808159d09a2SMark Phalan 	    memset(&hack_ai, 0, sizeof(hack_ai));
809159d09a2SMark Phalan 	    hack_ai.ai_addr = (struct sockaddr *) &ss;
810159d09a2SMark Phalan 	    hack_ai.ai_addrlen = sslen;
811159d09a2SMark Phalan 	    hack_ai.ai_socktype = SOCK_DGRAM;
812159d09a2SMark Phalan 	    hack_ai.ai_family = ai->ai_family;
813159d09a2SMark Phalan 	    dprint("local socket address is %A\n", &hack_ai);
814159d09a2SMark Phalan 	}
815159d09a2SMark Phalan     }
816159d09a2SMark Phalan #endif
8177c478bd9Sstevel@tonic-gate     FD_SET(state->fd, &selstate->rfds);
8187c478bd9Sstevel@tonic-gate     if (state->state == CONNECTING || state->state == WRITING)
8197c478bd9Sstevel@tonic-gate 	FD_SET(state->fd, &selstate->wfds);
8207c478bd9Sstevel@tonic-gate     FD_SET(state->fd, &selstate->xfds);
8217c478bd9Sstevel@tonic-gate     if (selstate->max <= state->fd)
8227c478bd9Sstevel@tonic-gate 	selstate->max = state->fd + 1;
8237c478bd9Sstevel@tonic-gate     selstate->nfds++;
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate     /*LINTED*/
8267c478bd9Sstevel@tonic-gate     dprint("new select vectors: %F\n",
8277c478bd9Sstevel@tonic-gate 	   /*LINTED*/
8287c478bd9Sstevel@tonic-gate 	   &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate     return 0;
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise.
8347c478bd9Sstevel@tonic-gate    If 0 is returned, the caller should delay waiting for a response.
8357c478bd9Sstevel@tonic-gate    Otherwise, the caller should immediately move on to process the
8367c478bd9Sstevel@tonic-gate    next connection.  */
8377c478bd9Sstevel@tonic-gate static int
838159d09a2SMark Phalan maybe_send (struct conn_state *conn,
839159d09a2SMark Phalan 	    struct select_state *selstate,
840159d09a2SMark Phalan 	    struct sendto_callback_info* callback_info,
841159d09a2SMark Phalan 	    krb5_data* callback_buffer)
8427c478bd9Sstevel@tonic-gate {
8437c478bd9Sstevel@tonic-gate     sg_buf *sg;
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate     /*LINTED*/
8467c478bd9Sstevel@tonic-gate     dprint("maybe_send(@%p) state=%s type=%s\n", conn,
8477c478bd9Sstevel@tonic-gate 	   /*LINTED*/
848159d09a2SMark Phalan 	   state_strings[conn->state],
849159d09a2SMark Phalan 	   conn->is_udp ? "udp" : "tcp");
8507c478bd9Sstevel@tonic-gate     if (conn->state == INITIALIZING)
851159d09a2SMark Phalan 	return start_connection(conn, selstate, callback_info, callback_buffer);
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate     /* Did we already shut down this channel?  */
8547c478bd9Sstevel@tonic-gate     if (conn->state == FAILED) {
8557c478bd9Sstevel@tonic-gate 	dprint("connection already closed\n");
8567c478bd9Sstevel@tonic-gate 	return -1;
8577c478bd9Sstevel@tonic-gate     }
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate     if (conn->addr->ai_socktype == SOCK_STREAM) {
8607c478bd9Sstevel@tonic-gate 	dprint("skipping stream socket\n");
8617c478bd9Sstevel@tonic-gate 	/* The select callback will handle flushing any data we
8627c478bd9Sstevel@tonic-gate 	   haven't written yet, and we only write it once.  */
8637c478bd9Sstevel@tonic-gate 	return -1;
8647c478bd9Sstevel@tonic-gate     }
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate     /* UDP - Send message, possibly for the first time, possibly a
8677c478bd9Sstevel@tonic-gate        retransmit if a previous attempt timed out.  */
8687c478bd9Sstevel@tonic-gate     sg = &conn->x.out.sgbuf[0];
8697c478bd9Sstevel@tonic-gate     /*LINTED*/
8707c478bd9Sstevel@tonic-gate     dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
8717c478bd9Sstevel@tonic-gate     if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) {
8727c478bd9Sstevel@tonic-gate 	dperror("send");
8737c478bd9Sstevel@tonic-gate 	/* Keep connection alive, we'll try again next pass.
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	   Is this likely to catch any errors we didn't get from the
8767c478bd9Sstevel@tonic-gate 	   select callbacks?  */
8777c478bd9Sstevel@tonic-gate 	return -1;
8787c478bd9Sstevel@tonic-gate     }
8797c478bd9Sstevel@tonic-gate     /* Yay, it worked.  */
8807c478bd9Sstevel@tonic-gate     return 0;
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate static void
8847c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
8857c478bd9Sstevel@tonic-gate {
8867c478bd9Sstevel@tonic-gate     conn->state = FAILED;
8877c478bd9Sstevel@tonic-gate     shutdown(conn->fd, SHUTDOWN_BOTH);
8887c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->rfds);
8897c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->wfds);
8907c478bd9Sstevel@tonic-gate     FD_CLR(conn->fd, &selstate->xfds);
8917c478bd9Sstevel@tonic-gate     conn->err = err;
8927c478bd9Sstevel@tonic-gate     /*LINTED*/
8937c478bd9Sstevel@tonic-gate     dprint("abandoning connection %d: %m\n", conn->fd, err);
8947c478bd9Sstevel@tonic-gate     /* Fix up max fd for next select call.  */
8957c478bd9Sstevel@tonic-gate     if (selstate->max == 1 + conn->fd) {
8967c478bd9Sstevel@tonic-gate 	while (selstate->max > 0
8977c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->rfds)
8987c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->wfds)
8997c478bd9Sstevel@tonic-gate 	       && ! FD_ISSET(selstate->max-1, &selstate->xfds))
9007c478bd9Sstevel@tonic-gate 	    selstate->max--;
9017c478bd9Sstevel@tonic-gate 	/*LINTED*/
9027c478bd9Sstevel@tonic-gate 	dprint("new max_fd + 1 is %d\n", selstate->max);
9037c478bd9Sstevel@tonic-gate     }
9047c478bd9Sstevel@tonic-gate     selstate->nfds--;
9057c478bd9Sstevel@tonic-gate }
9067c478bd9Sstevel@tonic-gate 
907159d09a2SMark Phalan /* Check socket for error.  */
908159d09a2SMark Phalan static int
909159d09a2SMark Phalan get_so_error(int fd)
910159d09a2SMark Phalan {
911159d09a2SMark Phalan     int e, sockerr;
912159d09a2SMark Phalan     socklen_t sockerrlen;
913159d09a2SMark Phalan 
914159d09a2SMark Phalan     sockerr = 0;
915159d09a2SMark Phalan     sockerrlen = sizeof(sockerr);
916159d09a2SMark Phalan     e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
917159d09a2SMark Phalan     if (e != 0) {
918159d09a2SMark Phalan 	/* What to do now?  */
919159d09a2SMark Phalan 	e = SOCKET_ERRNO;
920159d09a2SMark Phalan 	dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e);
921159d09a2SMark Phalan 	return e;
922159d09a2SMark Phalan     }
923159d09a2SMark Phalan     return sockerr;
924159d09a2SMark Phalan }
925159d09a2SMark Phalan 
9267c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit
9277c478bd9Sstevel@tonic-gate    its loop.  This happens in two cases: We have a complete message,
9287c478bd9Sstevel@tonic-gate    or the socket has closed and no others are open.  */
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate static int
9317c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
9327c478bd9Sstevel@tonic-gate 		int ssflags)
9337c478bd9Sstevel@tonic-gate {
9347c478bd9Sstevel@tonic-gate     krb5_error_code e = 0;
9357c478bd9Sstevel@tonic-gate     int nwritten, nread;
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate     if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
9387c478bd9Sstevel@tonic-gate 	abort();
9397c478bd9Sstevel@tonic-gate     switch (conn->state) {
9407c478bd9Sstevel@tonic-gate 	SOCKET_WRITEV_TEMP tmp;
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate     case CONNECTING:
9437c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_READ) {
9447c478bd9Sstevel@tonic-gate 	    /* Bad -- the KDC shouldn't be sending to us first.  */
9457c478bd9Sstevel@tonic-gate 	    e = EINVAL /* ?? */;
9467c478bd9Sstevel@tonic-gate 	kill_conn:
9477c478bd9Sstevel@tonic-gate 	    kill_conn(conn, selstate, e);
9487c478bd9Sstevel@tonic-gate 	    if (e == EINVAL) {
9497c478bd9Sstevel@tonic-gate 		closesocket(conn->fd);
9507c478bd9Sstevel@tonic-gate 		conn->fd = INVALID_SOCKET;
9517c478bd9Sstevel@tonic-gate 	    }
9527c478bd9Sstevel@tonic-gate 	    return e == 0;
9537c478bd9Sstevel@tonic-gate 	}
9547c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION) {
9557c478bd9Sstevel@tonic-gate 	handle_exception:
956159d09a2SMark Phalan 	    e = get_so_error(conn->fd);
957159d09a2SMark Phalan 	    if (e)
958159d09a2SMark Phalan 		dprint("socket error on exception fd: %m", e);
959159d09a2SMark Phalan 	    else
960159d09a2SMark Phalan 		dprint("no socket error info available on exception fd");
9617c478bd9Sstevel@tonic-gate 	    goto kill_conn;
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	/*
9657c478bd9Sstevel@tonic-gate 	 * Connect finished -- but did it succeed or fail?
9667c478bd9Sstevel@tonic-gate 	 * UNIX sets can_write if failed.
967159d09a2SMark Phalan 	 * Call getsockopt to see if error pending.
968159d09a2SMark Phalan 	 *
969159d09a2SMark Phalan 	 * (For most UNIX systems it works to just try writing the
970159d09a2SMark Phalan 	 * first time and detect an error.  But Bill Dodd at IBM
971159d09a2SMark Phalan 	 * reports that some version of AIX, SIGPIPE can result.)
9727c478bd9Sstevel@tonic-gate 	 */
973159d09a2SMark Phalan 	e = get_so_error(conn->fd);
974159d09a2SMark Phalan 	if (e) {
975159d09a2SMark Phalan 	    dprint("socket error on write fd: %m", e);
976159d09a2SMark Phalan 	    goto kill_conn;
977159d09a2SMark Phalan 	}
9787c478bd9Sstevel@tonic-gate 	conn->state = WRITING;
9797c478bd9Sstevel@tonic-gate 	goto try_writing;
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate     case WRITING:
9827c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_READ) {
9837c478bd9Sstevel@tonic-gate 	    e = E2BIG;
9847c478bd9Sstevel@tonic-gate 	    /* Bad -- the KDC shouldn't be sending anything yet.  */
9857c478bd9Sstevel@tonic-gate 	    goto kill_conn;
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION)
9887c478bd9Sstevel@tonic-gate 	    goto handle_exception;
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate     try_writing:
9917c478bd9Sstevel@tonic-gate 	/*LINTED*/
9927c478bd9Sstevel@tonic-gate 	dprint("trying to writev %d (%d bytes) to fd %d\n",
9937c478bd9Sstevel@tonic-gate 		/*LINTED*/
9947c478bd9Sstevel@tonic-gate 	       conn->x.out.sg_count,
9957c478bd9Sstevel@tonic-gate 	       ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
9967c478bd9Sstevel@tonic-gate 		/*LINTED*/
9977c478bd9Sstevel@tonic-gate 		+ SG_LEN(&conn->x.out.sgp[0])),
9987c478bd9Sstevel@tonic-gate 	       conn->fd);
9997c478bd9Sstevel@tonic-gate 	nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
10007c478bd9Sstevel@tonic-gate 				 conn->x.out.sg_count, tmp);
10017c478bd9Sstevel@tonic-gate 	if (nwritten < 0) {
10027c478bd9Sstevel@tonic-gate 	    e = SOCKET_ERRNO;
10037c478bd9Sstevel@tonic-gate 	    /*LINTED*/
10047c478bd9Sstevel@tonic-gate 	    dprint("failed: %m\n", e);
10057c478bd9Sstevel@tonic-gate 	    goto kill_conn;
10067c478bd9Sstevel@tonic-gate 	}
10077c478bd9Sstevel@tonic-gate 	/*LINTED*/
10087c478bd9Sstevel@tonic-gate 	dprint("wrote %d bytes\n", nwritten);
10097c478bd9Sstevel@tonic-gate 	while (nwritten) {
10107c478bd9Sstevel@tonic-gate 	    sg_buf *sgp = conn->x.out.sgp;
10117c478bd9Sstevel@tonic-gate 	    if (nwritten < SG_LEN(sgp)) {
10127c478bd9Sstevel@tonic-gate 		/*LINTED*/
10137c478bd9Sstevel@tonic-gate 		SG_ADVANCE(sgp, nwritten);
10147c478bd9Sstevel@tonic-gate 		nwritten = 0;
10157c478bd9Sstevel@tonic-gate 	    } else {
10167c478bd9Sstevel@tonic-gate 		nwritten -= SG_LEN(conn->x.out.sgp);
10177c478bd9Sstevel@tonic-gate 		conn->x.out.sgp++;
10187c478bd9Sstevel@tonic-gate 		conn->x.out.sg_count--;
10197c478bd9Sstevel@tonic-gate 		if (conn->x.out.sg_count == 0 && nwritten != 0)
10207c478bd9Sstevel@tonic-gate 		    /* Wrote more than we wanted to?  */
10217c478bd9Sstevel@tonic-gate 		    abort();
10227c478bd9Sstevel@tonic-gate 	    }
10237c478bd9Sstevel@tonic-gate 	}
10247c478bd9Sstevel@tonic-gate 	if (conn->x.out.sg_count == 0) {
10257c478bd9Sstevel@tonic-gate 	    /* Done writing, switch to reading.  */
10267c478bd9Sstevel@tonic-gate 	    /* Don't call shutdown at this point because
10277c478bd9Sstevel@tonic-gate 	     * some implementations cannot deal with half-closed connections.*/
10287c478bd9Sstevel@tonic-gate 	    FD_CLR(conn->fd, &selstate->wfds);
10297c478bd9Sstevel@tonic-gate 	    /* Q: How do we detect failures to send the remaining data
10307c478bd9Sstevel@tonic-gate 	       to the remote side, since we're in non-blocking mode?
10317c478bd9Sstevel@tonic-gate 	       Will we always get errors on the reading side?  */
10327c478bd9Sstevel@tonic-gate 	    /*LINTED*/
10337c478bd9Sstevel@tonic-gate 	    dprint("switching fd %d to READING\n", conn->fd);
10347c478bd9Sstevel@tonic-gate 	    conn->state = READING;
10357c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsizebytes_read = 0;
10367c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsize = 0;
10377c478bd9Sstevel@tonic-gate 	    conn->x.in.buf = 0;
10387c478bd9Sstevel@tonic-gate 	    conn->x.in.pos = 0;
10397c478bd9Sstevel@tonic-gate 	    conn->x.in.n_left = 0;
10407c478bd9Sstevel@tonic-gate 	}
10417c478bd9Sstevel@tonic-gate 	return 0;
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate     case READING:
10447c478bd9Sstevel@tonic-gate 	if (ssflags & SSF_EXCEPTION) {
10457c478bd9Sstevel@tonic-gate 	    if (conn->x.in.buf) {
10467c478bd9Sstevel@tonic-gate 		free(conn->x.in.buf);
10477c478bd9Sstevel@tonic-gate 		conn->x.in.buf = 0;
10487c478bd9Sstevel@tonic-gate 	    }
10497c478bd9Sstevel@tonic-gate 	    goto handle_exception;
10507c478bd9Sstevel@tonic-gate 	}
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	if (conn->x.in.bufsizebytes_read == 4) {
10537c478bd9Sstevel@tonic-gate 	    /* Reading data.  */
10547c478bd9Sstevel@tonic-gate 	    /*LINTED*/
10557c478bd9Sstevel@tonic-gate 	    dprint("reading %d bytes of data from fd %d\n",
10567c478bd9Sstevel@tonic-gate 		   (int) conn->x.in.n_left, conn->fd);
10577c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
10587c478bd9Sstevel@tonic-gate 	    if (nread <= 0) {
10597c478bd9Sstevel@tonic-gate 		e = nread ? SOCKET_ERRNO : ECONNRESET;
10607c478bd9Sstevel@tonic-gate 		free(conn->x.in.buf);
10617c478bd9Sstevel@tonic-gate 		conn->x.in.buf = 0;
10627c478bd9Sstevel@tonic-gate 		goto kill_conn;
10637c478bd9Sstevel@tonic-gate 	    }
10647c478bd9Sstevel@tonic-gate 	    conn->x.in.n_left -= nread;
10657c478bd9Sstevel@tonic-gate 	    conn->x.in.pos += nread;
1066159d09a2SMark Phalan 	    /* Solaris Kerberos */
10677c478bd9Sstevel@tonic-gate 	    if ((long)conn->x.in.n_left <= 0) {
10687c478bd9Sstevel@tonic-gate 		/* We win!  */
10697c478bd9Sstevel@tonic-gate 		return 1;
10707c478bd9Sstevel@tonic-gate 	    }
10717c478bd9Sstevel@tonic-gate 	} else {
10727c478bd9Sstevel@tonic-gate 	    /* Reading length.  */
10737c478bd9Sstevel@tonic-gate 	    nread = SOCKET_READ(conn->fd,
10747c478bd9Sstevel@tonic-gate 				conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
10757c478bd9Sstevel@tonic-gate 				4 - conn->x.in.bufsizebytes_read);
10767c478bd9Sstevel@tonic-gate 	    if (nread < 0) {
10777c478bd9Sstevel@tonic-gate 		e = SOCKET_ERRNO;
10787c478bd9Sstevel@tonic-gate 		goto kill_conn;
10797c478bd9Sstevel@tonic-gate 	    }
10807c478bd9Sstevel@tonic-gate 	    conn->x.in.bufsizebytes_read += nread;
10817c478bd9Sstevel@tonic-gate 	    if (conn->x.in.bufsizebytes_read == 4) {
10827c478bd9Sstevel@tonic-gate 		unsigned long len;
10837c478bd9Sstevel@tonic-gate 		len = conn->x.in.bufsizebytes[0];
10847c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[1];
10857c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[2];
10867c478bd9Sstevel@tonic-gate 		len = (len << 8) + conn->x.in.bufsizebytes[3];
10877c478bd9Sstevel@tonic-gate 		/*LINTED*/
10887c478bd9Sstevel@tonic-gate 		dprint("received length on fd %d is %d\n", conn->fd, (int)len);
10897c478bd9Sstevel@tonic-gate 		/* Arbitrary 1M cap.  */
10907c478bd9Sstevel@tonic-gate 		if (len > 1 * 1024 * 1024) {
10917c478bd9Sstevel@tonic-gate 		    e = E2BIG;
10927c478bd9Sstevel@tonic-gate 		    goto kill_conn;
10937c478bd9Sstevel@tonic-gate 		}
10947c478bd9Sstevel@tonic-gate 		conn->x.in.bufsize = conn->x.in.n_left = len;
10957c478bd9Sstevel@tonic-gate 		conn->x.in.buf = conn->x.in.pos = malloc(len);
10967c478bd9Sstevel@tonic-gate 		/*LINTED*/
10977c478bd9Sstevel@tonic-gate 		dprint("allocated %d byte buffer at %p\n", (int) len,
10987c478bd9Sstevel@tonic-gate 		       conn->x.in.buf);
10997c478bd9Sstevel@tonic-gate 		if (conn->x.in.buf == 0) {
11007c478bd9Sstevel@tonic-gate 		    /* allocation failure */
11017c478bd9Sstevel@tonic-gate 		    e = errno;
11027c478bd9Sstevel@tonic-gate 		    goto kill_conn;
11037c478bd9Sstevel@tonic-gate 		}
11047c478bd9Sstevel@tonic-gate 	    }
11057c478bd9Sstevel@tonic-gate 	}
11067c478bd9Sstevel@tonic-gate 	break;
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate     default:
11097c478bd9Sstevel@tonic-gate 	abort();
11107c478bd9Sstevel@tonic-gate     }
11117c478bd9Sstevel@tonic-gate     return 0;
11127c478bd9Sstevel@tonic-gate }
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate static int
11157c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate,
11167c478bd9Sstevel@tonic-gate 	       int ssflags)
11177c478bd9Sstevel@tonic-gate {
11187c478bd9Sstevel@tonic-gate     int nread;
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate     if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
11217c478bd9Sstevel@tonic-gate 	abort();
11227c478bd9Sstevel@tonic-gate     if (conn->state != READING)
11237c478bd9Sstevel@tonic-gate 	abort();
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate     nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
11267c478bd9Sstevel@tonic-gate     if (nread < 0) {
11277c478bd9Sstevel@tonic-gate 	kill_conn(conn, selstate, SOCKET_ERRNO);
11287c478bd9Sstevel@tonic-gate 	return 0;
11297c478bd9Sstevel@tonic-gate     }
11307c478bd9Sstevel@tonic-gate     conn->x.in.pos = conn->x.in.buf + nread;
11317c478bd9Sstevel@tonic-gate     return 1;
11327c478bd9Sstevel@tonic-gate }
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate static int
1135159d09a2SMark Phalan service_fds (krb5_context context,
1136159d09a2SMark Phalan 	     struct select_state *selstate,
1137159d09a2SMark Phalan 	     struct conn_state *conns, size_t n_conns, int *winning_conn,
1138159d09a2SMark Phalan 	     struct select_state *seltemp,
1139159d09a2SMark Phalan 	     int (*msg_handler)(krb5_context, const krb5_data *, void *),
1140159d09a2SMark Phalan 	     void *msg_handler_data)
11417c478bd9Sstevel@tonic-gate {
11427c478bd9Sstevel@tonic-gate     int e, selret;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate     e = 0;
11457c478bd9Sstevel@tonic-gate     while (selstate->nfds > 0
1146159d09a2SMark Phalan 	   && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) {
11477c478bd9Sstevel@tonic-gate 	int i;
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	/*LINTED*/
11507c478bd9Sstevel@tonic-gate 	dprint("service_fds examining results, selret=%d\n", selret);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	if (selret == 0)
11537c478bd9Sstevel@tonic-gate 	    /* Timeout, return to caller.  */
11547c478bd9Sstevel@tonic-gate 	    return 0;
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 	/* Got something on a socket, process it.  */
11577c478bd9Sstevel@tonic-gate 	for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) {
11587c478bd9Sstevel@tonic-gate 	    int ssflags;
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	    if (conns[i].fd == INVALID_SOCKET)
11617c478bd9Sstevel@tonic-gate 		continue;
11627c478bd9Sstevel@tonic-gate 	    ssflags = 0;
1163159d09a2SMark Phalan 	    if (FD_ISSET(conns[i].fd, &seltemp->rfds))
11647c478bd9Sstevel@tonic-gate 		ssflags |= SSF_READ, selret--;
1165159d09a2SMark Phalan 	    if (FD_ISSET(conns[i].fd, &seltemp->wfds))
11667c478bd9Sstevel@tonic-gate 		ssflags |= SSF_WRITE, selret--;
1167159d09a2SMark Phalan 	    if (FD_ISSET(conns[i].fd, &seltemp->xfds))
11687c478bd9Sstevel@tonic-gate 		ssflags |= SSF_EXCEPTION, selret--;
11697c478bd9Sstevel@tonic-gate 	    if (!ssflags)
11707c478bd9Sstevel@tonic-gate 		continue;
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 	    /*LINTED*/
11737c478bd9Sstevel@tonic-gate 	    dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
11747c478bd9Sstevel@tonic-gate 		    /*LINTED*/
11757c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_READ) ? "r" : "",
11767c478bd9Sstevel@tonic-gate 		    /*LINTED*/
11777c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_WRITE) ? "w" : "",
11787c478bd9Sstevel@tonic-gate 		    /*LINTED*/
11797c478bd9Sstevel@tonic-gate 		   (ssflags & SSF_EXCEPTION) ? "x" : "",
11807c478bd9Sstevel@tonic-gate 		    /*LINTED*/
11817c478bd9Sstevel@tonic-gate 		   conns[i].fd, conns[i].addr,
11827c478bd9Sstevel@tonic-gate 		   state_strings[(int) conns[i].state]);
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 	    if (conns[i].service (&conns[i], selstate, ssflags)) {
1185159d09a2SMark Phalan 		int stop = 1;
1186159d09a2SMark Phalan 
1187159d09a2SMark Phalan 		if (msg_handler != NULL) {
1188159d09a2SMark Phalan 		    krb5_data reply;
1189159d09a2SMark Phalan 
1190159d09a2SMark Phalan 		    reply.data = conns[i].x.in.buf;
1191159d09a2SMark Phalan 		    reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
1192159d09a2SMark Phalan 
1193159d09a2SMark Phalan 		    stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1194159d09a2SMark Phalan 		}
1195159d09a2SMark Phalan 
1196159d09a2SMark Phalan 		if (stop) {
1197159d09a2SMark Phalan 		    dprint("fd service routine says we're done\n");
1198159d09a2SMark Phalan 		    *winning_conn = i;
1199159d09a2SMark Phalan 		    return 1;
1200159d09a2SMark Phalan 		}
12017c478bd9Sstevel@tonic-gate 	    }
12027c478bd9Sstevel@tonic-gate 	}
12037c478bd9Sstevel@tonic-gate     }
12047c478bd9Sstevel@tonic-gate     if (e != 0) {
12057c478bd9Sstevel@tonic-gate 	/*LINTED*/
12067c478bd9Sstevel@tonic-gate 	dprint("select returned %m\n", e);
12077c478bd9Sstevel@tonic-gate 	*winning_conn = -1;
12087c478bd9Sstevel@tonic-gate 	return 1;
12097c478bd9Sstevel@tonic-gate     }
12107c478bd9Sstevel@tonic-gate     return 0;
12117c478bd9Sstevel@tonic-gate }
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate /*
12147c478bd9Sstevel@tonic-gate  * Current worst-case timeout behavior:
12157c478bd9Sstevel@tonic-gate  *
12167c478bd9Sstevel@tonic-gate  * First pass, 1s per udp or tcp server, plus 2s at end.
12177c478bd9Sstevel@tonic-gate  * Second pass, 1s per udp server, plus 4s.
12187c478bd9Sstevel@tonic-gate  * Third pass, 1s per udp server, plus 8s.
12197c478bd9Sstevel@tonic-gate  * Fourth => 16s, etc.
12207c478bd9Sstevel@tonic-gate  *
12217c478bd9Sstevel@tonic-gate  * Restated:
12227c478bd9Sstevel@tonic-gate  * Per UDP server, 1s per pass.
12237c478bd9Sstevel@tonic-gate  * Per TCP server, 1s.
12247c478bd9Sstevel@tonic-gate  * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
12257c478bd9Sstevel@tonic-gate  *
12267c478bd9Sstevel@tonic-gate  * Total = 2**(P+1) + U*P + T - 2.
12277c478bd9Sstevel@tonic-gate  *
12287c478bd9Sstevel@tonic-gate  * If P=3, Total = 3*U + T + 14.
12297c478bd9Sstevel@tonic-gate  * If P=4, Total = 4*U + T + 30.
12307c478bd9Sstevel@tonic-gate  *
12317c478bd9Sstevel@tonic-gate  * Note that if you try to reach two ports (e.g., both 88 and 750) on
12327c478bd9Sstevel@tonic-gate  * one server, it counts as two.
12337c478bd9Sstevel@tonic-gate  */
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate krb5_error_code
12367c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12377c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message,
1238159d09a2SMark Phalan                 const struct addrlist *addrs,
1239159d09a2SMark Phalan 		struct sendto_callback_info* callback_info, krb5_data *reply,
1240159d09a2SMark Phalan 		struct sockaddr *localaddr, socklen_t *localaddrlen,
1241159d09a2SMark Phalan                 struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1242159d09a2SMark Phalan 		int *addr_used,
1243159d09a2SMark Phalan 		/* return 0 -> keep going, 1 -> quit */
1244159d09a2SMark Phalan 		int (*msg_handler)(krb5_context, const krb5_data *, void *),
1245159d09a2SMark Phalan 		void *msg_handler_data)
12467c478bd9Sstevel@tonic-gate {
12477c478bd9Sstevel@tonic-gate     int i, pass;
12487c478bd9Sstevel@tonic-gate     int delay_this_pass = 2;
12497c478bd9Sstevel@tonic-gate     krb5_error_code retval;
12507c478bd9Sstevel@tonic-gate     struct conn_state *conns;
1251159d09a2SMark Phalan     krb5_data *callback_data = 0;
12527c478bd9Sstevel@tonic-gate     size_t n_conns, host;
1253159d09a2SMark Phalan     struct select_state *sel_state;
12547c478bd9Sstevel@tonic-gate     struct timeval now;
12557c478bd9Sstevel@tonic-gate     int winning_conn = -1, e = 0;
12567c478bd9Sstevel@tonic-gate     char *udpbuf = 0;
12577c478bd9Sstevel@tonic-gate 
1258159d09a2SMark Phalan     if (message)
1259159d09a2SMark Phalan 	dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
1260159d09a2SMark Phalan     else
1261159d09a2SMark Phalan 	dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
1262159d09a2SMark Phalan     print_addrlist(addrs);
1263159d09a2SMark Phalan     dprint(")\n");
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate     reply->data = 0;
12667c478bd9Sstevel@tonic-gate     reply->length = 0;
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate     n_conns = addrs->naddrs;
12697c478bd9Sstevel@tonic-gate     conns = malloc(n_conns * sizeof(struct conn_state));
12707c478bd9Sstevel@tonic-gate     if (conns == NULL) {
12717c478bd9Sstevel@tonic-gate 	return ENOMEM;
12727c478bd9Sstevel@tonic-gate     }
1273159d09a2SMark Phalan 
1274159d09a2SMark Phalan     memset(conns, 0, n_conns * sizeof(struct conn_state));
1275159d09a2SMark Phalan 
1276159d09a2SMark Phalan     if (callback_info) {
1277159d09a2SMark Phalan 	callback_data = malloc(n_conns * sizeof(krb5_data));
1278159d09a2SMark Phalan 	if (callback_data == NULL) {
1279159d09a2SMark Phalan 	    return ENOMEM;
1280159d09a2SMark Phalan 	}
1281159d09a2SMark Phalan 
1282159d09a2SMark Phalan 	memset(callback_data, 0, n_conns * sizeof(krb5_data));
1283159d09a2SMark Phalan     }
1284159d09a2SMark Phalan 
12857c478bd9Sstevel@tonic-gate     for (i = 0; i < n_conns; i++) {
12867c478bd9Sstevel@tonic-gate 	conns[i].fd = INVALID_SOCKET;
12877c478bd9Sstevel@tonic-gate     }
12887c478bd9Sstevel@tonic-gate 
1289159d09a2SMark Phalan     /* One for use here, listing all our fds in use, and one for
1290159d09a2SMark Phalan        temporary use in service_fds, for the fds of interest.  */
1291159d09a2SMark Phalan     sel_state = malloc(2 * sizeof(*sel_state));
1292159d09a2SMark Phalan     if (sel_state == NULL) {
1293159d09a2SMark Phalan 	free(conns);
1294159d09a2SMark Phalan 	return ENOMEM;
1295159d09a2SMark Phalan     }
1296159d09a2SMark Phalan     sel_state->max = 0;
1297159d09a2SMark Phalan     sel_state->nfds = 0;
1298159d09a2SMark Phalan     sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
1299159d09a2SMark Phalan     FD_ZERO(&sel_state->rfds);
1300159d09a2SMark Phalan     FD_ZERO(&sel_state->wfds);
1301159d09a2SMark Phalan     FD_ZERO(&sel_state->xfds);
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate     /* Set up connections.  */
13057c478bd9Sstevel@tonic-gate     for (host = 0; host < n_conns; host++) {
1306159d09a2SMark Phalan 	retval = setup_connection(&conns[host],
1307159d09a2SMark Phalan 				  addrs->addrs[host].ai,
1308159d09a2SMark Phalan 				  message,
1309159d09a2SMark Phalan 				  &udpbuf);
13107c478bd9Sstevel@tonic-gate 	if (retval)
13117c478bd9Sstevel@tonic-gate 	    continue;
13127c478bd9Sstevel@tonic-gate     }
13137c478bd9Sstevel@tonic-gate     for (pass = 0; pass < MAX_PASS; pass++) {
13147c478bd9Sstevel@tonic-gate 	/* Possible optimization: Make only one pass if TCP only.
13157c478bd9Sstevel@tonic-gate 	   Stop making passes if all UDP ports are closed down.  */
13167c478bd9Sstevel@tonic-gate 	/*LINTED*/
13177c478bd9Sstevel@tonic-gate 	dprint("pass %d delay=%d\n", pass, delay_this_pass);
13187c478bd9Sstevel@tonic-gate 	for (host = 0; host < n_conns; host++) {
13197c478bd9Sstevel@tonic-gate 	    /*LINTED*/
13207c478bd9Sstevel@tonic-gate 	    dprint("host %d\n", host);
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	    /* Send to the host, wait for a response, then move on. */
1323159d09a2SMark Phalan 	    if (maybe_send(&conns[host],
1324159d09a2SMark Phalan 			   sel_state,
1325159d09a2SMark Phalan 			   callback_info,
1326159d09a2SMark Phalan 			   (callback_info ? &callback_data[host] : NULL)))
13277c478bd9Sstevel@tonic-gate 		continue;
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	    retval = getcurtime(&now);
13307c478bd9Sstevel@tonic-gate 	    if (retval)
13317c478bd9Sstevel@tonic-gate 		goto egress;
1332159d09a2SMark Phalan 	    sel_state->end_time = now;
1333159d09a2SMark Phalan 	    sel_state->end_time.tv_sec += 1;
1334159d09a2SMark Phalan 	    e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1335159d09a2SMark Phalan 			    sel_state+1, msg_handler, msg_handler_data);
13367c478bd9Sstevel@tonic-gate 	    if (e)
13377c478bd9Sstevel@tonic-gate 		break;
1338159d09a2SMark Phalan 	    if (pass > 0 && sel_state->nfds == 0)
13397c478bd9Sstevel@tonic-gate 		/*
13407c478bd9Sstevel@tonic-gate 		 * After the first pass, if we close all fds, break
13417c478bd9Sstevel@tonic-gate 		 * out right away.  During the first pass, it's okay,
13427c478bd9Sstevel@tonic-gate 		 * we're probably about to open another connection.
13437c478bd9Sstevel@tonic-gate 		 */
13447c478bd9Sstevel@tonic-gate 		break;
13457c478bd9Sstevel@tonic-gate 	}
13467c478bd9Sstevel@tonic-gate 	if (e)
13477c478bd9Sstevel@tonic-gate 	    break;
13487c478bd9Sstevel@tonic-gate 	retval = getcurtime(&now);
13497c478bd9Sstevel@tonic-gate 	if (retval)
13507c478bd9Sstevel@tonic-gate 	    goto egress;
13517c478bd9Sstevel@tonic-gate 	/* Possible optimization: Find a way to integrate this select
13527c478bd9Sstevel@tonic-gate 	   call with the last one from the above loop, if the loop
13537c478bd9Sstevel@tonic-gate 	   actually calls select.  */
1354159d09a2SMark Phalan 	sel_state->end_time.tv_sec += delay_this_pass;
1355159d09a2SMark Phalan 	e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1356159d09a2SMark Phalan 		        sel_state+1, msg_handler, msg_handler_data);
13577c478bd9Sstevel@tonic-gate 	if (e)
13587c478bd9Sstevel@tonic-gate 	    break;
1359159d09a2SMark Phalan 	if (sel_state->nfds == 0)
13607c478bd9Sstevel@tonic-gate 	    break;
13617c478bd9Sstevel@tonic-gate 	delay_this_pass *= 2;
13627c478bd9Sstevel@tonic-gate     }
13637c478bd9Sstevel@tonic-gate 
1364159d09a2SMark Phalan     if (sel_state->nfds == 0) {
13657c478bd9Sstevel@tonic-gate 	/* No addresses?  */
13667c478bd9Sstevel@tonic-gate 	retval = KRB5_KDC_UNREACH;
13677c478bd9Sstevel@tonic-gate 	goto egress;
13687c478bd9Sstevel@tonic-gate     }
13697c478bd9Sstevel@tonic-gate     if (e == 0 || winning_conn < 0) {
13707c478bd9Sstevel@tonic-gate 	retval = KRB5_KDC_UNREACH;
13717c478bd9Sstevel@tonic-gate 	goto egress;
13727c478bd9Sstevel@tonic-gate     }
13737c478bd9Sstevel@tonic-gate     /* Success!  */
13747c478bd9Sstevel@tonic-gate     reply->data = conns[winning_conn].x.in.buf;
13757c478bd9Sstevel@tonic-gate     reply->length = (conns[winning_conn].x.in.pos
13767c478bd9Sstevel@tonic-gate 		     - conns[winning_conn].x.in.buf);
13777c478bd9Sstevel@tonic-gate     /*LINTED*/
1378159d09a2SMark Phalan     dprint("returning %d bytes in buffer %p\n",
1379159d09a2SMark Phalan 	   (int) reply->length, reply->data);
13807c478bd9Sstevel@tonic-gate     retval = 0;
13817c478bd9Sstevel@tonic-gate     conns[winning_conn].x.in.buf = 0;
1382505d05c7Sgtb     if (addr_used)
1383159d09a2SMark Phalan         *addr_used = winning_conn;
13847c478bd9Sstevel@tonic-gate     if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
1385159d09a2SMark Phalan 	(void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
1386159d09a2SMark Phalan 
1387159d09a2SMark Phalan 	if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
1388159d09a2SMark Phalan 	(void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
1389159d09a2SMark Phalan 
13907c478bd9Sstevel@tonic-gate egress:
13917c478bd9Sstevel@tonic-gate     for (i = 0; i < n_conns; i++) {
13927c478bd9Sstevel@tonic-gate 	if (conns[i].fd != INVALID_SOCKET)
1393159d09a2SMark Phalan 	    closesocket(conns[i].fd);
13947c478bd9Sstevel@tonic-gate 	if (conns[i].state == READING
13957c478bd9Sstevel@tonic-gate 	    && conns[i].x.in.buf != 0
13967c478bd9Sstevel@tonic-gate 	    && conns[i].x.in.buf != udpbuf)
13977c478bd9Sstevel@tonic-gate 	    free(conns[i].x.in.buf);
1398159d09a2SMark Phalan 	if (callback_info) {
1399159d09a2SMark Phalan 	    callback_info->pfn_cleanup( callback_info->context, &callback_data[i]);
1400159d09a2SMark Phalan 	}
14017c478bd9Sstevel@tonic-gate     }
1402159d09a2SMark Phalan 
1403159d09a2SMark Phalan     if (callback_data)
1404159d09a2SMark Phalan 	free(callback_data);
1405159d09a2SMark Phalan 
14067c478bd9Sstevel@tonic-gate     free(conns);
14077c478bd9Sstevel@tonic-gate     if (reply->data != udpbuf)
14087c478bd9Sstevel@tonic-gate 	free(udpbuf);
1409159d09a2SMark Phalan     free(sel_state);
14107c478bd9Sstevel@tonic-gate     return retval;
14117c478bd9Sstevel@tonic-gate }
1412