17c478bd9Sstevel@tonic-gate /* 2*56a424ccSmp * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 67c478bd9Sstevel@tonic-gate /* 77c478bd9Sstevel@tonic-gate * lib/krb5/os/sendto_kdc.c 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * Copyright 1990,1991,2001,2002 by the Massachusetts Institute of Technology. 107c478bd9Sstevel@tonic-gate * All Rights Reserved. 117c478bd9Sstevel@tonic-gate * 127c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 137c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 147c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 157c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 167c478bd9Sstevel@tonic-gate * 177c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 187c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 197c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 207c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 217c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 227c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 237c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 247c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 257c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 267c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 277c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 287c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 297c478bd9Sstevel@tonic-gate * or implied warranty. 307c478bd9Sstevel@tonic-gate * 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * Send packet to KDC for realm; wait for response, retransmitting 337c478bd9Sstevel@tonic-gate * as necessary. 347c478bd9Sstevel@tonic-gate */ 357c478bd9Sstevel@tonic-gate 367c478bd9Sstevel@tonic-gate #define NEED_SOCKETS 377c478bd9Sstevel@tonic-gate #define NEED_LOWLEVEL_IO 387c478bd9Sstevel@tonic-gate #include <fake-addrinfo.h> 397c478bd9Sstevel@tonic-gate #include <k5-int.h> 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H 427c478bd9Sstevel@tonic-gate #include <sys/time.h> 437c478bd9Sstevel@tonic-gate #else 447c478bd9Sstevel@tonic-gate #include <time.h> 457c478bd9Sstevel@tonic-gate #endif 467c478bd9Sstevel@tonic-gate #include "os-proto.h" 477c478bd9Sstevel@tonic-gate 487c478bd9Sstevel@tonic-gate #ifdef _AIX 497c478bd9Sstevel@tonic-gate #include <sys/select.h> 507c478bd9Sstevel@tonic-gate #endif 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate /* For FIONBIO. */ 537c478bd9Sstevel@tonic-gate #include <sys/ioctl.h> 547c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H 557c478bd9Sstevel@tonic-gate #include <sys/filio.h> 567c478bd9Sstevel@tonic-gate #endif 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate #define MAX_PASS 3 597c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */ 607c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT 1465 */ 617c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */ 627c478bd9Sstevel@tonic-gate 63505d05c7Sgtb krb5_error_code krb5int_sendto(krb5_context, const krb5_data *, 64505d05c7Sgtb const struct addrlist *, krb5_data *, 65505d05c7Sgtb struct sockaddr_storage *, 66505d05c7Sgtb socklen_t *, int *); 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* Solaris kerberos: leaving this here because other code depends on this. */ 697c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len) 707c478bd9Sstevel@tonic-gate { 717c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr); 727c478bd9Sstevel@tonic-gate /* stderr is unbuffered */ 737c478bd9Sstevel@tonic-gate } 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler; 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate /* 787c478bd9Sstevel@tonic-gate * Solaris Kerberos: only including the debug stuff if DEBUG defined outside 797c478bd9Sstevel@tonic-gate * this file. 807c478bd9Sstevel@tonic-gate */ 817c478bd9Sstevel@tonic-gate #ifdef DEBUG 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024]; 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */ 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate static void putstr(const char *str) 887c478bd9Sstevel@tonic-gate { 897c478bd9Sstevel@tonic-gate /* Solaris kerberos: build the string which will be passed to syslog later */ 907c478bd9Sstevel@tonic-gate strlcat(global_err_str, str, sizeof (global_err_str)); 917c478bd9Sstevel@tonic-gate } 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint 947c478bd9Sstevel@tonic-gate #define dperror dprint 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate #include <com_err.h> 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate static void 997c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...) 1007c478bd9Sstevel@tonic-gate { 1017c478bd9Sstevel@tonic-gate va_list args; 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate /* Temporaries for variable arguments, etc. */ 1047c478bd9Sstevel@tonic-gate krb5_error_code kerr; 1057c478bd9Sstevel@tonic-gate int err; 1067c478bd9Sstevel@tonic-gate fd_set *rfds, *wfds, *xfds; 1077c478bd9Sstevel@tonic-gate int i; 1087c478bd9Sstevel@tonic-gate int maxfd; 1097c478bd9Sstevel@tonic-gate struct timeval *tv; 1107c478bd9Sstevel@tonic-gate struct addrinfo *ai; 1117c478bd9Sstevel@tonic-gate const krb5_data *d; 1127c478bd9Sstevel@tonic-gate char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 1137c478bd9Sstevel@tonic-gate const char *p; 1147c478bd9Sstevel@tonic-gate char tmpbuf[NI_MAXHOST + NI_MAXSERV + 30]; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate /* 1177c478bd9Sstevel@tonic-gate * Solaris kerberos: modified this function to create a string to pass to 1187c478bd9Sstevel@tonic-gate * syslog() 1197c478bd9Sstevel@tonic-gate */ 1207c478bd9Sstevel@tonic-gate global_err_str[0] = NULL; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate va_start(args, fmt); 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate #define putf(FMT,X) (sprintf(tmpbuf,FMT,X),putstr(tmpbuf)) 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate for (; *fmt; fmt++) { 1277c478bd9Sstevel@tonic-gate if (*fmt != '%') { 1287c478bd9Sstevel@tonic-gate /* Possible optimization: Look for % and print all chars 1297c478bd9Sstevel@tonic-gate up to it in one call. */ 1307c478bd9Sstevel@tonic-gate putf("%c", *fmt); 1317c478bd9Sstevel@tonic-gate continue; 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate /* After this, always processing a '%' sequence. */ 1347c478bd9Sstevel@tonic-gate fmt++; 1357c478bd9Sstevel@tonic-gate switch (*fmt) { 1367c478bd9Sstevel@tonic-gate case 0: 1377c478bd9Sstevel@tonic-gate default: 1387c478bd9Sstevel@tonic-gate abort(); 1397c478bd9Sstevel@tonic-gate case 'E': 1407c478bd9Sstevel@tonic-gate /* %E => krb5_error_code */ 1417c478bd9Sstevel@tonic-gate kerr = va_arg(args, krb5_error_code); 1427c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%lu/", (unsigned long) kerr); 1437c478bd9Sstevel@tonic-gate putstr(tmpbuf); 1447c478bd9Sstevel@tonic-gate p = error_message(kerr); 1457c478bd9Sstevel@tonic-gate putstr(p); 1467c478bd9Sstevel@tonic-gate break; 1477c478bd9Sstevel@tonic-gate case 'm': 1487c478bd9Sstevel@tonic-gate /* %m => errno value (int) */ 1497c478bd9Sstevel@tonic-gate /* Like syslog's %m except the errno value is passed in 1507c478bd9Sstevel@tonic-gate rather than the current value. */ 1517c478bd9Sstevel@tonic-gate err = va_arg(args, int); 1527c478bd9Sstevel@tonic-gate putf("%d/", err); 1537c478bd9Sstevel@tonic-gate p = strerror(err); 1547c478bd9Sstevel@tonic-gate putstr(p); 1557c478bd9Sstevel@tonic-gate break; 1567c478bd9Sstevel@tonic-gate case 'F': 1577c478bd9Sstevel@tonic-gate /* %F => fd_set *, fd_set *, fd_set *, int */ 1587c478bd9Sstevel@tonic-gate rfds = va_arg(args, fd_set *); 1597c478bd9Sstevel@tonic-gate wfds = va_arg(args, fd_set *); 1607c478bd9Sstevel@tonic-gate xfds = va_arg(args, fd_set *); 1617c478bd9Sstevel@tonic-gate maxfd = va_arg(args, int); 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate for (i = 0; i < maxfd; i++) { 1647c478bd9Sstevel@tonic-gate int r = FD_ISSET(i, rfds); 1657c478bd9Sstevel@tonic-gate int w = wfds && FD_ISSET(i, wfds); 1667c478bd9Sstevel@tonic-gate int x = xfds && FD_ISSET(i, xfds); 1677c478bd9Sstevel@tonic-gate if (r || w || x) { 1687c478bd9Sstevel@tonic-gate putf(" %d", i); 1697c478bd9Sstevel@tonic-gate if (r) 1707c478bd9Sstevel@tonic-gate putstr("r"); 1717c478bd9Sstevel@tonic-gate if (w) 1727c478bd9Sstevel@tonic-gate putstr("w"); 1737c478bd9Sstevel@tonic-gate if (x) 1747c478bd9Sstevel@tonic-gate putstr("x"); 1757c478bd9Sstevel@tonic-gate } 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate putstr(" "); 1787c478bd9Sstevel@tonic-gate break; 1797c478bd9Sstevel@tonic-gate case 's': 1807c478bd9Sstevel@tonic-gate /* %s => char * */ 1817c478bd9Sstevel@tonic-gate p = va_arg(args, const char *); 1827c478bd9Sstevel@tonic-gate putstr(p); 1837c478bd9Sstevel@tonic-gate break; 1847c478bd9Sstevel@tonic-gate case 't': 1857c478bd9Sstevel@tonic-gate /* %t => struct timeval * */ 1867c478bd9Sstevel@tonic-gate tv = va_arg(args, struct timeval *); 1877c478bd9Sstevel@tonic-gate if (tv) { 1887c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%ld.%06ld", 1897c478bd9Sstevel@tonic-gate (long) tv->tv_sec, (long) tv->tv_usec); 1907c478bd9Sstevel@tonic-gate putstr(tmpbuf); 1917c478bd9Sstevel@tonic-gate } else 1927c478bd9Sstevel@tonic-gate putstr("never"); 1937c478bd9Sstevel@tonic-gate break; 1947c478bd9Sstevel@tonic-gate case 'd': 1957c478bd9Sstevel@tonic-gate /* %d => int */ 1967c478bd9Sstevel@tonic-gate putf("%d", va_arg(args, int)); 1977c478bd9Sstevel@tonic-gate break; 1987c478bd9Sstevel@tonic-gate case 'p': 1997c478bd9Sstevel@tonic-gate /* %p => pointer */ 2007c478bd9Sstevel@tonic-gate putf("%p", va_arg(args, void*)); 2017c478bd9Sstevel@tonic-gate break; 2027c478bd9Sstevel@tonic-gate case 'A': 2037c478bd9Sstevel@tonic-gate /* %A => addrinfo */ 2047c478bd9Sstevel@tonic-gate ai = va_arg(args, struct addrinfo *); 2057c478bd9Sstevel@tonic-gate if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen, 2067c478bd9Sstevel@tonic-gate addrbuf, sizeof (addrbuf), 2077c478bd9Sstevel@tonic-gate portbuf, sizeof (portbuf), 2087c478bd9Sstevel@tonic-gate NI_NUMERICHOST | NI_NUMERICSERV)) 2097c478bd9Sstevel@tonic-gate strcpy (addrbuf, "??"), strcpy (portbuf, "??"); 2107c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%s %s.%s", 2117c478bd9Sstevel@tonic-gate (ai->ai_socktype == SOCK_DGRAM 2127c478bd9Sstevel@tonic-gate ? "udp" 2137c478bd9Sstevel@tonic-gate : ai->ai_socktype == SOCK_STREAM 2147c478bd9Sstevel@tonic-gate ? "tcp" 2157c478bd9Sstevel@tonic-gate : "???"), 2167c478bd9Sstevel@tonic-gate addrbuf, portbuf); 2177c478bd9Sstevel@tonic-gate putstr(tmpbuf); 2187c478bd9Sstevel@tonic-gate break; 2197c478bd9Sstevel@tonic-gate case 'D': 2207c478bd9Sstevel@tonic-gate /* %D => krb5_data * */ 2217c478bd9Sstevel@tonic-gate d = va_arg(args, krb5_data *); 2227c478bd9Sstevel@tonic-gate p = d->data; 2237c478bd9Sstevel@tonic-gate putstr("0x"); 2247c478bd9Sstevel@tonic-gate for (i = 0; i < d->length; i++) { 2257c478bd9Sstevel@tonic-gate putf("%.2x", *p++); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate break; 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate va_end(args); 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* Solaris kerberos: use syslog() for debug output */ 2337c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, global_err_str); 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate #else 2377c478bd9Sstevel@tonic-gate #define dprint (void) 2387c478bd9Sstevel@tonic-gate #define dperror(MSG) ((void)(MSG)) 2397c478bd9Sstevel@tonic-gate #endif 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate static int 2427c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src) 2437c478bd9Sstevel@tonic-gate { 2447c478bd9Sstevel@tonic-gate int err, i; 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate #ifdef DEBUG 2477c478bd9Sstevel@tonic-gate /*LINTED*/ 2487c478bd9Sstevel@tonic-gate dprint("merging addrlists:\n\tlist1: "); 2497c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 2507c478bd9Sstevel@tonic-gate /*LINTED*/ 2517c478bd9Sstevel@tonic-gate dprint(" %A", dest->addrs[i]); 2527c478bd9Sstevel@tonic-gate /*LINTED*/ 2537c478bd9Sstevel@tonic-gate dprint("\n\tlist2: "); 2547c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) 2557c478bd9Sstevel@tonic-gate /*LINTED*/ 2567c478bd9Sstevel@tonic-gate dprint(" %A", src->addrs[i]); 2577c478bd9Sstevel@tonic-gate /*LINTED*/ 2587c478bd9Sstevel@tonic-gate dprint("\n"); 2597c478bd9Sstevel@tonic-gate #endif 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate err = krb5int_grow_addrlist (dest, src->naddrs); 2627c478bd9Sstevel@tonic-gate if (err) 2637c478bd9Sstevel@tonic-gate return err; 2647c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) { 2657c478bd9Sstevel@tonic-gate dest->addrs[dest->naddrs + i] = src->addrs[i]; 2667c478bd9Sstevel@tonic-gate src->addrs[i] = 0; 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate dest->naddrs += i; 2697c478bd9Sstevel@tonic-gate src->naddrs = 0; 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate #ifdef DEBUG 2727c478bd9Sstevel@tonic-gate /*LINTED*/ 2737c478bd9Sstevel@tonic-gate dprint("\tout: "); 2747c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 2757c478bd9Sstevel@tonic-gate /*LINTED*/ 2767c478bd9Sstevel@tonic-gate dprint(" %A", dest->addrs[i]); 2777c478bd9Sstevel@tonic-gate /*LINTED*/ 2787c478bd9Sstevel@tonic-gate dprint("\n"); 2797c478bd9Sstevel@tonic-gate #endif 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate return 0; 2827c478bd9Sstevel@tonic-gate } 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate /* 2857c478bd9Sstevel@tonic-gate * send the formatted request 'message' to a KDC for realm 'realm' and 2867c478bd9Sstevel@tonic-gate * return the response (if any) in 'reply'. 2877c478bd9Sstevel@tonic-gate * 2887c478bd9Sstevel@tonic-gate * If the message is sent and a response is received, 0 is returned, 2897c478bd9Sstevel@tonic-gate * otherwise an error code is returned. 2907c478bd9Sstevel@tonic-gate * 2917c478bd9Sstevel@tonic-gate * The storage for 'reply' is allocated and should be freed by the caller 2927c478bd9Sstevel@tonic-gate * when finished. 2937c478bd9Sstevel@tonic-gate */ 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate krb5_error_code 2967c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message, 2977c478bd9Sstevel@tonic-gate const krb5_data *realm, krb5_data *reply, 298505d05c7Sgtb int *use_master, int tcp_only) 2997c478bd9Sstevel@tonic-gate { 3007c478bd9Sstevel@tonic-gate krb5_error_code retval; 3017c478bd9Sstevel@tonic-gate struct addrlist addrs; 302505d05c7Sgtb int socktype1 = 0, socktype2 = 0, addr_used; 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate /* 3057c478bd9Sstevel@tonic-gate * find KDC location(s) for realm 3067c478bd9Sstevel@tonic-gate */ 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem, 3107c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be 3117c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or 3127c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also, 3137c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} 3147c478bd9Sstevel@tonic-gate * should probably be returned as well. 3157c478bd9Sstevel@tonic-gate */ 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /*LINTED*/ 3187c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", 3197c478bd9Sstevel@tonic-gate /*LINTED*/ 320505d05c7Sgtb message->length, message->data, realm, *use_master, tcp_only); 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) { 3237c478bd9Sstevel@tonic-gate int tmp; 3247c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile, 3257c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0, 3267c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp); 3277c478bd9Sstevel@tonic-gate if (retval) 3287c478bd9Sstevel@tonic-gate return retval; 3297c478bd9Sstevel@tonic-gate if (tmp < 0) 3307c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT; 331*56a424ccSmp else if (tmp > HARD_UDP_LIMIT) 3327c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is 3337c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can 3347c478bd9Sstevel@tonic-gate support. */ 3357c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT; 3367c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp; 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate 339505d05c7Sgtb retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate if (tcp_only) 3427c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0; 3437c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit) 3447c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; 3457c478bd9Sstevel@tonic-gate else 3467c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; 3477c478bd9Sstevel@tonic-gate 348505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); 3497c478bd9Sstevel@tonic-gate if (socktype2) { 3507c478bd9Sstevel@tonic-gate struct addrlist addrs2; 3517c478bd9Sstevel@tonic-gate 352505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs2, *use_master, 3537c478bd9Sstevel@tonic-gate socktype2, 0); 3547c478bd9Sstevel@tonic-gate if (retval == 0) { 3557c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2); 3567c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2); 3577c478bd9Sstevel@tonic-gate } 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) { 360505d05c7Sgtb retval = krb5int_sendto (context, message, &addrs, reply, 0, 0, 361505d05c7Sgtb &addr_used); 362505d05c7Sgtb if (retval == 0) { 363505d05c7Sgtb /* 364505d05c7Sgtb * Set use_master to 1 if we ended up talking to a master when 365505d05c7Sgtb * didn't explicitly request to 366505d05c7Sgtb */ 367505d05c7Sgtb 368505d05c7Sgtb if (*use_master == 0) { 369505d05c7Sgtb struct addrlist addrs3; 370505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs3, 1, 371505d05c7Sgtb addrs.addrs[addr_used]->ai_socktype, 372505d05c7Sgtb addrs.addrs[addr_used]->ai_family); 373505d05c7Sgtb if (retval == 0) { 374505d05c7Sgtb int i; 375505d05c7Sgtb for (i = 0; i < addrs3.naddrs; i++) { 376505d05c7Sgtb if (addrs.addrs[addr_used]->ai_addrlen == 377505d05c7Sgtb addrs3.addrs[i]->ai_addrlen && 378505d05c7Sgtb memcmp(addrs.addrs[addr_used]->ai_addr, 379505d05c7Sgtb addrs3.addrs[i]->ai_addr, 380505d05c7Sgtb addrs.addrs[addr_used]->ai_addrlen) == 0) { 381505d05c7Sgtb *use_master = 1; 382505d05c7Sgtb break; 383505d05c7Sgtb } 384505d05c7Sgtb } 385505d05c7Sgtb krb5int_free_addrlist (&addrs3); 386505d05c7Sgtb } 387505d05c7Sgtb } 388505d05c7Sgtb krb5int_free_addrlist (&addrs); 3897c478bd9Sstevel@tonic-gate return 0; 390505d05c7Sgtb } 391505d05c7Sgtb krb5int_free_addrlist (&addrs); 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate return retval; 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate 3977c478bd9Sstevel@tonic-gate /* 3987c478bd9Sstevel@tonic-gate * Notes: 3997c478bd9Sstevel@tonic-gate * 4007c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes 4017c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up 4027c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags 4037c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it 4047c478bd9Sstevel@tonic-gate * system-specific. 4057c478bd9Sstevel@tonic-gate * 4067c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually 4077c478bd9Sstevel@tonic-gate * fix the UDP code to do the same. 4087c478bd9Sstevel@tonic-gate * 4097c478bd9Sstevel@tonic-gate * To do: 4107c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options? 4117c478bd9Sstevel@tonic-gate * - error codes that don't suck 4127c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status 4137c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP 4147c478bd9Sstevel@tonic-gate * connections already in progress 4157c478bd9Sstevel@tonic-gate */ 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate #include <cm.h> 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate static const char *const state_strings[] = { 4207c478bd9Sstevel@tonic-gate "INITIALIZING", "CONNECTING", "WRITING", "READING", "FAILED" 4217c478bd9Sstevel@tonic-gate }; 4227c478bd9Sstevel@tonic-gate enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED }; 4237c478bd9Sstevel@tonic-gate struct incoming_krb5_message { 4247c478bd9Sstevel@tonic-gate size_t bufsizebytes_read; 4257c478bd9Sstevel@tonic-gate size_t bufsize; 4267c478bd9Sstevel@tonic-gate char *buf; 4277c478bd9Sstevel@tonic-gate char *pos; 4287c478bd9Sstevel@tonic-gate unsigned char bufsizebytes[4]; 4297c478bd9Sstevel@tonic-gate size_t n_left; 4307c478bd9Sstevel@tonic-gate }; 4317c478bd9Sstevel@tonic-gate struct conn_state { 4327c478bd9Sstevel@tonic-gate SOCKET fd; 4337c478bd9Sstevel@tonic-gate krb5_error_code err; 4347c478bd9Sstevel@tonic-gate enum conn_states state; 4357c478bd9Sstevel@tonic-gate unsigned int is_udp : 1; 4367c478bd9Sstevel@tonic-gate int (*service)(struct conn_state *, struct select_state *, int); 4377c478bd9Sstevel@tonic-gate struct addrinfo *addr; 4387c478bd9Sstevel@tonic-gate struct { 4397c478bd9Sstevel@tonic-gate struct { 4407c478bd9Sstevel@tonic-gate sg_buf sgbuf[2]; 4417c478bd9Sstevel@tonic-gate sg_buf *sgp; 4427c478bd9Sstevel@tonic-gate int sg_count; 4437c478bd9Sstevel@tonic-gate } out; 4447c478bd9Sstevel@tonic-gate struct incoming_krb5_message in; 4457c478bd9Sstevel@tonic-gate } x; 4467c478bd9Sstevel@tonic-gate }; 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp) 4497c478bd9Sstevel@tonic-gate { 4507c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) { 4517c478bd9Sstevel@tonic-gate dperror("gettimeofday"); 4527c478bd9Sstevel@tonic-gate return errno; 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate return 0; 4557c478bd9Sstevel@tonic-gate } 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate /* 4587c478bd9Sstevel@tonic-gate * Call select and return results. 4597c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout 4607c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets 4617c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate krb5_error_code 4647c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in, 4657c478bd9Sstevel@tonic-gate struct select_state *out, int *sret) 4667c478bd9Sstevel@tonic-gate { 4677c478bd9Sstevel@tonic-gate struct timeval now, *timo; 4687c478bd9Sstevel@tonic-gate krb5_error_code e; 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate *out = *in; 4717c478bd9Sstevel@tonic-gate e = getcurtime(&now); 4727c478bd9Sstevel@tonic-gate if (e) 4737c478bd9Sstevel@tonic-gate return e; 4747c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0) 4757c478bd9Sstevel@tonic-gate timo = 0; 4767c478bd9Sstevel@tonic-gate else { 4777c478bd9Sstevel@tonic-gate timo = &out->end_time; 4787c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec; 4797c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec; 4807c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) { 4817c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000; 4827c478bd9Sstevel@tonic-gate out->end_time.tv_sec--; 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) { 4857c478bd9Sstevel@tonic-gate *sret = 0; 4867c478bd9Sstevel@tonic-gate return 0; 4877c478bd9Sstevel@tonic-gate } 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate /*LINTED*/ 4907c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n", 4917c478bd9Sstevel@tonic-gate /*LINTED*/ 4927c478bd9Sstevel@tonic-gate out->max, &out->rfds, &out->wfds, &out->xfds, out->max, timo); 4937c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo); 4947c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate #ifdef DEBUG 4977c478bd9Sstevel@tonic-gate /*LINTED*/ 4987c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret); 4997c478bd9Sstevel@tonic-gate if (*sret < 0) 5007c478bd9Sstevel@tonic-gate /*LINTED*/ 5017c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e); 5027c478bd9Sstevel@tonic-gate else if (*sret == 0) 5037c478bd9Sstevel@tonic-gate /*LINTED*/ 5047c478bd9Sstevel@tonic-gate dprint(" (timeout)\n"); 5057c478bd9Sstevel@tonic-gate else 5067c478bd9Sstevel@tonic-gate /*LINTED*/ 5077c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max); 5087c478bd9Sstevel@tonic-gate #endif 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate if (*sret < 0) 5117c478bd9Sstevel@tonic-gate return e; 5127c478bd9Sstevel@tonic-gate return 0; 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn, 5167c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 5177c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn, 5187c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate static int 5227c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai, 5237c478bd9Sstevel@tonic-gate const krb5_data *message, unsigned char *message_len_buf, 5247c478bd9Sstevel@tonic-gate char **udpbufp) 5257c478bd9Sstevel@tonic-gate { 5267c478bd9Sstevel@tonic-gate state->state = INITIALIZING; 5277c478bd9Sstevel@tonic-gate state->err = 0; 5287c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf; 5297c478bd9Sstevel@tonic-gate state->addr = ai; 5307c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 5317c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 5327c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 5337c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4); 5347c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 5357c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2; 5367c478bd9Sstevel@tonic-gate state->is_udp = 0; 5377c478bd9Sstevel@tonic-gate state->service = service_tcp_fd; 5387c478bd9Sstevel@tonic-gate } else { 5397c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 5407c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 5417c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1; 5427c478bd9Sstevel@tonic-gate state->is_udp = 1; 5437c478bd9Sstevel@tonic-gate state->service = service_udp_fd; 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 5467c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size); 5477c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 5487c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)"); 5497c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 5507c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 5517c478bd9Sstevel@tonic-gate state->state = FAILED; 5527c478bd9Sstevel@tonic-gate return 1; 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp; 5567c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size; 5577c478bd9Sstevel@tonic-gate } 5587c478bd9Sstevel@tonic-gate return 0; 5597c478bd9Sstevel@tonic-gate } 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate static int 5627c478bd9Sstevel@tonic-gate start_connection (struct conn_state *state, struct select_state *selstate) 5637c478bd9Sstevel@tonic-gate { 5647c478bd9Sstevel@tonic-gate int fd, e; 5657c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr; 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate /*LINTED*/ 5687c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, 5697c478bd9Sstevel@tonic-gate /*LINTED*/ 5707c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family); 5717c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0); 5727c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) { 5737c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 5747c478bd9Sstevel@tonic-gate /*LINTED*/ 5757c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family); 5767c478bd9Sstevel@tonic-gate return -1; /* try other hosts */ 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate /* Make it non-blocking. */ 5797c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 5807c478bd9Sstevel@tonic-gate static const int one = 1; 5817c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 }; 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one)) 5847c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)"); 5857c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) 5867c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)"); 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */ 5907c478bd9Sstevel@tonic-gate /*LINTED*/ 5917c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai); 5927c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen); 5937c478bd9Sstevel@tonic-gate if (e != 0) { 5947c478bd9Sstevel@tonic-gate /* 5957c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking 5967c478bd9Sstevel@tonic-gate * connections. 5977c478bd9Sstevel@tonic-gate */ 5987c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { 5997c478bd9Sstevel@tonic-gate state->state = CONNECTING; 6007c478bd9Sstevel@tonic-gate } else { 6017c478bd9Sstevel@tonic-gate /*LINTED*/ 6027c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO); 6037c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 6047c478bd9Sstevel@tonic-gate state->state = FAILED; 6057c478bd9Sstevel@tonic-gate return -2; 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate } else { 6087c478bd9Sstevel@tonic-gate /* 6097c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it 6107c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before 6117c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network 6127c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it. 6137c478bd9Sstevel@tonic-gate */ 6147c478bd9Sstevel@tonic-gate state->state = WRITING; 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate /*LINTED*/ 6177c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]); 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate state->fd = fd; 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) { 6227c478bd9Sstevel@tonic-gate /* Send it now. */ 6237c478bd9Sstevel@tonic-gate int ret; 6247c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0]; 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate /*LINTED*/ 6277c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); 6287c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); 6297c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) { 6307c478bd9Sstevel@tonic-gate dperror("sendto"); 6317c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 6327c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6337c478bd9Sstevel@tonic-gate state->state = FAILED; 6347c478bd9Sstevel@tonic-gate return -3; 6357c478bd9Sstevel@tonic-gate } else { 6367c478bd9Sstevel@tonic-gate state->state = READING; 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate 6407c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds); 6417c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING) 6427c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds); 6437c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds); 6447c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd) 6457c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1; 6467c478bd9Sstevel@tonic-gate selstate->nfds++; 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate /*LINTED*/ 6497c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n", 6507c478bd9Sstevel@tonic-gate /*LINTED*/ 6517c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); 6527c478bd9Sstevel@tonic-gate 6537c478bd9Sstevel@tonic-gate return 0; 6547c478bd9Sstevel@tonic-gate } 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise. 6577c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response. 6587c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the 6597c478bd9Sstevel@tonic-gate next connection. */ 6607c478bd9Sstevel@tonic-gate static int 6617c478bd9Sstevel@tonic-gate maybe_send (struct conn_state *conn, struct select_state *selstate) 6627c478bd9Sstevel@tonic-gate { 6637c478bd9Sstevel@tonic-gate sg_buf *sg; 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate /*LINTED*/ 6667c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn, 6677c478bd9Sstevel@tonic-gate /*LINTED*/ 6687c478bd9Sstevel@tonic-gate state_strings[conn->state], conn->is_udp ? "udp" : "tcp"); 6697c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING) 6707c478bd9Sstevel@tonic-gate return start_connection(conn, selstate); 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */ 6737c478bd9Sstevel@tonic-gate if (conn->state == FAILED) { 6747c478bd9Sstevel@tonic-gate dprint("connection already closed\n"); 6757c478bd9Sstevel@tonic-gate return -1; 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) { 6797c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n"); 6807c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we 6817c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */ 6827c478bd9Sstevel@tonic-gate return -1; 6837c478bd9Sstevel@tonic-gate } 6847c478bd9Sstevel@tonic-gate 6857c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a 6867c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */ 6877c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0]; 6887c478bd9Sstevel@tonic-gate /*LINTED*/ 6897c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd); 6907c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) { 6917c478bd9Sstevel@tonic-gate dperror("send"); 6927c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass. 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the 6957c478bd9Sstevel@tonic-gate select callbacks? */ 6967c478bd9Sstevel@tonic-gate return -1; 6977c478bd9Sstevel@tonic-gate } 6987c478bd9Sstevel@tonic-gate /* Yay, it worked. */ 6997c478bd9Sstevel@tonic-gate return 0; 7007c478bd9Sstevel@tonic-gate } 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate static void 7037c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err) 7047c478bd9Sstevel@tonic-gate { 7057c478bd9Sstevel@tonic-gate conn->state = FAILED; 7067c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH); 7077c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds); 7087c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 7097c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds); 7107c478bd9Sstevel@tonic-gate conn->err = err; 7117c478bd9Sstevel@tonic-gate /*LINTED*/ 7127c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err); 7137c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */ 7147c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) { 7157c478bd9Sstevel@tonic-gate while (selstate->max > 0 7167c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds) 7177c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds) 7187c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds)) 7197c478bd9Sstevel@tonic-gate selstate->max--; 7207c478bd9Sstevel@tonic-gate /*LINTED*/ 7217c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max); 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate selstate->nfds--; 7247c478bd9Sstevel@tonic-gate } 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit 7277c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message, 7287c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */ 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate static int 7317c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate, 7327c478bd9Sstevel@tonic-gate int ssflags) 7337c478bd9Sstevel@tonic-gate { 7347c478bd9Sstevel@tonic-gate krb5_error_code e = 0; 7357c478bd9Sstevel@tonic-gate int nwritten, nread; 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) 7387c478bd9Sstevel@tonic-gate abort(); 7397c478bd9Sstevel@tonic-gate switch (conn->state) { 7407c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp; 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate case CONNECTING: 7437c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 7447c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */ 7457c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */; 7467c478bd9Sstevel@tonic-gate kill_conn: 7477c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e); 7487c478bd9Sstevel@tonic-gate if (e == EINVAL) { 7497c478bd9Sstevel@tonic-gate closesocket(conn->fd); 7507c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET; 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate return e == 0; 7537c478bd9Sstevel@tonic-gate } 7547c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 7557c478bd9Sstevel@tonic-gate handle_exception: 7567c478bd9Sstevel@tonic-gate e = 1; /* need only be non-zero */ 7577c478bd9Sstevel@tonic-gate goto kill_conn; 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate 7607c478bd9Sstevel@tonic-gate /* 7617c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail? 7627c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed. 7637c478bd9Sstevel@tonic-gate * Try writing, I guess, and find out. 7647c478bd9Sstevel@tonic-gate */ 7657c478bd9Sstevel@tonic-gate conn->state = WRITING; 7667c478bd9Sstevel@tonic-gate goto try_writing; 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate case WRITING: 7697c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 7707c478bd9Sstevel@tonic-gate e = E2BIG; 7717c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */ 7727c478bd9Sstevel@tonic-gate goto kill_conn; 7737c478bd9Sstevel@tonic-gate } 7747c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) 7757c478bd9Sstevel@tonic-gate goto handle_exception; 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate try_writing: 7787c478bd9Sstevel@tonic-gate /*LINTED*/ 7797c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n", 7807c478bd9Sstevel@tonic-gate /*LINTED*/ 7817c478bd9Sstevel@tonic-gate conn->x.out.sg_count, 7827c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) 7837c478bd9Sstevel@tonic-gate /*LINTED*/ 7847c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])), 7857c478bd9Sstevel@tonic-gate conn->fd); 7867c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, 7877c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp); 7887c478bd9Sstevel@tonic-gate if (nwritten < 0) { 7897c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 7907c478bd9Sstevel@tonic-gate /*LINTED*/ 7917c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e); 7927c478bd9Sstevel@tonic-gate goto kill_conn; 7937c478bd9Sstevel@tonic-gate } 7947c478bd9Sstevel@tonic-gate /*LINTED*/ 7957c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten); 7967c478bd9Sstevel@tonic-gate while (nwritten) { 7977c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp; 7987c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) { 7997c478bd9Sstevel@tonic-gate /*LINTED*/ 8007c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten); 8017c478bd9Sstevel@tonic-gate nwritten = 0; 8027c478bd9Sstevel@tonic-gate } else { 8037c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp); 8047c478bd9Sstevel@tonic-gate conn->x.out.sgp++; 8057c478bd9Sstevel@tonic-gate conn->x.out.sg_count--; 8067c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0) 8077c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */ 8087c478bd9Sstevel@tonic-gate abort(); 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) { 8127c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */ 8137c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because 8147c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/ 8157c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 8167c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data 8177c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode? 8187c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */ 8197c478bd9Sstevel@tonic-gate /*LINTED*/ 8207c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd); 8217c478bd9Sstevel@tonic-gate conn->state = READING; 8227c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0; 8237c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0; 8247c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 8257c478bd9Sstevel@tonic-gate conn->x.in.pos = 0; 8267c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0; 8277c478bd9Sstevel@tonic-gate } 8287c478bd9Sstevel@tonic-gate return 0; 8297c478bd9Sstevel@tonic-gate 8307c478bd9Sstevel@tonic-gate case READING: 8317c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 8327c478bd9Sstevel@tonic-gate if (conn->x.in.buf) { 8337c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 8347c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate goto handle_exception; 8377c478bd9Sstevel@tonic-gate } 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 8407c478bd9Sstevel@tonic-gate /* Reading data. */ 8417c478bd9Sstevel@tonic-gate /*LINTED*/ 8427c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n", 8437c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd); 8447c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); 8457c478bd9Sstevel@tonic-gate if (nread <= 0) { 8467c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET; 8477c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 8487c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 8497c478bd9Sstevel@tonic-gate goto kill_conn; 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread; 8527c478bd9Sstevel@tonic-gate conn->x.in.pos += nread; 8537c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) { 8547c478bd9Sstevel@tonic-gate /* We win! */ 8557c478bd9Sstevel@tonic-gate return 1; 8567c478bd9Sstevel@tonic-gate } 8577c478bd9Sstevel@tonic-gate } else { 8587c478bd9Sstevel@tonic-gate /* Reading length. */ 8597c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, 8607c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 8617c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read); 8627c478bd9Sstevel@tonic-gate if (nread < 0) { 8637c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 8647c478bd9Sstevel@tonic-gate goto kill_conn; 8657c478bd9Sstevel@tonic-gate } 8667c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread; 8677c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 8687c478bd9Sstevel@tonic-gate unsigned long len; 8697c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0]; 8707c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1]; 8717c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2]; 8727c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3]; 8737c478bd9Sstevel@tonic-gate /*LINTED*/ 8747c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len); 8757c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */ 8767c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) { 8777c478bd9Sstevel@tonic-gate e = E2BIG; 8787c478bd9Sstevel@tonic-gate goto kill_conn; 8797c478bd9Sstevel@tonic-gate } 8807c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len; 8817c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len); 8827c478bd9Sstevel@tonic-gate /*LINTED*/ 8837c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len, 8847c478bd9Sstevel@tonic-gate conn->x.in.buf); 8857c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) { 8867c478bd9Sstevel@tonic-gate /* allocation failure */ 8877c478bd9Sstevel@tonic-gate e = errno; 8887c478bd9Sstevel@tonic-gate goto kill_conn; 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate } 8917c478bd9Sstevel@tonic-gate } 8927c478bd9Sstevel@tonic-gate break; 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate default: 8957c478bd9Sstevel@tonic-gate abort(); 8967c478bd9Sstevel@tonic-gate } 8977c478bd9Sstevel@tonic-gate return 0; 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate static int 9017c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate, 9027c478bd9Sstevel@tonic-gate int ssflags) 9037c478bd9Sstevel@tonic-gate { 9047c478bd9Sstevel@tonic-gate int nread; 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) 9077c478bd9Sstevel@tonic-gate abort(); 9087c478bd9Sstevel@tonic-gate if (conn->state != READING) 9097c478bd9Sstevel@tonic-gate abort(); 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); 9127c478bd9Sstevel@tonic-gate if (nread < 0) { 9137c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO); 9147c478bd9Sstevel@tonic-gate return 0; 9157c478bd9Sstevel@tonic-gate } 9167c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread; 9177c478bd9Sstevel@tonic-gate return 1; 9187c478bd9Sstevel@tonic-gate } 9197c478bd9Sstevel@tonic-gate 9207c478bd9Sstevel@tonic-gate static int 9217c478bd9Sstevel@tonic-gate service_fds (struct select_state *selstate, 9227c478bd9Sstevel@tonic-gate struct conn_state *conns, size_t n_conns, int *winning_conn) 9237c478bd9Sstevel@tonic-gate { 9247c478bd9Sstevel@tonic-gate int e, selret; 9257c478bd9Sstevel@tonic-gate struct select_state sel_results; 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate e = 0; 9287c478bd9Sstevel@tonic-gate while (selstate->nfds > 0 9297c478bd9Sstevel@tonic-gate && (e = krb5int_cm_call_select(selstate, &sel_results, &selret)) == 0) { 9307c478bd9Sstevel@tonic-gate int i; 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate /*LINTED*/ 9337c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret); 9347c478bd9Sstevel@tonic-gate 9357c478bd9Sstevel@tonic-gate if (selret == 0) 9367c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */ 9377c478bd9Sstevel@tonic-gate return 0; 9387c478bd9Sstevel@tonic-gate 9397c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */ 9407c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) { 9417c478bd9Sstevel@tonic-gate int ssflags; 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET) 9447c478bd9Sstevel@tonic-gate continue; 9457c478bd9Sstevel@tonic-gate ssflags = 0; 9467c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.rfds)) 9477c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--; 9487c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.wfds)) 9497c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--; 9507c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.xfds)) 9517c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--; 9527c478bd9Sstevel@tonic-gate if (!ssflags) 9537c478bd9Sstevel@tonic-gate continue; 9547c478bd9Sstevel@tonic-gate 9557c478bd9Sstevel@tonic-gate /*LINTED*/ 9567c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n", 9577c478bd9Sstevel@tonic-gate /*LINTED*/ 9587c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "", 9597c478bd9Sstevel@tonic-gate /*LINTED*/ 9607c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "", 9617c478bd9Sstevel@tonic-gate /*LINTED*/ 9627c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "", 9637c478bd9Sstevel@tonic-gate /*LINTED*/ 9647c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr, 9657c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]); 9667c478bd9Sstevel@tonic-gate 9677c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) { 9687c478bd9Sstevel@tonic-gate dprint("fd service routine says we're done\n"); 9697c478bd9Sstevel@tonic-gate *winning_conn = i; 9707c478bd9Sstevel@tonic-gate return 1; 9717c478bd9Sstevel@tonic-gate } 9727c478bd9Sstevel@tonic-gate } 9737c478bd9Sstevel@tonic-gate } 9747c478bd9Sstevel@tonic-gate if (e != 0) { 9757c478bd9Sstevel@tonic-gate /*LINTED*/ 9767c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e); 9777c478bd9Sstevel@tonic-gate *winning_conn = -1; 9787c478bd9Sstevel@tonic-gate return 1; 9797c478bd9Sstevel@tonic-gate } 9807c478bd9Sstevel@tonic-gate return 0; 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate 9837c478bd9Sstevel@tonic-gate /* 9847c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior: 9857c478bd9Sstevel@tonic-gate * 9867c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end. 9877c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s. 9887c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s. 9897c478bd9Sstevel@tonic-gate * Fourth => 16s, etc. 9907c478bd9Sstevel@tonic-gate * 9917c478bd9Sstevel@tonic-gate * Restated: 9927c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass. 9937c478bd9Sstevel@tonic-gate * Per TCP server, 1s. 9947c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes. 9957c478bd9Sstevel@tonic-gate * 9967c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2. 9977c478bd9Sstevel@tonic-gate * 9987c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14. 9997c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30. 10007c478bd9Sstevel@tonic-gate * 10017c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on 10027c478bd9Sstevel@tonic-gate * one server, it counts as two. 10037c478bd9Sstevel@tonic-gate */ 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate krb5_error_code 10067c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 10077c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 10087c478bd9Sstevel@tonic-gate const struct addrlist *addrs, krb5_data *reply, 1009505d05c7Sgtb struct sockaddr_storage *localaddr, socklen_t *localaddrlen, 1010505d05c7Sgtb int *addr_used) 10117c478bd9Sstevel@tonic-gate { 10127c478bd9Sstevel@tonic-gate int i, pass; 10137c478bd9Sstevel@tonic-gate int delay_this_pass = 2; 10147c478bd9Sstevel@tonic-gate krb5_error_code retval; 10157c478bd9Sstevel@tonic-gate struct conn_state *conns; 10167c478bd9Sstevel@tonic-gate size_t n_conns, host; 10177c478bd9Sstevel@tonic-gate struct select_state select_state; 10187c478bd9Sstevel@tonic-gate struct timeval now; 10197c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0; 10207c478bd9Sstevel@tonic-gate unsigned char message_len_buf[4]; 10217c478bd9Sstevel@tonic-gate char *udpbuf = 0; 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate /*LINTED*/ 10247c478bd9Sstevel@tonic-gate dprint("krb5int_sendto(message=%d@%p)\n", message->length, message->data); 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate reply->data = 0; 10277c478bd9Sstevel@tonic-gate reply->length = 0; 10287c478bd9Sstevel@tonic-gate 10297c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs; 10307c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state)); 10317c478bd9Sstevel@tonic-gate if (conns == NULL) { 10327c478bd9Sstevel@tonic-gate return ENOMEM; 10337c478bd9Sstevel@tonic-gate } 10347c478bd9Sstevel@tonic-gate memset(conns, 0, n_conns * sizeof(conns[i])); 10357c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 10367c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET; 10377c478bd9Sstevel@tonic-gate } 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate select_state.max = 0; 10407c478bd9Sstevel@tonic-gate select_state.nfds = 0; 10417c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.rfds); 10427c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.wfds); 10437c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.xfds); 10447c478bd9Sstevel@tonic-gate 10457c478bd9Sstevel@tonic-gate message_len_buf[0] = (message->length >> 24) & 0xff; 10467c478bd9Sstevel@tonic-gate message_len_buf[1] = (message->length >> 16) & 0xff; 10477c478bd9Sstevel@tonic-gate message_len_buf[2] = (message->length >> 8) & 0xff; 10487c478bd9Sstevel@tonic-gate message_len_buf[3] = message->length & 0xff; 10497c478bd9Sstevel@tonic-gate 10507c478bd9Sstevel@tonic-gate /* Set up connections. */ 10517c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 10527c478bd9Sstevel@tonic-gate retval = setup_connection (&conns[host], addrs->addrs[host], 10537c478bd9Sstevel@tonic-gate message, message_len_buf, &udpbuf); 10547c478bd9Sstevel@tonic-gate if (retval) 10557c478bd9Sstevel@tonic-gate continue; 10567c478bd9Sstevel@tonic-gate } 10577c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) { 10587c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only. 10597c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */ 10607c478bd9Sstevel@tonic-gate /*LINTED*/ 10617c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass); 10627c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 10637c478bd9Sstevel@tonic-gate /*LINTED*/ 10647c478bd9Sstevel@tonic-gate dprint("host %d\n", host); 10657c478bd9Sstevel@tonic-gate 10667c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */ 10677c478bd9Sstevel@tonic-gate if (maybe_send(&conns[host], &select_state)) 10687c478bd9Sstevel@tonic-gate continue; 10697c478bd9Sstevel@tonic-gate 10707c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 10717c478bd9Sstevel@tonic-gate if (retval) 10727c478bd9Sstevel@tonic-gate goto egress; 10737c478bd9Sstevel@tonic-gate select_state.end_time = now; 10747c478bd9Sstevel@tonic-gate select_state.end_time.tv_sec += 1; 10757c478bd9Sstevel@tonic-gate e = service_fds(&select_state, conns, host+1, &winning_conn); 10767c478bd9Sstevel@tonic-gate if (e) 10777c478bd9Sstevel@tonic-gate break; 10787c478bd9Sstevel@tonic-gate if (pass > 0 && select_state.nfds == 0) 10797c478bd9Sstevel@tonic-gate /* 10807c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break 10817c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay, 10827c478bd9Sstevel@tonic-gate * we're probably about to open another connection. 10837c478bd9Sstevel@tonic-gate */ 10847c478bd9Sstevel@tonic-gate break; 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate if (e) 10877c478bd9Sstevel@tonic-gate break; 10887c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 10897c478bd9Sstevel@tonic-gate if (retval) 10907c478bd9Sstevel@tonic-gate goto egress; 10917c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select 10927c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop 10937c478bd9Sstevel@tonic-gate actually calls select. */ 10947c478bd9Sstevel@tonic-gate select_state.end_time.tv_sec += delay_this_pass; 10957c478bd9Sstevel@tonic-gate e = service_fds(&select_state, conns, host+1, &winning_conn); 10967c478bd9Sstevel@tonic-gate if (e) 10977c478bd9Sstevel@tonic-gate break; 10987c478bd9Sstevel@tonic-gate if (select_state.nfds == 0) 10997c478bd9Sstevel@tonic-gate break; 11007c478bd9Sstevel@tonic-gate delay_this_pass *= 2; 11017c478bd9Sstevel@tonic-gate } 11027c478bd9Sstevel@tonic-gate 11037c478bd9Sstevel@tonic-gate if (select_state.nfds == 0) { 11047c478bd9Sstevel@tonic-gate /* No addresses? */ 11057c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 11067c478bd9Sstevel@tonic-gate goto egress; 11077c478bd9Sstevel@tonic-gate } 11087c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) { 11097c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 11107c478bd9Sstevel@tonic-gate goto egress; 11117c478bd9Sstevel@tonic-gate } 11127c478bd9Sstevel@tonic-gate /* Success! */ 11137c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf; 11147c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos 11157c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf); 11167c478bd9Sstevel@tonic-gate /*LINTED*/ 1117505d05c7Sgtb dprint("returning %d bytes in buffer %p (winning_conn=%d)\n", 1118505d05c7Sgtb (int) reply->length, reply->data, winning_conn); 11197c478bd9Sstevel@tonic-gate retval = 0; 11207c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0; 1121505d05c7Sgtb if (addr_used) 1122505d05c7Sgtb *addr_used = winning_conn; 11237c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0) 11247c478bd9Sstevel@tonic-gate (void) getsockname(conns[winning_conn].fd, (struct sockaddr *)localaddr, 11257c478bd9Sstevel@tonic-gate localaddrlen); 11267c478bd9Sstevel@tonic-gate egress: 11277c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 11287c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET) 11297c478bd9Sstevel@tonic-gate close(conns[i].fd); 11307c478bd9Sstevel@tonic-gate if (conns[i].state == READING 11317c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0 11327c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf) 11337c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf); 11347c478bd9Sstevel@tonic-gate } 11357c478bd9Sstevel@tonic-gate free(conns); 11367c478bd9Sstevel@tonic-gate if (reply->data != udpbuf) 11377c478bd9Sstevel@tonic-gate free(udpbuf); 11387c478bd9Sstevel@tonic-gate return retval; 11397c478bd9Sstevel@tonic-gate } 1140