xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sendto_kdc.c (revision 23a1ccea6aac035f084a7a4cdc968687d1b02daf)
1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * lib/krb5/os/sendto_kdc.c
6  *
7  * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology.
8  * All Rights Reserved.
9  *
10  * Export of this software from the United States of America may
11  *   require a specific license from the United States Government.
12  *   It is the responsibility of any person or organization contemplating
13  *   export to obtain such a license before exporting.
14  *
15  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16  * distribute this software and its documentation for any purpose and
17  * without fee is hereby granted, provided that the above copyright
18  * notice appear in all copies and that both that copyright notice and
19  * this permission notice appear in supporting documentation, and that
20  * the name of M.I.T. not be used in advertising or publicity pertaining
21  * to distribution of the software without specific, written prior
22  * permission.  Furthermore if you modify this software you must label
23  * your software as modified software and not distribute it in such a
24  * fashion that it might be confused with the original M.I.T. software.
25  * M.I.T. makes no representations about the suitability of
26  * this software for any purpose.  It is provided "as is" without express
27  * or implied warranty.
28  *
29  *
30  * Send packet to KDC for realm; wait for response, retransmitting
31  * as necessary.
32  */
33 
34 #include "fake-addrinfo.h"
35 #include "k5-int.h"
36 
37 /* Solaris Kerberos */
38 #include <syslog.h>
39 
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #else
43 #include <time.h>
44 #endif
45 #include "os-proto.h"
46 #ifdef _WIN32
47 #include <sys/timeb.h>
48 #endif
49 
50 #ifdef _AIX
51 #include <sys/select.h>
52 #endif
53 
54 #ifndef _WIN32
55 /* For FIONBIO.  */
56 #include <sys/ioctl.h>
57 #ifdef HAVE_SYS_FILIO_H
58 #include <sys/filio.h>
59 #endif
60 #endif
61 
62 #define MAX_PASS		    3
63 /* Solaris Kerberos: moved to k5-int.h */
64 /* #define DEFAULT_UDP_PREF_LIMIT	 1465 */
65 #define HARD_UDP_LIMIT		32700 /* could probably do 64K-epsilon ? */
66 
67 #undef DEBUG
68 
69 #ifdef DEBUG
70 int krb5int_debug_sendto_kdc = 0;
71 #define debug krb5int_debug_sendto_kdc
72 
73 static void default_debug_handler (const void *data, size_t len)
74 {
75 #if 0
76     FILE *logfile;
77     logfile = fopen("/tmp/sendto_kdc.log", "a");
78     if (logfile == NULL)
79 	return;
80     fwrite(data, 1, len, logfile);
81     fclose(logfile);
82 #else
83     fwrite(data, 1, len, stderr);
84     /* stderr is unbuffered */
85 #endif
86 }
87 
88 void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler;
89 
90 /*
91  * Solaris Kerberos: only including the debug stuff if DEBUG defined outside
92  * this file.
93  */
94 static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024];
95 
96 /* Solaris kerberos: removed put() since it isn't needed. */
97 #if 0
98 static void put(const void *ptr, size_t len)
99 {
100     (*krb5int_sendtokdc_debug_handler)(ptr, len);
101 }
102 #endif
103 
104 static void putstr(const char *str)
105 {
106     /* Solaris kerberos: build the string which will be passed to syslog later */
107     strlcat(global_err_str, str, sizeof (global_err_str));
108 }
109 #else
110 void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0;
111 #endif
112 
113 #define dprint krb5int_debug_fprint
114  void
115 krb5int_debug_fprint (const char *fmt, ...)
116 {
117 #ifdef DEBUG
118     va_list args;
119 
120     /* Temporaries for variable arguments, etc.  */
121     krb5_error_code kerr;
122     int err;
123     fd_set *rfds, *wfds, *xfds;
124     int i;
125     int maxfd;
126     struct timeval *tv;
127     struct addrinfo *ai;
128     const krb5_data *d;
129     char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
130     const char *p;
131 #ifndef max
132 #define max(a,b) ((a) > (b) ? (a) : (b))
133 #endif
134     char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)];
135 
136     /*
137      * Solaris kerberos: modified this function to create a string to pass to
138      * syslog()
139      */
140     global_err_str[0] = NULL;
141 
142     va_start(args, fmt);
143 
144 #define putf(FMT,X)	(sprintf(tmpbuf,FMT,X),putstr(tmpbuf))
145 
146     for (; *fmt; fmt++) {
147 	if (*fmt != '%') {
148 	    /* Possible optimization: Look for % and print all chars
149 	       up to it in one call.  */
150 	    putf("%c", *fmt);
151 	    continue;
152 	}
153 	/* After this, always processing a '%' sequence.  */
154 	fmt++;
155 	switch (*fmt) {
156 	case 0:
157 	default:
158 	    abort();
159 	case 'E':
160 	    /* %E => krb5_error_code */
161 	    kerr = va_arg(args, krb5_error_code);
162 	    sprintf(tmpbuf, "%lu/", (unsigned long) kerr);
163 	    putstr(tmpbuf);
164 	    p = error_message(kerr);
165 	    putstr(p);
166 	    break;
167 	case 'm':
168 	    /* %m => errno value (int) */
169 	    /* Like syslog's %m except the errno value is passed in
170 	       rather than the current value.  */
171 	    err = va_arg(args, int);
172 	    putf("%d/", err);
173 	    p = NULL;
174 #ifdef HAVE_STRERROR_R
175 	    if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
176 		p = tmpbuf;
177 #endif
178 	    if (p == NULL)
179 		p = strerror(err);
180 	    putstr(p);
181 	    break;
182 	case 'F':
183 	    /* %F => fd_set *, fd_set *, fd_set *, int */
184 	    rfds = va_arg(args, fd_set *);
185 	    wfds = va_arg(args, fd_set *);
186 	    xfds = va_arg(args, fd_set *);
187 	    maxfd = va_arg(args, int);
188 
189 	    for (i = 0; i < maxfd; i++) {
190 		int r = FD_ISSET(i, rfds);
191 		int w = wfds && FD_ISSET(i, wfds);
192 		int x = xfds && FD_ISSET(i, xfds);
193 		if (r || w || x) {
194 		    putf(" %d", i);
195 		    if (r)
196 			putstr("r");
197 		    if (w)
198 			putstr("w");
199 		    if (x)
200 			putstr("x");
201 		}
202 	    }
203 	    putstr(" ");
204 	    break;
205 	case 's':
206 	    /* %s => char * */
207 	    p = va_arg(args, const char *);
208 	    putstr(p);
209 	    break;
210 	case 't':
211 	    /* %t => struct timeval * */
212 	    tv = va_arg(args, struct timeval *);
213 	    if (tv) {
214 		sprintf(tmpbuf, "%ld.%06ld",
215 			(long) tv->tv_sec, (long) tv->tv_usec);
216 		putstr(tmpbuf);
217 	    } else
218 		putstr("never");
219 	    break;
220 	case 'd':
221 	    /* %d => int */
222 	    putf("%d", va_arg(args, int));
223 	    break;
224 	case 'p':
225 	    /* %p => pointer */
226 	    putf("%p", va_arg(args, void*));
227 	    break;
228 	case 'A':
229 	    /* %A => addrinfo */
230 	    ai = va_arg(args, struct addrinfo *);
231 	    if (ai->ai_socktype == SOCK_DGRAM)
232 		strcpy(tmpbuf, "dgram");
233 	    else if (ai->ai_socktype == SOCK_STREAM)
234 		strcpy(tmpbuf, "stream");
235 	    else
236 		sprintf(tmpbuf, "socktype%d", ai->ai_socktype);
237 	    if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen,
238 				  addrbuf, sizeof (addrbuf),
239 				  portbuf, sizeof (portbuf),
240 				  NI_NUMERICHOST | NI_NUMERICSERV)) {
241 		if (ai->ai_addr->sa_family == AF_UNSPEC)
242 		    strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC");
243 		else
244 		    sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family);
245 	    } else
246 		sprintf(tmpbuf + strlen(tmpbuf), " %s.%s", addrbuf, portbuf);
247 	    putstr(tmpbuf);
248 	    break;
249 	case 'D':
250 	    /* %D => krb5_data * */
251 	    d = va_arg(args, krb5_data *);
252 	    /* Solaris Kerberos */
253 	    p = d->data;
254 	    putstr("0x");
255 	    for (i = 0; i < d->length; i++) {
256 		putf("%.2x", *p++);
257 	    }
258 	    break;
259 	}
260     }
261     va_end(args);
262 
263     /* Solaris kerberos: use syslog() for debug output */
264     syslog(LOG_DEBUG, global_err_str);
265 #endif
266 }
267 
268 #define print_addrlist krb5int_print_addrlist
269 static void
270 print_addrlist (const struct addrlist *a)
271 {
272     int i;
273     dprint("%d{", a->naddrs);
274     for (i = 0; i < a->naddrs; i++)
275 	dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai);
276     dprint("}");
277 }
278 
279 static int
280 merge_addrlists (struct addrlist *dest, struct addrlist *src)
281 {
282     /* Wouldn't it be nice if we could filter out duplicates?  The
283        alloc/free handling makes that pretty difficult though.  */
284     int err, i;
285 
286 /* Solaris Kerberos */
287 #ifdef DEBUG
288     /*LINTED*/
289     dprint("merging addrlists:\n\tlist1: ");
290     for (i = 0; i < dest->naddrs; i++)
291 	/*LINTED*/
292 	dprint(" %A", dest->addrs[i].ai);
293     /*LINTED*/
294     dprint("\n\tlist2: ");
295     for (i = 0; i < src->naddrs; i++)
296 	/*LINTED*/
297 	dprint(" %A", src->addrs[i].ai);
298     /*LINTED*/
299     dprint("\n");
300 #endif
301 
302     err = krb5int_grow_addrlist (dest, src->naddrs);
303     if (err)
304 	return err;
305     for (i = 0; i < src->naddrs; i++) {
306 	dest->addrs[dest->naddrs + i] = src->addrs[i];
307 	src->addrs[i].ai = 0;
308 	src->addrs[i].freefn = 0;
309     }
310     dest->naddrs += i;
311     src->naddrs = 0;
312 
313 /* Solaris Kerberos */
314 #ifdef DEBUG
315     /*LINTED*/
316     dprint("\tout:   ");
317     for (i = 0; i < dest->naddrs; i++)
318 	/*LINTED*/
319 	dprint(" %A", dest->addrs[i].ai);
320     /*LINTED*/
321     dprint("\n");
322 #endif
323 
324     return 0;
325 }
326 
327 static int
328 in_addrlist (struct addrinfo *thisaddr, struct addrlist *list)
329 {
330     int i;
331     for (i = 0; i < list->naddrs; i++) {
332 	if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen
333 	    && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr,
334 		       thisaddr->ai_addrlen))
335 	    return 1;
336     }
337     return 0;
338 }
339 
340 static int
341 check_for_svc_unavailable (krb5_context context,
342 			   const krb5_data *reply,
343 			   void *msg_handler_data)
344 {
345     krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
346 
347     *retval = 0;
348 
349     if (krb5_is_krb_error(reply)) {
350 	krb5_error *err_reply;
351 
352 	if (decode_krb5_error(reply, &err_reply) == 0) {
353 	    *retval = err_reply->error;
354 	    krb5_free_error(context, err_reply);
355 
356 	    /* Returning 0 means continue to next KDC */
357 	    return (*retval != KDC_ERR_SVC_UNAVAILABLE);
358 	}
359     }
360 
361     return 1;
362 }
363 
364 /*
365  * send the formatted request 'message' to a KDC for realm 'realm' and
366  * return the response (if any) in 'reply'.
367  *
368  * If the message is sent and a response is received, 0 is returned,
369  * otherwise an error code is returned.
370  *
371  * The storage for 'reply' is allocated and should be freed by the caller
372  * when finished.
373  */
374 
375 krb5_error_code
376 krb5_sendto_kdc (krb5_context context, const krb5_data *message,
377 		 const krb5_data *realm, krb5_data *reply,
378 		 int *use_master, int tcp_only)
379 {
380     krb5_error_code retval, retval2;
381     struct addrlist addrs = ADDRLIST_INIT;	/* Solaris Kerberos */
382     int socktype1 = 0, socktype2 = 0, addr_used;
383 
384     /*
385      * find KDC location(s) for realm
386      */
387 
388     /*
389      * BUG: This code won't return "interesting" errors (e.g., out of mem,
390      * bad config file) from locate_kdc.  KRB5_REALM_CANT_RESOLVE can be
391      * ignored from one query of two, but if only one query is done, or
392      * both return that error, it should be returned to the caller.  Also,
393      * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
394      * should probably be returned as well.
395      */
396 
397     /*LINTED*/
398     dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
399     /*LINTED*/
400 	   message->length, message->data, realm, *use_master, tcp_only);
401 
402     if (!tcp_only && context->udp_pref_limit < 0) {
403 	int tmp;
404 	retval = profile_get_integer(context->profile,
405 				     "libdefaults", "udp_preference_limit", 0,
406 				     DEFAULT_UDP_PREF_LIMIT, &tmp);
407 	if (retval)
408 	    return retval;
409 	if (tmp < 0)
410 	    tmp = DEFAULT_UDP_PREF_LIMIT;
411 	else if (tmp > HARD_UDP_LIMIT)
412 	    /* In the unlikely case that a *really* big value is
413 	       given, let 'em use as big as we think we can
414 	       support.  */
415 	    tmp = HARD_UDP_LIMIT;
416 	context->udp_pref_limit = tmp;
417     }
418 
419     retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
420 
421     if (tcp_only)
422 	socktype1 = SOCK_STREAM, socktype2 = 0;
423     else if (message->length <= context->udp_pref_limit)
424 	socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
425     else
426 	socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
427 
428     retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
429     if (socktype2) {
430 	struct addrlist addrs2;
431 
432 	retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
433 				  socktype2, 0);
434 #if 0
435 	if (retval2 == 0) {
436 	    (void) merge_addrlists(&addrs, &addrs2);
437 	    krb5int_free_addrlist(&addrs2);
438 	    retval = 0;
439 	} else if (retval == KRB5_REALM_CANT_RESOLVE) {
440 	    retval = retval2;
441 	}
442 #else
443 	retval = retval2;
444 	if (retval == 0) {
445 	    (void) merge_addrlists(&addrs, &addrs2);
446 	    krb5int_free_addrlist(&addrs2);
447 	}
448 #endif
449     }
450 
451     if (addrs.naddrs > 0) {
452 	krb5_error_code err = 0;
453 
454         retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
455 				 0, 0, &addr_used, check_for_svc_unavailable, &err);
456 	switch (retval) {
457 	case 0:
458             /*
459              * Set use_master to 1 if we ended up talking to a master when
460              * we didn't explicitly request to
461              */
462             if (*use_master == 0) {
463                 struct addrlist addrs3;
464                 retval = krb5_locate_kdc(context, realm, &addrs3, 1,
465                                          addrs.addrs[addr_used].ai->ai_socktype,
466                                          addrs.addrs[addr_used].ai->ai_family);
467                 if (retval == 0) {
468 		    if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
469 			*use_master = 1;
470                     krb5int_free_addrlist (&addrs3);
471                 }
472             }
473             krb5int_free_addrlist (&addrs);
474             return 0;
475 	default:
476 	    break;
477 	    /* Cases here are for constructing useful error messages.  */
478 	case KRB5_KDC_UNREACH:
479 	    if (err == KDC_ERR_SVC_UNAVAILABLE) {
480 		retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
481 	    } else {
482 		krb5_set_error_message(context, retval,
483 				       "Cannot contact any KDC for realm '%.*s'",
484 				       realm->length, realm->data);
485 	    }
486 	    break;
487 	}
488         krb5int_free_addrlist (&addrs);
489     }
490     return retval;
491 }
492 
493 #ifdef DEBUG
494 
495 #ifdef _WIN32
496 #define dperror(MSG) \
497 	 dprint("%s: an error occurred ... "			\
498 		"\tline=%d errno=%m socketerrno=%m\n",		\
499 		(MSG), __LINE__, errno, SOCKET_ERRNO)
500 #else
501 #define dperror(MSG) dprint("%s: %m\n", MSG, errno)
502 #endif
503 #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0)
504 
505 #else /* ! DEBUG */
506 
507 #define dperror(MSG) ((void)(MSG))
508 #define dfprintf(ARGLIST) ((void)0)
509 
510 #endif
511 
512 /*
513  * Notes:
514  *
515  * Getting "connection refused" on a connected UDP socket causes
516  * select to indicate write capability on UNIX, but only shows up
517  * as an exception on Windows.  (I don't think any UNIX system flags
518  * the error as an exception.)  So we check for both, or make it
519  * system-specific.
520  *
521  * Always watch for responses from *any* of the servers.  Eventually
522  * fix the UDP code to do the same.
523  *
524  * To do:
525  * - TCP NOPUSH/CORK socket options?
526  * - error codes that don't suck
527  * - getsockopt(SO_ERROR) to check connect status
528  * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
529  *   connections already in progress
530  */
531 
532 #include "cm.h"
533 
534 static int getcurtime (struct timeval *tvp)
535 {
536 #ifdef _WIN32
537     struct _timeb tb;
538     _ftime(&tb);
539     tvp->tv_sec = tb.time;
540     tvp->tv_usec = tb.millitm * 1000;
541     /* Can _ftime fail?  */
542     return 0;
543 #else
544     if (gettimeofday(tvp, 0)) {
545 	dperror("gettimeofday");
546 	return errno;
547     }
548     return 0;
549 #endif
550 }
551 
552 /*
553  * Call select and return results.
554  * Input: interesting file descriptors and absolute timeout
555  * Output: select return value (-1 or num fds ready) and fd_sets
556  * Return: 0 (for i/o available or timeout) or error code.
557  */
558 krb5_error_code
559 krb5int_cm_call_select (const struct select_state *in,
560 			struct select_state *out, int *sret)
561 {
562     struct timeval now, *timo;
563     krb5_error_code e;
564 
565     *out = *in;
566     e = getcurtime(&now);
567     if (e)
568 	return e;
569     if (out->end_time.tv_sec == 0)
570 	timo = 0;
571     else {
572 	timo = &out->end_time;
573 	out->end_time.tv_sec -= now.tv_sec;
574 	out->end_time.tv_usec -= now.tv_usec;
575 	if (out->end_time.tv_usec < 0) {
576 	    out->end_time.tv_usec += 1000000;
577 	    out->end_time.tv_sec--;
578 	}
579 	if (out->end_time.tv_sec < 0) {
580 	    *sret = 0;
581 	    return 0;
582 	}
583     }
584     /*LINTED*/
585     dprint("selecting on max=%d sockets [%F] timeout %t\n",
586 	    /*LINTED*/
587 	   out->max,
588 	   &out->rfds, &out->wfds, &out->xfds, out->max,
589 	   timo);
590     *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
591     e = SOCKET_ERRNO;
592 
593 /* Solaris Kerberos */
594 #ifdef DEBUG
595     /*LINTED*/
596     dprint("select returns %d", *sret);
597     if (*sret < 0)
598 	/*LINTED*/
599 	dprint(", error = %E\n", e);
600     else if (*sret == 0)
601 	/*LINTED*/
602 	dprint(" (timeout)\n");
603     else
604 	/*LINTED*/
605 	dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
606 #endif
607 
608     if (*sret < 0)
609 	return e;
610     return 0;
611 }
612 
613 static int service_tcp_fd (struct conn_state *conn,
614 			   struct select_state *selstate, int ssflags);
615 static int service_udp_fd (struct conn_state *conn,
616 			   struct select_state *selstate, int ssflags);
617 
618 static void
619 set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
620 {
621     if (!message || message->length == 0)
622 	return;
623 
624     if (!state->is_udp) {
625 
626 	state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff;
627 	state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff;
628 	state->x.out.msg_len_buf[2] = (message->length >>  8) & 0xff;
629 	state->x.out.msg_len_buf[3] =  message->length        & 0xff;
630 
631 	SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
632 	SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
633    	state->x.out.sg_count = 2;
634 
635     } else {
636 
637 	SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
638 	SG_SET(&state->x.out.sgbuf[1], 0, 0);
639 	state->x.out.sg_count = 1;
640 
641     }
642 }
643 
644 
645 
646 static int
647 setup_connection (struct conn_state *state, struct addrinfo *ai,
648 		  const krb5_data *message, char **udpbufp)
649 {
650     state->state = INITIALIZING;
651     state->err = 0;
652     state->x.out.sgp = state->x.out.sgbuf;
653     state->addr = ai;
654     state->fd = INVALID_SOCKET;
655     SG_SET(&state->x.out.sgbuf[1], 0, 0);
656     if (ai->ai_socktype == SOCK_STREAM) {
657 	/*
658 	SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
659 	SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
660 	state->x.out.sg_count = 2;
661 	*/
662 
663 	state->is_udp = 0;
664 	state->service = service_tcp_fd;
665 	set_conn_state_msg_length (state, message);
666     } else {
667 	/*
668 	SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
669 	SG_SET(&state->x.out.sgbuf[1], 0, 0);
670 	state->x.out.sg_count = 1;
671 	*/
672 
673 	state->is_udp = 1;
674 	state->service = service_udp_fd;
675 	set_conn_state_msg_length (state, message);
676 
677 	if (*udpbufp == 0) {
678 	    *udpbufp = malloc(krb5_max_dgram_size);
679 	    if (*udpbufp == 0) {
680 		dperror("malloc(krb5_max_dgram_size)");
681 		(void) closesocket(state->fd);
682 		state->fd = INVALID_SOCKET;
683 		state->state = FAILED;
684 		return 1;
685 	    }
686 	}
687 	state->x.in.buf = *udpbufp;
688 	state->x.in.bufsize = krb5_max_dgram_size;
689     }
690     return 0;
691 }
692 
693 static int
694 start_connection (struct conn_state *state,
695 		  struct select_state *selstate,
696 		  struct sendto_callback_info* callback_info,
697                   krb5_data* callback_buffer)
698 {
699     int fd, e;
700     struct addrinfo *ai = state->addr;
701 
702     /*LINTED*/
703     dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
704 	   /*LINTED*/
705 	   ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
706     fd = socket(ai->ai_family, ai->ai_socktype, 0);
707     if (fd == INVALID_SOCKET) {
708 	state->err = SOCKET_ERRNO;
709 	/*LINTED*/
710 	dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
711 	return -1;		/* try other hosts */
712     }
713     /* Make it non-blocking.  */
714     if (ai->ai_socktype == SOCK_STREAM) {
715 	static const int one = 1;
716 	static const struct linger lopt = { 0, 0 };
717 
718 	if (ioctlsocket(fd, FIONBIO, (const void *) &one))
719 	    dperror("sendto_kdc: ioctl(FIONBIO)");
720 	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
721 	    dperror("sendto_kdc: setsockopt(SO_LINGER)");
722     }
723 
724     /* Start connecting to KDC.  */
725     /*LINTED*/
726     dprint(" fd %d; connecting to %A...\n", fd, ai);
727     e = connect(fd, ai->ai_addr, ai->ai_addrlen);
728     if (e != 0) {
729 	/*
730 	 * This is the path that should be followed for non-blocking
731 	 * connections.
732 	 */
733 	if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
734 	    state->state = CONNECTING;
735 	    state->fd = fd;
736 	} else {
737 	    /*LINTED*/
738 	    dprint("connect failed: %m\n", SOCKET_ERRNO);
739 	    (void) closesocket(fd);
740 	    state->err = SOCKET_ERRNO;
741 	    state->state = FAILED;
742 	    return -2;
743 	}
744     } else {
745 	/*
746 	 * Connect returned zero even though we tried to make it
747 	 * non-blocking, which should have caused it to return before
748 	 * finishing the connection.  Oh well.  Someone's network
749 	 * stack is broken, but if they gave us a connection, use it.
750 	 */
751 	state->state = WRITING;
752 	state->fd = fd;
753     }
754     /*LINTED*/
755     dprint("new state = %s\n", state_strings[state->state]);
756 
757 
758     /*
759      * Here's where KPASSWD callback gets the socket information it needs for
760      * a kpasswd request
761      */
762     if (callback_info) {
763 
764 	e = callback_info->pfn_callback(state,
765 					callback_info->context,
766 					callback_buffer);
767 	if (e != 0) {
768 	    dprint("callback failed: %m\n", e);
769 	    (void) closesocket(fd);
770 	    state->err = e;
771 	    state->fd = INVALID_SOCKET;
772 	    state->state = FAILED;
773 	    return -3;
774 	}
775 
776 	dprint("callback %p (message=%d@%p)\n",
777 	       state,
778 	       callback_buffer->length,
779 	       callback_buffer->data);
780 
781 	set_conn_state_msg_length( state, callback_buffer );
782     }
783 
784     if (ai->ai_socktype == SOCK_DGRAM) {
785 	/* Send it now.  */
786 	int ret;
787 	sg_buf *sg = &state->x.out.sgbuf[0];
788 
789 	/*LINTED*/
790 	dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
791 	ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
792 	if (ret != SG_LEN(sg)) {
793 	    dperror("sendto");
794 	    (void) closesocket(state->fd);
795 	    state->fd = INVALID_SOCKET;
796 	    state->state = FAILED;
797 	    return -4;
798 	} else {
799 	    state->state = READING;
800 	}
801     }
802 #ifdef DEBUG
803     if (debug) {
804 	struct sockaddr_storage ss;
805 	socklen_t sslen = sizeof(ss);
806 	if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
807 	    struct addrinfo hack_ai;
808 	    memset(&hack_ai, 0, sizeof(hack_ai));
809 	    hack_ai.ai_addr = (struct sockaddr *) &ss;
810 	    hack_ai.ai_addrlen = sslen;
811 	    hack_ai.ai_socktype = SOCK_DGRAM;
812 	    hack_ai.ai_family = ai->ai_family;
813 	    dprint("local socket address is %A\n", &hack_ai);
814 	}
815     }
816 #endif
817     FD_SET(state->fd, &selstate->rfds);
818     if (state->state == CONNECTING || state->state == WRITING)
819 	FD_SET(state->fd, &selstate->wfds);
820     FD_SET(state->fd, &selstate->xfds);
821     if (selstate->max <= state->fd)
822 	selstate->max = state->fd + 1;
823     selstate->nfds++;
824 
825     /*LINTED*/
826     dprint("new select vectors: %F\n",
827 	   /*LINTED*/
828 	   &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
829 
830     return 0;
831 }
832 
833 /* Return 0 if we sent something, non-0 otherwise.
834    If 0 is returned, the caller should delay waiting for a response.
835    Otherwise, the caller should immediately move on to process the
836    next connection.  */
837 static int
838 maybe_send (struct conn_state *conn,
839 	    struct select_state *selstate,
840 	    struct sendto_callback_info* callback_info,
841 	    krb5_data* callback_buffer)
842 {
843     sg_buf *sg;
844 
845     /*LINTED*/
846     dprint("maybe_send(@%p) state=%s type=%s\n", conn,
847 	   /*LINTED*/
848 	   state_strings[conn->state],
849 	   conn->is_udp ? "udp" : "tcp");
850     if (conn->state == INITIALIZING)
851 	return start_connection(conn, selstate, callback_info, callback_buffer);
852 
853     /* Did we already shut down this channel?  */
854     if (conn->state == FAILED) {
855 	dprint("connection already closed\n");
856 	return -1;
857     }
858 
859     if (conn->addr->ai_socktype == SOCK_STREAM) {
860 	dprint("skipping stream socket\n");
861 	/* The select callback will handle flushing any data we
862 	   haven't written yet, and we only write it once.  */
863 	return -1;
864     }
865 
866     /* UDP - Send message, possibly for the first time, possibly a
867        retransmit if a previous attempt timed out.  */
868     sg = &conn->x.out.sgbuf[0];
869     /*LINTED*/
870     dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
871     if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) {
872 	dperror("send");
873 	/* Keep connection alive, we'll try again next pass.
874 
875 	   Is this likely to catch any errors we didn't get from the
876 	   select callbacks?  */
877 	return -1;
878     }
879     /* Yay, it worked.  */
880     return 0;
881 }
882 
883 static void
884 kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
885 {
886     conn->state = FAILED;
887     shutdown(conn->fd, SHUTDOWN_BOTH);
888     FD_CLR(conn->fd, &selstate->rfds);
889     FD_CLR(conn->fd, &selstate->wfds);
890     FD_CLR(conn->fd, &selstate->xfds);
891     conn->err = err;
892     /*LINTED*/
893     dprint("abandoning connection %d: %m\n", conn->fd, err);
894     /* Fix up max fd for next select call.  */
895     if (selstate->max == 1 + conn->fd) {
896 	while (selstate->max > 0
897 	       && ! FD_ISSET(selstate->max-1, &selstate->rfds)
898 	       && ! FD_ISSET(selstate->max-1, &selstate->wfds)
899 	       && ! FD_ISSET(selstate->max-1, &selstate->xfds))
900 	    selstate->max--;
901 	/*LINTED*/
902 	dprint("new max_fd + 1 is %d\n", selstate->max);
903     }
904     selstate->nfds--;
905 }
906 
907 /* Check socket for error.  */
908 static int
909 get_so_error(int fd)
910 {
911     int e, sockerr;
912     socklen_t sockerrlen;
913 
914     sockerr = 0;
915     sockerrlen = sizeof(sockerr);
916     e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
917     if (e != 0) {
918 	/* What to do now?  */
919 	e = SOCKET_ERRNO;
920 	dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e);
921 	return e;
922     }
923     return sockerr;
924 }
925 
926 /* Return nonzero only if we're finished and the caller should exit
927    its loop.  This happens in two cases: We have a complete message,
928    or the socket has closed and no others are open.  */
929 
930 static int
931 service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
932 		int ssflags)
933 {
934     krb5_error_code e = 0;
935     int nwritten, nread;
936 
937     if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
938 	abort();
939     switch (conn->state) {
940 	SOCKET_WRITEV_TEMP tmp;
941 
942     case CONNECTING:
943 	if (ssflags & SSF_READ) {
944 	    /* Bad -- the KDC shouldn't be sending to us first.  */
945 	    e = EINVAL /* ?? */;
946 	kill_conn:
947 	    kill_conn(conn, selstate, e);
948 	    if (e == EINVAL) {
949 		closesocket(conn->fd);
950 		conn->fd = INVALID_SOCKET;
951 	    }
952 	    return e == 0;
953 	}
954 	if (ssflags & SSF_EXCEPTION) {
955 	handle_exception:
956 	    e = get_so_error(conn->fd);
957 	    if (e)
958 		dprint("socket error on exception fd: %m", e);
959 	    else
960 		dprint("no socket error info available on exception fd");
961 	    goto kill_conn;
962 	}
963 
964 	/*
965 	 * Connect finished -- but did it succeed or fail?
966 	 * UNIX sets can_write if failed.
967 	 * Call getsockopt to see if error pending.
968 	 *
969 	 * (For most UNIX systems it works to just try writing the
970 	 * first time and detect an error.  But Bill Dodd at IBM
971 	 * reports that some version of AIX, SIGPIPE can result.)
972 	 */
973 	e = get_so_error(conn->fd);
974 	if (e) {
975 	    dprint("socket error on write fd: %m", e);
976 	    goto kill_conn;
977 	}
978 	conn->state = WRITING;
979 	goto try_writing;
980 
981     case WRITING:
982 	if (ssflags & SSF_READ) {
983 	    e = E2BIG;
984 	    /* Bad -- the KDC shouldn't be sending anything yet.  */
985 	    goto kill_conn;
986 	}
987 	if (ssflags & SSF_EXCEPTION)
988 	    goto handle_exception;
989 
990     try_writing:
991 	/*LINTED*/
992 	dprint("trying to writev %d (%d bytes) to fd %d\n",
993 		/*LINTED*/
994 	       conn->x.out.sg_count,
995 	       ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
996 		/*LINTED*/
997 		+ SG_LEN(&conn->x.out.sgp[0])),
998 	       conn->fd);
999 	nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
1000 				 conn->x.out.sg_count, tmp);
1001 	if (nwritten < 0) {
1002 	    e = SOCKET_ERRNO;
1003 	    /*LINTED*/
1004 	    dprint("failed: %m\n", e);
1005 	    goto kill_conn;
1006 	}
1007 	/*LINTED*/
1008 	dprint("wrote %d bytes\n", nwritten);
1009 	while (nwritten) {
1010 	    sg_buf *sgp = conn->x.out.sgp;
1011 	    if (nwritten < SG_LEN(sgp)) {
1012 		/*LINTED*/
1013 		SG_ADVANCE(sgp, nwritten);
1014 		nwritten = 0;
1015 	    } else {
1016 		nwritten -= SG_LEN(conn->x.out.sgp);
1017 		conn->x.out.sgp++;
1018 		conn->x.out.sg_count--;
1019 		if (conn->x.out.sg_count == 0 && nwritten != 0)
1020 		    /* Wrote more than we wanted to?  */
1021 		    abort();
1022 	    }
1023 	}
1024 	if (conn->x.out.sg_count == 0) {
1025 	    /* Done writing, switch to reading.  */
1026 	    /* Don't call shutdown at this point because
1027 	     * some implementations cannot deal with half-closed connections.*/
1028 	    FD_CLR(conn->fd, &selstate->wfds);
1029 	    /* Q: How do we detect failures to send the remaining data
1030 	       to the remote side, since we're in non-blocking mode?
1031 	       Will we always get errors on the reading side?  */
1032 	    /*LINTED*/
1033 	    dprint("switching fd %d to READING\n", conn->fd);
1034 	    conn->state = READING;
1035 	    conn->x.in.bufsizebytes_read = 0;
1036 	    conn->x.in.bufsize = 0;
1037 	    conn->x.in.buf = 0;
1038 	    conn->x.in.pos = 0;
1039 	    conn->x.in.n_left = 0;
1040 	}
1041 	return 0;
1042 
1043     case READING:
1044 	if (ssflags & SSF_EXCEPTION) {
1045 	    if (conn->x.in.buf) {
1046 		free(conn->x.in.buf);
1047 		conn->x.in.buf = 0;
1048 	    }
1049 	    goto handle_exception;
1050 	}
1051 
1052 	if (conn->x.in.bufsizebytes_read == 4) {
1053 	    /* Reading data.  */
1054 	    /*LINTED*/
1055 	    dprint("reading %d bytes of data from fd %d\n",
1056 		   (int) conn->x.in.n_left, conn->fd);
1057 	    nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
1058 	    if (nread <= 0) {
1059 		e = nread ? SOCKET_ERRNO : ECONNRESET;
1060 		free(conn->x.in.buf);
1061 		conn->x.in.buf = 0;
1062 		goto kill_conn;
1063 	    }
1064 	    conn->x.in.n_left -= nread;
1065 	    conn->x.in.pos += nread;
1066 	    /* Solaris Kerberos */
1067 	    if ((long)conn->x.in.n_left <= 0) {
1068 		/* We win!  */
1069 		return 1;
1070 	    }
1071 	} else {
1072 	    /* Reading length.  */
1073 	    nread = SOCKET_READ(conn->fd,
1074 				conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
1075 				4 - conn->x.in.bufsizebytes_read);
1076 	    if (nread < 0) {
1077 		e = SOCKET_ERRNO;
1078 		goto kill_conn;
1079 	    }
1080 	    conn->x.in.bufsizebytes_read += nread;
1081 	    if (conn->x.in.bufsizebytes_read == 4) {
1082 		unsigned long len;
1083 		len = conn->x.in.bufsizebytes[0];
1084 		len = (len << 8) + conn->x.in.bufsizebytes[1];
1085 		len = (len << 8) + conn->x.in.bufsizebytes[2];
1086 		len = (len << 8) + conn->x.in.bufsizebytes[3];
1087 		/*LINTED*/
1088 		dprint("received length on fd %d is %d\n", conn->fd, (int)len);
1089 		/* Arbitrary 1M cap.  */
1090 		if (len > 1 * 1024 * 1024) {
1091 		    e = E2BIG;
1092 		    goto kill_conn;
1093 		}
1094 		conn->x.in.bufsize = conn->x.in.n_left = len;
1095 		conn->x.in.buf = conn->x.in.pos = malloc(len);
1096 		/*LINTED*/
1097 		dprint("allocated %d byte buffer at %p\n", (int) len,
1098 		       conn->x.in.buf);
1099 		if (conn->x.in.buf == 0) {
1100 		    /* allocation failure */
1101 		    e = errno;
1102 		    goto kill_conn;
1103 		}
1104 	    }
1105 	}
1106 	break;
1107 
1108     default:
1109 	abort();
1110     }
1111     return 0;
1112 }
1113 
1114 static int
1115 service_udp_fd(struct conn_state *conn, struct select_state *selstate,
1116 	       int ssflags)
1117 {
1118     int nread;
1119 
1120     if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
1121 	abort();
1122     if (conn->state != READING)
1123 	abort();
1124 
1125     nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
1126     if (nread < 0) {
1127 	kill_conn(conn, selstate, SOCKET_ERRNO);
1128 	return 0;
1129     }
1130     conn->x.in.pos = conn->x.in.buf + nread;
1131     return 1;
1132 }
1133 
1134 static int
1135 service_fds (krb5_context context,
1136 	     struct select_state *selstate,
1137 	     struct conn_state *conns, size_t n_conns, int *winning_conn,
1138 	     struct select_state *seltemp,
1139 	     int (*msg_handler)(krb5_context, const krb5_data *, void *),
1140 	     void *msg_handler_data)
1141 {
1142     int e, selret;
1143 
1144     e = 0;
1145     while (selstate->nfds > 0
1146 	   && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) {
1147 	int i;
1148 
1149 	/*LINTED*/
1150 	dprint("service_fds examining results, selret=%d\n", selret);
1151 
1152 	if (selret == 0)
1153 	    /* Timeout, return to caller.  */
1154 	    return 0;
1155 
1156 	/* Got something on a socket, process it.  */
1157 	for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) {
1158 	    int ssflags;
1159 
1160 	    if (conns[i].fd == INVALID_SOCKET)
1161 		continue;
1162 	    ssflags = 0;
1163 	    if (FD_ISSET(conns[i].fd, &seltemp->rfds))
1164 		ssflags |= SSF_READ, selret--;
1165 	    if (FD_ISSET(conns[i].fd, &seltemp->wfds))
1166 		ssflags |= SSF_WRITE, selret--;
1167 	    if (FD_ISSET(conns[i].fd, &seltemp->xfds))
1168 		ssflags |= SSF_EXCEPTION, selret--;
1169 	    if (!ssflags)
1170 		continue;
1171 
1172 	    /*LINTED*/
1173 	    dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
1174 		    /*LINTED*/
1175 		   (ssflags & SSF_READ) ? "r" : "",
1176 		    /*LINTED*/
1177 		   (ssflags & SSF_WRITE) ? "w" : "",
1178 		    /*LINTED*/
1179 		   (ssflags & SSF_EXCEPTION) ? "x" : "",
1180 		    /*LINTED*/
1181 		   conns[i].fd, conns[i].addr,
1182 		   state_strings[(int) conns[i].state]);
1183 
1184 	    if (conns[i].service (&conns[i], selstate, ssflags)) {
1185 		int stop = 1;
1186 
1187 		if (msg_handler != NULL) {
1188 		    krb5_data reply;
1189 
1190 		    reply.data = conns[i].x.in.buf;
1191 		    reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
1192 
1193 		    stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1194 		}
1195 
1196 		if (stop) {
1197 		    dprint("fd service routine says we're done\n");
1198 		    *winning_conn = i;
1199 		    return 1;
1200 		}
1201 	    }
1202 	}
1203     }
1204     if (e != 0) {
1205 	/*LINTED*/
1206 	dprint("select returned %m\n", e);
1207 	*winning_conn = -1;
1208 	return 1;
1209     }
1210     return 0;
1211 }
1212 
1213 /*
1214  * Current worst-case timeout behavior:
1215  *
1216  * First pass, 1s per udp or tcp server, plus 2s at end.
1217  * Second pass, 1s per udp server, plus 4s.
1218  * Third pass, 1s per udp server, plus 8s.
1219  * Fourth => 16s, etc.
1220  *
1221  * Restated:
1222  * Per UDP server, 1s per pass.
1223  * Per TCP server, 1s.
1224  * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
1225  *
1226  * Total = 2**(P+1) + U*P + T - 2.
1227  *
1228  * If P=3, Total = 3*U + T + 14.
1229  * If P=4, Total = 4*U + T + 30.
1230  *
1231  * Note that if you try to reach two ports (e.g., both 88 and 750) on
1232  * one server, it counts as two.
1233  */
1234 
1235 krb5_error_code
1236 /*ARGSUSED*/
1237 krb5int_sendto (krb5_context context, const krb5_data *message,
1238                 const struct addrlist *addrs,
1239 		struct sendto_callback_info* callback_info, krb5_data *reply,
1240 		struct sockaddr *localaddr, socklen_t *localaddrlen,
1241                 struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1242 		int *addr_used,
1243 		/* return 0 -> keep going, 1 -> quit */
1244 		int (*msg_handler)(krb5_context, const krb5_data *, void *),
1245 		void *msg_handler_data)
1246 {
1247     int i, pass;
1248     int delay_this_pass = 2;
1249     krb5_error_code retval;
1250     struct conn_state *conns;
1251     krb5_data *callback_data = 0;
1252     size_t n_conns, host;
1253     struct select_state *sel_state;
1254     struct timeval now;
1255     int winning_conn = -1, e = 0;
1256     char *udpbuf = 0;
1257 
1258     if (message)
1259 	dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
1260     else
1261 	dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
1262     print_addrlist(addrs);
1263     dprint(")\n");
1264 
1265     reply->data = 0;
1266     reply->length = 0;
1267 
1268     n_conns = addrs->naddrs;
1269     conns = malloc(n_conns * sizeof(struct conn_state));
1270     if (conns == NULL) {
1271 	return ENOMEM;
1272     }
1273 
1274     memset(conns, 0, n_conns * sizeof(struct conn_state));
1275 
1276     if (callback_info) {
1277 	callback_data = malloc(n_conns * sizeof(krb5_data));
1278 	if (callback_data == NULL) {
1279 	    return ENOMEM;
1280 	}
1281 
1282 	memset(callback_data, 0, n_conns * sizeof(krb5_data));
1283     }
1284 
1285     for (i = 0; i < n_conns; i++) {
1286 	conns[i].fd = INVALID_SOCKET;
1287     }
1288 
1289     /* One for use here, listing all our fds in use, and one for
1290        temporary use in service_fds, for the fds of interest.  */
1291     sel_state = malloc(2 * sizeof(*sel_state));
1292     if (sel_state == NULL) {
1293 	free(conns);
1294 	return ENOMEM;
1295     }
1296     sel_state->max = 0;
1297     sel_state->nfds = 0;
1298     sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
1299     FD_ZERO(&sel_state->rfds);
1300     FD_ZERO(&sel_state->wfds);
1301     FD_ZERO(&sel_state->xfds);
1302 
1303 
1304     /* Set up connections.  */
1305     for (host = 0; host < n_conns; host++) {
1306 	retval = setup_connection(&conns[host],
1307 				  addrs->addrs[host].ai,
1308 				  message,
1309 				  &udpbuf);
1310 	if (retval)
1311 	    continue;
1312     }
1313     for (pass = 0; pass < MAX_PASS; pass++) {
1314 	/* Possible optimization: Make only one pass if TCP only.
1315 	   Stop making passes if all UDP ports are closed down.  */
1316 	/*LINTED*/
1317 	dprint("pass %d delay=%d\n", pass, delay_this_pass);
1318 	for (host = 0; host < n_conns; host++) {
1319 	    /*LINTED*/
1320 	    dprint("host %d\n", host);
1321 
1322 	    /* Send to the host, wait for a response, then move on. */
1323 	    if (maybe_send(&conns[host],
1324 			   sel_state,
1325 			   callback_info,
1326 			   (callback_info ? &callback_data[host] : NULL)))
1327 		continue;
1328 
1329 	    retval = getcurtime(&now);
1330 	    if (retval)
1331 		goto egress;
1332 	    sel_state->end_time = now;
1333 	    sel_state->end_time.tv_sec += 1;
1334 	    e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1335 			    sel_state+1, msg_handler, msg_handler_data);
1336 	    if (e)
1337 		break;
1338 	    if (pass > 0 && sel_state->nfds == 0)
1339 		/*
1340 		 * After the first pass, if we close all fds, break
1341 		 * out right away.  During the first pass, it's okay,
1342 		 * we're probably about to open another connection.
1343 		 */
1344 		break;
1345 	}
1346 	if (e)
1347 	    break;
1348 	retval = getcurtime(&now);
1349 	if (retval)
1350 	    goto egress;
1351 	/* Possible optimization: Find a way to integrate this select
1352 	   call with the last one from the above loop, if the loop
1353 	   actually calls select.  */
1354 	sel_state->end_time.tv_sec += delay_this_pass;
1355 	e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1356 		        sel_state+1, msg_handler, msg_handler_data);
1357 	if (e)
1358 	    break;
1359 	if (sel_state->nfds == 0)
1360 	    break;
1361 	delay_this_pass *= 2;
1362     }
1363 
1364     if (sel_state->nfds == 0) {
1365 	/* No addresses?  */
1366 	retval = KRB5_KDC_UNREACH;
1367 	goto egress;
1368     }
1369     if (e == 0 || winning_conn < 0) {
1370 	retval = KRB5_KDC_UNREACH;
1371 	goto egress;
1372     }
1373     /* Success!  */
1374     reply->data = conns[winning_conn].x.in.buf;
1375     reply->length = (conns[winning_conn].x.in.pos
1376 		     - conns[winning_conn].x.in.buf);
1377     /*LINTED*/
1378     dprint("returning %d bytes in buffer %p\n",
1379 	   (int) reply->length, reply->data);
1380     retval = 0;
1381     conns[winning_conn].x.in.buf = 0;
1382     if (addr_used)
1383         *addr_used = winning_conn;
1384     if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
1385 	(void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
1386 
1387 	if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
1388 	(void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
1389 
1390 egress:
1391     for (i = 0; i < n_conns; i++) {
1392 	if (conns[i].fd != INVALID_SOCKET)
1393 	    closesocket(conns[i].fd);
1394 	if (conns[i].state == READING
1395 	    && conns[i].x.in.buf != 0
1396 	    && conns[i].x.in.buf != udpbuf)
1397 	    free(conns[i].x.in.buf);
1398 	if (callback_info) {
1399 	    callback_info->pfn_cleanup( callback_info->context, &callback_data[i]);
1400 	}
1401     }
1402 
1403     if (callback_data)
1404 	free(callback_data);
1405 
1406     free(conns);
1407     if (reply->data != udpbuf)
1408 	free(udpbuf);
1409     free(sel_state);
1410     return retval;
1411 }
1412