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