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 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 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 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 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 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 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 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 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 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 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 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 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 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 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*/ 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