1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * lib/krb5/os/sendto_kdc.c
6 *
7 * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology.
8 * All Rights Reserved.
9 *
10 * Export of this software from the United States of America may
11 * require a specific license from the United States Government.
12 * It is the responsibility of any person or organization contemplating
13 * export to obtain such a license before exporting.
14 *
15 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16 * distribute this software and its documentation for any purpose and
17 * without fee is hereby granted, provided that the above copyright
18 * notice appear in all copies and that both that copyright notice and
19 * this permission notice appear in supporting documentation, and that
20 * the name of M.I.T. not be used in advertising or publicity pertaining
21 * to distribution of the software without specific, written prior
22 * permission. Furthermore if you modify this software you must label
23 * your software as modified software and not distribute it in such a
24 * fashion that it might be confused with the original M.I.T. software.
25 * M.I.T. makes no representations about the suitability of
26 * this software for any purpose. It is provided "as is" without express
27 * or implied warranty.
28 *
29 *
30 * Send packet to KDC for realm; wait for response, retransmitting
31 * as necessary.
32 */
33
34 #include "fake-addrinfo.h"
35 #include "k5-int.h"
36
37 /* Solaris Kerberos */
38 #include <syslog.h>
39 #include <locale.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
default_debug_handler(const void * data,size_t len)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
putstr(const char * str)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
krb5int_debug_fprint(const char * fmt,...)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
print_addrlist(const struct addrlist * a)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
merge_addrlists(struct addrlist * dest,struct addrlist * src)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
in_addrlist(struct addrinfo * thisaddr,struct addrlist * list)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
check_for_svc_unavailable(krb5_context context,const krb5_data * reply,void * msg_handler_data)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
krb5_sendto_kdc(krb5_context context,const krb5_data * message,const krb5_data * realm,krb5_data * reply,int * use_master,int tcp_only)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 return (krb5_sendto_kdc2(context, message, realm, reply, use_master,
382 tcp_only, NULL));
383 }
384
385 /*
386 * Solaris Kerberos
387 * Same as krb5_sendto_kdc plus an extra arg to return the FQDN
388 * of the KDC sent the request.
389 * Caller (at top of stack) needs to free hostname_used.
390 */
391 krb5_error_code
krb5_sendto_kdc2(krb5_context context,const krb5_data * message,const krb5_data * realm,krb5_data * reply,int * use_master,int tcp_only,char ** hostname_used)392 krb5_sendto_kdc2 (krb5_context context, const krb5_data *message,
393 const krb5_data *realm, krb5_data *reply,
394 int *use_master, int tcp_only, char **hostname_used)
395 {
396 krb5_error_code retval, retval2;
397 struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */
398 int socktype1 = 0, socktype2 = 0, addr_used;
399
400 /*
401 * find KDC location(s) for realm
402 */
403
404 /*
405 * BUG: This code won't return "interesting" errors (e.g., out of mem,
406 * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be
407 * ignored from one query of two, but if only one query is done, or
408 * both return that error, it should be returned to the caller. Also,
409 * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
410 * should probably be returned as well.
411 */
412
413 /*LINTED*/
414 dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
415 /*LINTED*/
416 message->length, message->data, realm, *use_master, tcp_only);
417
418 if (!tcp_only && context->udp_pref_limit < 0) {
419 int tmp;
420 retval = profile_get_integer(context->profile,
421 "libdefaults", "udp_preference_limit", 0,
422 DEFAULT_UDP_PREF_LIMIT, &tmp);
423 if (retval)
424 return retval;
425 if (tmp < 0)
426 tmp = DEFAULT_UDP_PREF_LIMIT;
427 else if (tmp > HARD_UDP_LIMIT)
428 /* In the unlikely case that a *really* big value is
429 given, let 'em use as big as we think we can
430 support. */
431 tmp = HARD_UDP_LIMIT;
432 context->udp_pref_limit = tmp;
433 }
434
435 retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
436
437 if (tcp_only)
438 socktype1 = SOCK_STREAM, socktype2 = 0;
439 else if (message->length <= context->udp_pref_limit)
440 socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
441 else
442 socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
443
444 retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
445 if (socktype2) {
446 struct addrlist addrs2;
447
448 retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
449 socktype2, 0);
450 #if 0
451 if (retval2 == 0) {
452 (void) merge_addrlists(&addrs, &addrs2);
453 krb5int_free_addrlist(&addrs2);
454 retval = 0;
455 } else if (retval == KRB5_REALM_CANT_RESOLVE) {
456 retval = retval2;
457 }
458 #else
459 retval = retval2;
460 if (retval == 0) {
461 (void) merge_addrlists(&addrs, &addrs2);
462 krb5int_free_addrlist(&addrs2);
463 }
464 #endif
465 }
466
467 if (addrs.naddrs > 0) {
468 krb5_error_code err = 0;
469
470 retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
471 0, 0, &addr_used, check_for_svc_unavailable, &err);
472 switch (retval) {
473 case 0:
474 /*
475 * Set use_master to 1 if we ended up talking to a master when
476 * we didn't explicitly request to
477 */
478 if (*use_master == 0) {
479 struct addrlist addrs3;
480 retval = krb5_locate_kdc(context, realm, &addrs3, 1,
481 addrs.addrs[addr_used].ai->ai_socktype,
482 addrs.addrs[addr_used].ai->ai_family);
483 if (retval == 0) {
484 if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
485 *use_master = 1;
486 krb5int_free_addrlist (&addrs3);
487 }
488 }
489
490 if (hostname_used) {
491 struct sockaddr *sa;
492 char buf[NI_MAXHOST];
493 int err;
494
495 *hostname_used = NULL;
496 sa = addrs.addrs[addr_used].ai->ai_addr;
497 err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0,
498 AI_CANONNAME);
499 if (err)
500 err = getnameinfo (sa, socklen (sa), buf,
501 sizeof (buf), 0, 0,
502 NI_NUMERICHOST);
503 if (!err)
504 *hostname_used = strdup(buf);
505 /* don't sweat strdup fail */
506 }
507 krb5int_free_addrlist (&addrs);
508 return 0;
509 default:
510 break;
511 /* Cases here are for constructing useful error messages. */
512 case KRB5_KDC_UNREACH:
513 if (err == KDC_ERR_SVC_UNAVAILABLE) {
514 retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
515 } else {
516 krb5_set_error_message(context, retval,
517 dgettext(TEXT_DOMAIN,
518 "Cannot contact any KDC for realm '%.*s'"),
519 realm->length, realm->data);
520 }
521 break;
522 }
523 krb5int_free_addrlist (&addrs);
524 }
525 return retval;
526 }
527
528 #ifdef DEBUG
529
530 #ifdef _WIN32
531 #define dperror(MSG) \
532 dprint("%s: an error occurred ... " \
533 "\tline=%d errno=%m socketerrno=%m\n", \
534 (MSG), __LINE__, errno, SOCKET_ERRNO)
535 #else
536 #define dperror(MSG) dprint("%s: %m\n", MSG, errno)
537 #endif
538 #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0)
539
540 #else /* ! DEBUG */
541
542 #define dperror(MSG) ((void)(MSG))
543 #define dfprintf(ARGLIST) ((void)0)
544
545 #endif
546
547 /*
548 * Notes:
549 *
550 * Getting "connection refused" on a connected UDP socket causes
551 * select to indicate write capability on UNIX, but only shows up
552 * as an exception on Windows. (I don't think any UNIX system flags
553 * the error as an exception.) So we check for both, or make it
554 * system-specific.
555 *
556 * Always watch for responses from *any* of the servers. Eventually
557 * fix the UDP code to do the same.
558 *
559 * To do:
560 * - TCP NOPUSH/CORK socket options?
561 * - error codes that don't suck
562 * - getsockopt(SO_ERROR) to check connect status
563 * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
564 * connections already in progress
565 */
566
567 #include "cm.h"
568
getcurtime(struct timeval * tvp)569 static int getcurtime (struct timeval *tvp)
570 {
571 #ifdef _WIN32
572 struct _timeb tb;
573 _ftime(&tb);
574 tvp->tv_sec = tb.time;
575 tvp->tv_usec = tb.millitm * 1000;
576 /* Can _ftime fail? */
577 return 0;
578 #else
579 if (gettimeofday(tvp, 0)) {
580 dperror("gettimeofday");
581 return errno;
582 }
583 return 0;
584 #endif
585 }
586
587 /*
588 * Call select and return results.
589 * Input: interesting file descriptors and absolute timeout
590 * Output: select return value (-1 or num fds ready) and fd_sets
591 * Return: 0 (for i/o available or timeout) or error code.
592 */
593 krb5_error_code
krb5int_cm_call_select(const struct select_state * in,struct select_state * out,int * sret)594 krb5int_cm_call_select (const struct select_state *in,
595 struct select_state *out, int *sret)
596 {
597 struct timeval now, *timo;
598 krb5_error_code e;
599
600 *out = *in;
601 e = getcurtime(&now);
602 if (e)
603 return e;
604 if (out->end_time.tv_sec == 0)
605 timo = 0;
606 else {
607 timo = &out->end_time;
608 out->end_time.tv_sec -= now.tv_sec;
609 out->end_time.tv_usec -= now.tv_usec;
610 if (out->end_time.tv_usec < 0) {
611 out->end_time.tv_usec += 1000000;
612 out->end_time.tv_sec--;
613 }
614 if (out->end_time.tv_sec < 0) {
615 *sret = 0;
616 return 0;
617 }
618 }
619 /*LINTED*/
620 dprint("selecting on max=%d sockets [%F] timeout %t\n",
621 /*LINTED*/
622 out->max,
623 &out->rfds, &out->wfds, &out->xfds, out->max,
624 timo);
625 *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
626 e = SOCKET_ERRNO;
627
628 /* Solaris Kerberos */
629 #ifdef DEBUG
630 /*LINTED*/
631 dprint("select returns %d", *sret);
632 if (*sret < 0)
633 /*LINTED*/
634 dprint(", error = %E\n", e);
635 else if (*sret == 0)
636 /*LINTED*/
637 dprint(" (timeout)\n");
638 else
639 /*LINTED*/
640 dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
641 #endif
642
643 if (*sret < 0)
644 return e;
645 return 0;
646 }
647
648 static int service_tcp_fd (struct conn_state *conn,
649 struct select_state *selstate, int ssflags);
650 static int service_udp_fd (struct conn_state *conn,
651 struct select_state *selstate, int ssflags);
652
653 static void
set_conn_state_msg_length(struct conn_state * state,const krb5_data * message)654 set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
655 {
656 if (!message || message->length == 0)
657 return;
658
659 if (!state->is_udp) {
660
661 state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff;
662 state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff;
663 state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff;
664 state->x.out.msg_len_buf[3] = message->length & 0xff;
665
666 SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
667 SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
668 state->x.out.sg_count = 2;
669
670 } else {
671
672 SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
673 SG_SET(&state->x.out.sgbuf[1], 0, 0);
674 state->x.out.sg_count = 1;
675
676 }
677 }
678
679
680
681 static int
setup_connection(struct conn_state * state,struct addrinfo * ai,const krb5_data * message,char ** udpbufp)682 setup_connection (struct conn_state *state, struct addrinfo *ai,
683 const krb5_data *message, char **udpbufp)
684 {
685 state->state = INITIALIZING;
686 state->err = 0;
687 state->x.out.sgp = state->x.out.sgbuf;
688 state->addr = ai;
689 state->fd = INVALID_SOCKET;
690 SG_SET(&state->x.out.sgbuf[1], 0, 0);
691 if (ai->ai_socktype == SOCK_STREAM) {
692 /*
693 SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
694 SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
695 state->x.out.sg_count = 2;
696 */
697
698 state->is_udp = 0;
699 state->service = service_tcp_fd;
700 set_conn_state_msg_length (state, message);
701 } else {
702 /*
703 SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
704 SG_SET(&state->x.out.sgbuf[1], 0, 0);
705 state->x.out.sg_count = 1;
706 */
707
708 state->is_udp = 1;
709 state->service = service_udp_fd;
710 set_conn_state_msg_length (state, message);
711
712 if (*udpbufp == 0) {
713 *udpbufp = malloc(krb5_max_dgram_size);
714 if (*udpbufp == 0) {
715 dperror("malloc(krb5_max_dgram_size)");
716 (void) closesocket(state->fd);
717 state->fd = INVALID_SOCKET;
718 state->state = FAILED;
719 return 1;
720 }
721 }
722 state->x.in.buf = *udpbufp;
723 state->x.in.bufsize = krb5_max_dgram_size;
724 }
725 return 0;
726 }
727
728 static int
start_connection(struct conn_state * state,struct select_state * selstate,struct sendto_callback_info * callback_info,krb5_data * callback_buffer)729 start_connection (struct conn_state *state,
730 struct select_state *selstate,
731 struct sendto_callback_info* callback_info,
732 krb5_data* callback_buffer)
733 {
734 int fd, e;
735 struct addrinfo *ai = state->addr;
736
737 /*LINTED*/
738 dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
739 /*LINTED*/
740 ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
741 fd = socket(ai->ai_family, ai->ai_socktype, 0);
742 if (fd == INVALID_SOCKET) {
743 state->err = SOCKET_ERRNO;
744 /*LINTED*/
745 dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
746 return -1; /* try other hosts */
747 }
748 /* Make it non-blocking. */
749 if (ai->ai_socktype == SOCK_STREAM) {
750 static const int one = 1;
751 static const struct linger lopt = { 0, 0 };
752
753 if (ioctlsocket(fd, FIONBIO, (const void *) &one))
754 dperror("sendto_kdc: ioctl(FIONBIO)");
755 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
756 dperror("sendto_kdc: setsockopt(SO_LINGER)");
757 }
758
759 /* Start connecting to KDC. */
760 /*LINTED*/
761 dprint(" fd %d; connecting to %A...\n", fd, ai);
762 e = connect(fd, ai->ai_addr, ai->ai_addrlen);
763 if (e != 0) {
764 /*
765 * This is the path that should be followed for non-blocking
766 * connections.
767 */
768 if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
769 state->state = CONNECTING;
770 state->fd = fd;
771 } else {
772 /*LINTED*/
773 dprint("connect failed: %m\n", SOCKET_ERRNO);
774 (void) closesocket(fd);
775 state->err = SOCKET_ERRNO;
776 state->state = FAILED;
777 return -2;
778 }
779 } else {
780 /*
781 * Connect returned zero even though we tried to make it
782 * non-blocking, which should have caused it to return before
783 * finishing the connection. Oh well. Someone's network
784 * stack is broken, but if they gave us a connection, use it.
785 */
786 state->state = WRITING;
787 state->fd = fd;
788 }
789 /*LINTED*/
790 dprint("new state = %s\n", state_strings[state->state]);
791
792
793 /*
794 * Here's where KPASSWD callback gets the socket information it needs for
795 * a kpasswd request
796 */
797 if (callback_info) {
798
799 e = callback_info->pfn_callback(state,
800 callback_info->context,
801 callback_buffer);
802 if (e != 0) {
803 dprint("callback failed: %m\n", e);
804 (void) closesocket(fd);
805 state->err = e;
806 state->fd = INVALID_SOCKET;
807 state->state = FAILED;
808 return -3;
809 }
810
811 dprint("callback %p (message=%d@%p)\n",
812 state,
813 callback_buffer->length,
814 callback_buffer->data);
815
816 set_conn_state_msg_length( state, callback_buffer );
817 }
818
819 if (ai->ai_socktype == SOCK_DGRAM) {
820 /* Send it now. */
821 int ret;
822 sg_buf *sg = &state->x.out.sgbuf[0];
823
824 /*LINTED*/
825 dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
826 ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
827 if (ret != SG_LEN(sg)) {
828 dperror("sendto");
829 (void) closesocket(state->fd);
830 state->fd = INVALID_SOCKET;
831 state->state = FAILED;
832 return -4;
833 } else {
834 state->state = READING;
835 }
836 }
837 #ifdef DEBUG
838 if (debug) {
839 struct sockaddr_storage ss;
840 socklen_t sslen = sizeof(ss);
841 if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
842 struct addrinfo hack_ai;
843 memset(&hack_ai, 0, sizeof(hack_ai));
844 hack_ai.ai_addr = (struct sockaddr *) &ss;
845 hack_ai.ai_addrlen = sslen;
846 hack_ai.ai_socktype = SOCK_DGRAM;
847 hack_ai.ai_family = ai->ai_family;
848 dprint("local socket address is %A\n", &hack_ai);
849 }
850 }
851 #endif
852 FD_SET(state->fd, &selstate->rfds);
853 if (state->state == CONNECTING || state->state == WRITING)
854 FD_SET(state->fd, &selstate->wfds);
855 FD_SET(state->fd, &selstate->xfds);
856 if (selstate->max <= state->fd)
857 selstate->max = state->fd + 1;
858 selstate->nfds++;
859
860 /*LINTED*/
861 dprint("new select vectors: %F\n",
862 /*LINTED*/
863 &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
864
865 return 0;
866 }
867
868 /* Return 0 if we sent something, non-0 otherwise.
869 If 0 is returned, the caller should delay waiting for a response.
870 Otherwise, the caller should immediately move on to process the
871 next connection. */
872 static int
maybe_send(struct conn_state * conn,struct select_state * selstate,struct sendto_callback_info * callback_info,krb5_data * callback_buffer)873 maybe_send (struct conn_state *conn,
874 struct select_state *selstate,
875 struct sendto_callback_info* callback_info,
876 krb5_data* callback_buffer)
877 {
878 sg_buf *sg;
879
880 /*LINTED*/
881 dprint("maybe_send(@%p) state=%s type=%s\n", conn,
882 /*LINTED*/
883 state_strings[conn->state],
884 conn->is_udp ? "udp" : "tcp");
885 if (conn->state == INITIALIZING)
886 return start_connection(conn, selstate, callback_info, callback_buffer);
887
888 /* Did we already shut down this channel? */
889 if (conn->state == FAILED) {
890 dprint("connection already closed\n");
891 return -1;
892 }
893
894 if (conn->addr->ai_socktype == SOCK_STREAM) {
895 dprint("skipping stream socket\n");
896 /* The select callback will handle flushing any data we
897 haven't written yet, and we only write it once. */
898 return -1;
899 }
900
901 /* UDP - Send message, possibly for the first time, possibly a
902 retransmit if a previous attempt timed out. */
903 sg = &conn->x.out.sgbuf[0];
904 /*LINTED*/
905 dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
906 if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) {
907 dperror("send");
908 /* Keep connection alive, we'll try again next pass.
909
910 Is this likely to catch any errors we didn't get from the
911 select callbacks? */
912 return -1;
913 }
914 /* Yay, it worked. */
915 return 0;
916 }
917
918 static void
kill_conn(struct conn_state * conn,struct select_state * selstate,int err)919 kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
920 {
921 conn->state = FAILED;
922 shutdown(conn->fd, SHUTDOWN_BOTH);
923 FD_CLR(conn->fd, &selstate->rfds);
924 FD_CLR(conn->fd, &selstate->wfds);
925 FD_CLR(conn->fd, &selstate->xfds);
926 conn->err = err;
927 /*LINTED*/
928 dprint("abandoning connection %d: %m\n", conn->fd, err);
929 /* Fix up max fd for next select call. */
930 if (selstate->max == 1 + conn->fd) {
931 while (selstate->max > 0
932 && ! FD_ISSET(selstate->max-1, &selstate->rfds)
933 && ! FD_ISSET(selstate->max-1, &selstate->wfds)
934 && ! FD_ISSET(selstate->max-1, &selstate->xfds))
935 selstate->max--;
936 /*LINTED*/
937 dprint("new max_fd + 1 is %d\n", selstate->max);
938 }
939 selstate->nfds--;
940 }
941
942 /* Check socket for error. */
943 static int
get_so_error(int fd)944 get_so_error(int fd)
945 {
946 int e, sockerr;
947 socklen_t sockerrlen;
948
949 sockerr = 0;
950 sockerrlen = sizeof(sockerr);
951 e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
952 if (e != 0) {
953 /* What to do now? */
954 e = SOCKET_ERRNO;
955 dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e);
956 return e;
957 }
958 return sockerr;
959 }
960
961 /* Return nonzero only if we're finished and the caller should exit
962 its loop. This happens in two cases: We have a complete message,
963 or the socket has closed and no others are open. */
964
965 static int
service_tcp_fd(struct conn_state * conn,struct select_state * selstate,int ssflags)966 service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
967 int ssflags)
968 {
969 krb5_error_code e = 0;
970 int nwritten, nread;
971
972 if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
973 abort();
974 switch (conn->state) {
975 SOCKET_WRITEV_TEMP tmp;
976
977 case CONNECTING:
978 if (ssflags & SSF_READ) {
979 /* Bad -- the KDC shouldn't be sending to us first. */
980 e = EINVAL /* ?? */;
981 kill_conn:
982 kill_conn(conn, selstate, e);
983 if (e == EINVAL) {
984 closesocket(conn->fd);
985 conn->fd = INVALID_SOCKET;
986 }
987 return e == 0;
988 }
989 if (ssflags & SSF_EXCEPTION) {
990 handle_exception:
991 e = get_so_error(conn->fd);
992 if (e)
993 dprint("socket error on exception fd: %m", e);
994 else
995 dprint("no socket error info available on exception fd");
996 goto kill_conn;
997 }
998
999 /*
1000 * Connect finished -- but did it succeed or fail?
1001 * UNIX sets can_write if failed.
1002 * Call getsockopt to see if error pending.
1003 *
1004 * (For most UNIX systems it works to just try writing the
1005 * first time and detect an error. But Bill Dodd at IBM
1006 * reports that some version of AIX, SIGPIPE can result.)
1007 */
1008 e = get_so_error(conn->fd);
1009 if (e) {
1010 dprint("socket error on write fd: %m", e);
1011 goto kill_conn;
1012 }
1013 conn->state = WRITING;
1014 goto try_writing;
1015
1016 case WRITING:
1017 if (ssflags & SSF_READ) {
1018 e = E2BIG;
1019 /* Bad -- the KDC shouldn't be sending anything yet. */
1020 goto kill_conn;
1021 }
1022 if (ssflags & SSF_EXCEPTION)
1023 goto handle_exception;
1024
1025 try_writing:
1026 /*LINTED*/
1027 dprint("trying to writev %d (%d bytes) to fd %d\n",
1028 /*LINTED*/
1029 conn->x.out.sg_count,
1030 ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
1031 /*LINTED*/
1032 + SG_LEN(&conn->x.out.sgp[0])),
1033 conn->fd);
1034 nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
1035 conn->x.out.sg_count, tmp);
1036 if (nwritten < 0) {
1037 e = SOCKET_ERRNO;
1038 /*LINTED*/
1039 dprint("failed: %m\n", e);
1040 goto kill_conn;
1041 }
1042 /*LINTED*/
1043 dprint("wrote %d bytes\n", nwritten);
1044 while (nwritten) {
1045 sg_buf *sgp = conn->x.out.sgp;
1046 if (nwritten < SG_LEN(sgp)) {
1047 /*LINTED*/
1048 SG_ADVANCE(sgp, nwritten);
1049 nwritten = 0;
1050 } else {
1051 nwritten -= SG_LEN(conn->x.out.sgp);
1052 conn->x.out.sgp++;
1053 conn->x.out.sg_count--;
1054 if (conn->x.out.sg_count == 0 && nwritten != 0)
1055 /* Wrote more than we wanted to? */
1056 abort();
1057 }
1058 }
1059 if (conn->x.out.sg_count == 0) {
1060 /* Done writing, switch to reading. */
1061 /* Don't call shutdown at this point because
1062 * some implementations cannot deal with half-closed connections.*/
1063 FD_CLR(conn->fd, &selstate->wfds);
1064 /* Q: How do we detect failures to send the remaining data
1065 to the remote side, since we're in non-blocking mode?
1066 Will we always get errors on the reading side? */
1067 /*LINTED*/
1068 dprint("switching fd %d to READING\n", conn->fd);
1069 conn->state = READING;
1070 conn->x.in.bufsizebytes_read = 0;
1071 conn->x.in.bufsize = 0;
1072 conn->x.in.buf = 0;
1073 conn->x.in.pos = 0;
1074 conn->x.in.n_left = 0;
1075 }
1076 return 0;
1077
1078 case READING:
1079 if (ssflags & SSF_EXCEPTION) {
1080 if (conn->x.in.buf) {
1081 free(conn->x.in.buf);
1082 conn->x.in.buf = 0;
1083 }
1084 goto handle_exception;
1085 }
1086
1087 if (conn->x.in.bufsizebytes_read == 4) {
1088 /* Reading data. */
1089 /*LINTED*/
1090 dprint("reading %d bytes of data from fd %d\n",
1091 (int) conn->x.in.n_left, conn->fd);
1092 nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
1093 if (nread <= 0) {
1094 e = nread ? SOCKET_ERRNO : ECONNRESET;
1095 free(conn->x.in.buf);
1096 conn->x.in.buf = 0;
1097 goto kill_conn;
1098 }
1099 conn->x.in.n_left -= nread;
1100 conn->x.in.pos += nread;
1101 /* Solaris Kerberos */
1102 if ((long)conn->x.in.n_left <= 0) {
1103 /* We win! */
1104 return 1;
1105 }
1106 } else {
1107 /* Reading length. */
1108 nread = SOCKET_READ(conn->fd,
1109 conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
1110 4 - conn->x.in.bufsizebytes_read);
1111 if (nread < 0) {
1112 e = SOCKET_ERRNO;
1113 goto kill_conn;
1114 }
1115 conn->x.in.bufsizebytes_read += nread;
1116 if (conn->x.in.bufsizebytes_read == 4) {
1117 unsigned long len;
1118 len = conn->x.in.bufsizebytes[0];
1119 len = (len << 8) + conn->x.in.bufsizebytes[1];
1120 len = (len << 8) + conn->x.in.bufsizebytes[2];
1121 len = (len << 8) + conn->x.in.bufsizebytes[3];
1122 /*LINTED*/
1123 dprint("received length on fd %d is %d\n", conn->fd, (int)len);
1124 /* Arbitrary 1M cap. */
1125 if (len > 1 * 1024 * 1024) {
1126 e = E2BIG;
1127 goto kill_conn;
1128 }
1129 conn->x.in.bufsize = conn->x.in.n_left = len;
1130 conn->x.in.buf = conn->x.in.pos = malloc(len);
1131 /*LINTED*/
1132 dprint("allocated %d byte buffer at %p\n", (int) len,
1133 conn->x.in.buf);
1134 if (conn->x.in.buf == 0) {
1135 /* allocation failure */
1136 e = errno;
1137 goto kill_conn;
1138 }
1139 }
1140 }
1141 break;
1142
1143 default:
1144 abort();
1145 }
1146 return 0;
1147 }
1148
1149 static int
service_udp_fd(struct conn_state * conn,struct select_state * selstate,int ssflags)1150 service_udp_fd(struct conn_state *conn, struct select_state *selstate,
1151 int ssflags)
1152 {
1153 int nread;
1154
1155 if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
1156 abort();
1157 if (conn->state != READING)
1158 abort();
1159
1160 nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
1161 if (nread < 0) {
1162 kill_conn(conn, selstate, SOCKET_ERRNO);
1163 return 0;
1164 }
1165 conn->x.in.pos = conn->x.in.buf + nread;
1166 return 1;
1167 }
1168
1169 static int
service_fds(krb5_context context,struct select_state * selstate,struct conn_state * conns,size_t n_conns,int * winning_conn,struct select_state * seltemp,int (* msg_handler)(krb5_context,const krb5_data *,void *),void * msg_handler_data)1170 service_fds (krb5_context context,
1171 struct select_state *selstate,
1172 struct conn_state *conns, size_t n_conns, int *winning_conn,
1173 struct select_state *seltemp,
1174 int (*msg_handler)(krb5_context, const krb5_data *, void *),
1175 void *msg_handler_data)
1176 {
1177 int e, selret;
1178
1179 e = 0;
1180 while (selstate->nfds > 0
1181 && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) {
1182 int i;
1183
1184 /*LINTED*/
1185 dprint("service_fds examining results, selret=%d\n", selret);
1186
1187 if (selret == 0)
1188 /* Timeout, return to caller. */
1189 return 0;
1190
1191 /* Got something on a socket, process it. */
1192 for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) {
1193 int ssflags;
1194
1195 if (conns[i].fd == INVALID_SOCKET)
1196 continue;
1197 ssflags = 0;
1198 if (FD_ISSET(conns[i].fd, &seltemp->rfds))
1199 ssflags |= SSF_READ, selret--;
1200 if (FD_ISSET(conns[i].fd, &seltemp->wfds))
1201 ssflags |= SSF_WRITE, selret--;
1202 if (FD_ISSET(conns[i].fd, &seltemp->xfds))
1203 ssflags |= SSF_EXCEPTION, selret--;
1204 if (!ssflags)
1205 continue;
1206
1207 /*LINTED*/
1208 dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
1209 /*LINTED*/
1210 (ssflags & SSF_READ) ? "r" : "",
1211 /*LINTED*/
1212 (ssflags & SSF_WRITE) ? "w" : "",
1213 /*LINTED*/
1214 (ssflags & SSF_EXCEPTION) ? "x" : "",
1215 /*LINTED*/
1216 conns[i].fd, conns[i].addr,
1217 state_strings[(int) conns[i].state]);
1218
1219 if (conns[i].service (&conns[i], selstate, ssflags)) {
1220 int stop = 1;
1221
1222 if (msg_handler != NULL) {
1223 krb5_data reply;
1224
1225 reply.data = conns[i].x.in.buf;
1226 reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
1227
1228 stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1229 }
1230
1231 if (stop) {
1232 dprint("fd service routine says we're done\n");
1233 *winning_conn = i;
1234 return 1;
1235 }
1236 }
1237 }
1238 }
1239 if (e != 0) {
1240 /*LINTED*/
1241 dprint("select returned %m\n", e);
1242 *winning_conn = -1;
1243 return 1;
1244 }
1245 return 0;
1246 }
1247
1248 /*
1249 * Current worst-case timeout behavior:
1250 *
1251 * First pass, 1s per udp or tcp server, plus 2s at end.
1252 * Second pass, 1s per udp server, plus 4s.
1253 * Third pass, 1s per udp server, plus 8s.
1254 * Fourth => 16s, etc.
1255 *
1256 * Restated:
1257 * Per UDP server, 1s per pass.
1258 * Per TCP server, 1s.
1259 * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
1260 *
1261 * Total = 2**(P+1) + U*P + T - 2.
1262 *
1263 * If P=3, Total = 3*U + T + 14.
1264 * If P=4, Total = 4*U + T + 30.
1265 *
1266 * Note that if you try to reach two ports (e.g., both 88 and 750) on
1267 * one server, it counts as two.
1268 */
1269
1270 krb5_error_code
1271 /*ARGSUSED*/
krb5int_sendto(krb5_context context,const krb5_data * message,const struct addrlist * addrs,struct sendto_callback_info * callback_info,krb5_data * reply,struct sockaddr * localaddr,socklen_t * localaddrlen,struct sockaddr * remoteaddr,socklen_t * remoteaddrlen,int * addr_used,int (* msg_handler)(krb5_context,const krb5_data *,void *),void * msg_handler_data)1272 krb5int_sendto (krb5_context context, const krb5_data *message,
1273 const struct addrlist *addrs,
1274 struct sendto_callback_info* callback_info, krb5_data *reply,
1275 struct sockaddr *localaddr, socklen_t *localaddrlen,
1276 struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1277 int *addr_used,
1278 /* return 0 -> keep going, 1 -> quit */
1279 int (*msg_handler)(krb5_context, const krb5_data *, void *),
1280 void *msg_handler_data)
1281 {
1282 int i, pass;
1283 int delay_this_pass = 2;
1284 krb5_error_code retval;
1285 struct conn_state *conns;
1286 krb5_data *callback_data = 0;
1287 size_t n_conns, host;
1288 struct select_state *sel_state;
1289 struct timeval now;
1290 int winning_conn = -1, e = 0;
1291 char *udpbuf = 0;
1292
1293 if (message)
1294 dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
1295 else
1296 dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
1297 print_addrlist(addrs);
1298 dprint(")\n");
1299
1300 reply->data = 0;
1301 reply->length = 0;
1302
1303 n_conns = addrs->naddrs;
1304 conns = malloc(n_conns * sizeof(struct conn_state));
1305 if (conns == NULL) {
1306 return ENOMEM;
1307 }
1308
1309 memset(conns, 0, n_conns * sizeof(struct conn_state));
1310
1311 if (callback_info) {
1312 callback_data = malloc(n_conns * sizeof(krb5_data));
1313 if (callback_data == NULL) {
1314 return ENOMEM;
1315 }
1316
1317 memset(callback_data, 0, n_conns * sizeof(krb5_data));
1318 }
1319
1320 for (i = 0; i < n_conns; i++) {
1321 conns[i].fd = INVALID_SOCKET;
1322 }
1323
1324 /* One for use here, listing all our fds in use, and one for
1325 temporary use in service_fds, for the fds of interest. */
1326 sel_state = malloc(2 * sizeof(*sel_state));
1327 if (sel_state == NULL) {
1328 free(conns);
1329 return ENOMEM;
1330 }
1331 sel_state->max = 0;
1332 sel_state->nfds = 0;
1333 sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
1334 FD_ZERO(&sel_state->rfds);
1335 FD_ZERO(&sel_state->wfds);
1336 FD_ZERO(&sel_state->xfds);
1337
1338
1339 /* Set up connections. */
1340 for (host = 0; host < n_conns; host++) {
1341 retval = setup_connection(&conns[host],
1342 addrs->addrs[host].ai,
1343 message,
1344 &udpbuf);
1345 if (retval)
1346 continue;
1347 }
1348 for (pass = 0; pass < MAX_PASS; pass++) {
1349 /* Possible optimization: Make only one pass if TCP only.
1350 Stop making passes if all UDP ports are closed down. */
1351 /*LINTED*/
1352 dprint("pass %d delay=%d\n", pass, delay_this_pass);
1353 for (host = 0; host < n_conns; host++) {
1354 /*LINTED*/
1355 dprint("host %d\n", host);
1356
1357 /* Send to the host, wait for a response, then move on. */
1358 if (maybe_send(&conns[host],
1359 sel_state,
1360 callback_info,
1361 (callback_info ? &callback_data[host] : NULL)))
1362 continue;
1363
1364 retval = getcurtime(&now);
1365 if (retval)
1366 goto egress;
1367 sel_state->end_time = now;
1368 sel_state->end_time.tv_sec += 1;
1369 e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1370 sel_state+1, msg_handler, msg_handler_data);
1371 if (e)
1372 break;
1373 if (pass > 0 && sel_state->nfds == 0)
1374 /*
1375 * After the first pass, if we close all fds, break
1376 * out right away. During the first pass, it's okay,
1377 * we're probably about to open another connection.
1378 */
1379 break;
1380 }
1381 if (e)
1382 break;
1383 retval = getcurtime(&now);
1384 if (retval)
1385 goto egress;
1386 /* Possible optimization: Find a way to integrate this select
1387 call with the last one from the above loop, if the loop
1388 actually calls select. */
1389 sel_state->end_time.tv_sec += delay_this_pass;
1390 e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1391 sel_state+1, msg_handler, msg_handler_data);
1392 if (e)
1393 break;
1394 if (sel_state->nfds == 0)
1395 break;
1396 delay_this_pass *= 2;
1397 }
1398
1399 if (sel_state->nfds == 0) {
1400 /* No addresses? */
1401 retval = KRB5_KDC_UNREACH;
1402 goto egress;
1403 }
1404 if (e == 0 || winning_conn < 0) {
1405 retval = KRB5_KDC_UNREACH;
1406 goto egress;
1407 }
1408 /* Success! */
1409 reply->data = conns[winning_conn].x.in.buf;
1410 reply->length = (conns[winning_conn].x.in.pos
1411 - conns[winning_conn].x.in.buf);
1412 /*LINTED*/
1413 dprint("returning %d bytes in buffer %p\n",
1414 (int) reply->length, reply->data);
1415 retval = 0;
1416 conns[winning_conn].x.in.buf = 0;
1417 if (addr_used)
1418 *addr_used = winning_conn;
1419 if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
1420 (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
1421
1422 if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
1423 (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
1424
1425 egress:
1426 for (i = 0; i < n_conns; i++) {
1427 if (conns[i].fd != INVALID_SOCKET)
1428 closesocket(conns[i].fd);
1429 if (conns[i].state == READING
1430 && conns[i].x.in.buf != 0
1431 && conns[i].x.in.buf != udpbuf)
1432 free(conns[i].x.in.buf);
1433 if (callback_info) {
1434 callback_info->pfn_cleanup( callback_info->context, &callback_data[i]);
1435 }
1436 }
1437
1438 if (callback_data)
1439 free(callback_data);
1440
1441 free(conns);
1442 if (reply->data != udpbuf)
1443 free(udpbuf);
1444 free(sel_state);
1445 return retval;
1446 }
1447