Home | History | Annotate | Download | only in os
      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