17c478bd9Sstevel@tonic-gate /* 2*159d09a2SMark Phalan * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate /* 67c478bd9Sstevel@tonic-gate * lib/krb5/os/sendto_kdc.c 77c478bd9Sstevel@tonic-gate * 8*159d09a2SMark Phalan * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology. 97c478bd9Sstevel@tonic-gate * All Rights Reserved. 107c478bd9Sstevel@tonic-gate * 117c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 127c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 137c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 147c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 157c478bd9Sstevel@tonic-gate * 167c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 177c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 187c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 197c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 207c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 217c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 227c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 237c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 247c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 257c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 267c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 277c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 287c478bd9Sstevel@tonic-gate * or implied warranty. 297c478bd9Sstevel@tonic-gate * 307c478bd9Sstevel@tonic-gate * 317c478bd9Sstevel@tonic-gate * Send packet to KDC for realm; wait for response, retransmitting 327c478bd9Sstevel@tonic-gate * as necessary. 337c478bd9Sstevel@tonic-gate */ 347c478bd9Sstevel@tonic-gate 35*159d09a2SMark Phalan #include "fake-addrinfo.h" 36*159d09a2SMark Phalan #include "k5-int.h" 37*159d09a2SMark Phalan 38*159d09a2SMark Phalan /* Solaris Kerberos */ 39*159d09a2SMark Phalan #include <syslog.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" 47*159d09a2SMark Phalan #ifdef _WIN32 48*159d09a2SMark Phalan #include <sys/timeb.h> 49*159d09a2SMark Phalan #endif 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #ifdef _AIX 527c478bd9Sstevel@tonic-gate #include <sys/select.h> 537c478bd9Sstevel@tonic-gate #endif 547c478bd9Sstevel@tonic-gate 55*159d09a2SMark Phalan #ifndef _WIN32 567c478bd9Sstevel@tonic-gate /* For FIONBIO. */ 577c478bd9Sstevel@tonic-gate #include <sys/ioctl.h> 587c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H 597c478bd9Sstevel@tonic-gate #include <sys/filio.h> 607c478bd9Sstevel@tonic-gate #endif 61*159d09a2SMark Phalan #endif 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate #define MAX_PASS 3 647c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */ 657c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT 1465 */ 667c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */ 677c478bd9Sstevel@tonic-gate 68*159d09a2SMark Phalan #undef DEBUG 69*159d09a2SMark Phalan 70*159d09a2SMark Phalan #ifdef DEBUG 71*159d09a2SMark Phalan int krb5int_debug_sendto_kdc = 0; 72*159d09a2SMark Phalan #define debug krb5int_debug_sendto_kdc 73*159d09a2SMark Phalan 747c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len) 757c478bd9Sstevel@tonic-gate { 76*159d09a2SMark Phalan #if 0 77*159d09a2SMark Phalan FILE *logfile; 78*159d09a2SMark Phalan logfile = fopen("/tmp/sendto_kdc.log", "a"); 79*159d09a2SMark Phalan if (logfile == NULL) 80*159d09a2SMark Phalan return; 81*159d09a2SMark Phalan fwrite(data, 1, len, logfile); 82*159d09a2SMark Phalan fclose(logfile); 83*159d09a2SMark Phalan #else 847c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr); 857c478bd9Sstevel@tonic-gate /* stderr is unbuffered */ 86*159d09a2SMark Phalan #endif 877c478bd9Sstevel@tonic-gate } 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler; 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate /* 927c478bd9Sstevel@tonic-gate * Solaris Kerberos: only including the debug stuff if DEBUG defined outside 937c478bd9Sstevel@tonic-gate * this file. 947c478bd9Sstevel@tonic-gate */ 957c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024]; 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */ 98*159d09a2SMark Phalan #if 0 99*159d09a2SMark Phalan static void put(const void *ptr, size_t len) 100*159d09a2SMark Phalan { 101*159d09a2SMark Phalan (*krb5int_sendtokdc_debug_handler)(ptr, len); 102*159d09a2SMark Phalan } 103*159d09a2SMark Phalan #endif 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate static void putstr(const char *str) 1067c478bd9Sstevel@tonic-gate { 1077c478bd9Sstevel@tonic-gate /* Solaris kerberos: build the string which will be passed to syslog later */ 1087c478bd9Sstevel@tonic-gate strlcat(global_err_str, str, sizeof (global_err_str)); 1097c478bd9Sstevel@tonic-gate } 110*159d09a2SMark Phalan #else 111*159d09a2SMark Phalan void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0; 112*159d09a2SMark Phalan #endif 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint 115*159d09a2SMark Phalan void 1167c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...) 1177c478bd9Sstevel@tonic-gate { 118*159d09a2SMark Phalan #ifdef DEBUG 1197c478bd9Sstevel@tonic-gate va_list args; 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* Temporaries for variable arguments, etc. */ 1227c478bd9Sstevel@tonic-gate krb5_error_code kerr; 1237c478bd9Sstevel@tonic-gate int err; 1247c478bd9Sstevel@tonic-gate fd_set *rfds, *wfds, *xfds; 1257c478bd9Sstevel@tonic-gate int i; 1267c478bd9Sstevel@tonic-gate int maxfd; 1277c478bd9Sstevel@tonic-gate struct timeval *tv; 1287c478bd9Sstevel@tonic-gate struct addrinfo *ai; 1297c478bd9Sstevel@tonic-gate const krb5_data *d; 1307c478bd9Sstevel@tonic-gate char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 1317c478bd9Sstevel@tonic-gate const char *p; 132*159d09a2SMark Phalan #ifndef max 133*159d09a2SMark Phalan #define max(a,b) ((a) > (b) ? (a) : (b)) 134*159d09a2SMark Phalan #endif 135*159d09a2SMark Phalan char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)]; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * Solaris kerberos: modified this function to create a string to pass to 1397c478bd9Sstevel@tonic-gate * syslog() 1407c478bd9Sstevel@tonic-gate */ 1417c478bd9Sstevel@tonic-gate global_err_str[0] = NULL; 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate va_start(args, fmt); 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate #define putf(FMT,X) (sprintf(tmpbuf,FMT,X),putstr(tmpbuf)) 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate for (; *fmt; fmt++) { 1487c478bd9Sstevel@tonic-gate if (*fmt != '%') { 1497c478bd9Sstevel@tonic-gate /* Possible optimization: Look for % and print all chars 1507c478bd9Sstevel@tonic-gate up to it in one call. */ 1517c478bd9Sstevel@tonic-gate putf("%c", *fmt); 1527c478bd9Sstevel@tonic-gate continue; 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate /* After this, always processing a '%' sequence. */ 1557c478bd9Sstevel@tonic-gate fmt++; 1567c478bd9Sstevel@tonic-gate switch (*fmt) { 1577c478bd9Sstevel@tonic-gate case 0: 1587c478bd9Sstevel@tonic-gate default: 1597c478bd9Sstevel@tonic-gate abort(); 1607c478bd9Sstevel@tonic-gate case 'E': 1617c478bd9Sstevel@tonic-gate /* %E => krb5_error_code */ 1627c478bd9Sstevel@tonic-gate kerr = va_arg(args, krb5_error_code); 1637c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%lu/", (unsigned long) kerr); 1647c478bd9Sstevel@tonic-gate putstr(tmpbuf); 1657c478bd9Sstevel@tonic-gate p = error_message(kerr); 1667c478bd9Sstevel@tonic-gate putstr(p); 1677c478bd9Sstevel@tonic-gate break; 1687c478bd9Sstevel@tonic-gate case 'm': 1697c478bd9Sstevel@tonic-gate /* %m => errno value (int) */ 1707c478bd9Sstevel@tonic-gate /* Like syslog's %m except the errno value is passed in 1717c478bd9Sstevel@tonic-gate rather than the current value. */ 1727c478bd9Sstevel@tonic-gate err = va_arg(args, int); 1737c478bd9Sstevel@tonic-gate putf("%d/", err); 174*159d09a2SMark Phalan p = NULL; 175*159d09a2SMark Phalan #ifdef HAVE_STRERROR_R 176*159d09a2SMark Phalan if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0) 177*159d09a2SMark Phalan p = tmpbuf; 178*159d09a2SMark Phalan #endif 179*159d09a2SMark Phalan if (p == NULL) 180*159d09a2SMark Phalan p = strerror(err); 1817c478bd9Sstevel@tonic-gate putstr(p); 1827c478bd9Sstevel@tonic-gate break; 1837c478bd9Sstevel@tonic-gate case 'F': 1847c478bd9Sstevel@tonic-gate /* %F => fd_set *, fd_set *, fd_set *, int */ 1857c478bd9Sstevel@tonic-gate rfds = va_arg(args, fd_set *); 1867c478bd9Sstevel@tonic-gate wfds = va_arg(args, fd_set *); 1877c478bd9Sstevel@tonic-gate xfds = va_arg(args, fd_set *); 1887c478bd9Sstevel@tonic-gate maxfd = va_arg(args, int); 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate for (i = 0; i < maxfd; i++) { 1917c478bd9Sstevel@tonic-gate int r = FD_ISSET(i, rfds); 1927c478bd9Sstevel@tonic-gate int w = wfds && FD_ISSET(i, wfds); 1937c478bd9Sstevel@tonic-gate int x = xfds && FD_ISSET(i, xfds); 1947c478bd9Sstevel@tonic-gate if (r || w || x) { 1957c478bd9Sstevel@tonic-gate putf(" %d", i); 1967c478bd9Sstevel@tonic-gate if (r) 1977c478bd9Sstevel@tonic-gate putstr("r"); 1987c478bd9Sstevel@tonic-gate if (w) 1997c478bd9Sstevel@tonic-gate putstr("w"); 2007c478bd9Sstevel@tonic-gate if (x) 2017c478bd9Sstevel@tonic-gate putstr("x"); 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate putstr(" "); 2057c478bd9Sstevel@tonic-gate break; 2067c478bd9Sstevel@tonic-gate case 's': 2077c478bd9Sstevel@tonic-gate /* %s => char * */ 2087c478bd9Sstevel@tonic-gate p = va_arg(args, const char *); 2097c478bd9Sstevel@tonic-gate putstr(p); 2107c478bd9Sstevel@tonic-gate break; 2117c478bd9Sstevel@tonic-gate case 't': 2127c478bd9Sstevel@tonic-gate /* %t => struct timeval * */ 2137c478bd9Sstevel@tonic-gate tv = va_arg(args, struct timeval *); 2147c478bd9Sstevel@tonic-gate if (tv) { 2157c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%ld.%06ld", 2167c478bd9Sstevel@tonic-gate (long) tv->tv_sec, (long) tv->tv_usec); 2177c478bd9Sstevel@tonic-gate putstr(tmpbuf); 2187c478bd9Sstevel@tonic-gate } else 2197c478bd9Sstevel@tonic-gate putstr("never"); 2207c478bd9Sstevel@tonic-gate break; 2217c478bd9Sstevel@tonic-gate case 'd': 2227c478bd9Sstevel@tonic-gate /* %d => int */ 2237c478bd9Sstevel@tonic-gate putf("%d", va_arg(args, int)); 2247c478bd9Sstevel@tonic-gate break; 2257c478bd9Sstevel@tonic-gate case 'p': 2267c478bd9Sstevel@tonic-gate /* %p => pointer */ 2277c478bd9Sstevel@tonic-gate putf("%p", va_arg(args, void*)); 2287c478bd9Sstevel@tonic-gate break; 2297c478bd9Sstevel@tonic-gate case 'A': 2307c478bd9Sstevel@tonic-gate /* %A => addrinfo */ 2317c478bd9Sstevel@tonic-gate ai = va_arg(args, struct addrinfo *); 232*159d09a2SMark Phalan if (ai->ai_socktype == SOCK_DGRAM) 233*159d09a2SMark Phalan strcpy(tmpbuf, "dgram"); 234*159d09a2SMark Phalan else if (ai->ai_socktype == SOCK_STREAM) 235*159d09a2SMark Phalan strcpy(tmpbuf, "stream"); 236*159d09a2SMark Phalan else 237*159d09a2SMark Phalan sprintf(tmpbuf, "socktype%d", ai->ai_socktype); 2387c478bd9Sstevel@tonic-gate if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen, 2397c478bd9Sstevel@tonic-gate addrbuf, sizeof (addrbuf), 2407c478bd9Sstevel@tonic-gate portbuf, sizeof (portbuf), 241*159d09a2SMark Phalan NI_NUMERICHOST | NI_NUMERICSERV)) { 242*159d09a2SMark Phalan if (ai->ai_addr->sa_family == AF_UNSPEC) 243*159d09a2SMark Phalan strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC"); 244*159d09a2SMark Phalan else 245*159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family); 246*159d09a2SMark Phalan } else 247*159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " %s.%s", addrbuf, portbuf); 2487c478bd9Sstevel@tonic-gate putstr(tmpbuf); 2497c478bd9Sstevel@tonic-gate break; 2507c478bd9Sstevel@tonic-gate case 'D': 2517c478bd9Sstevel@tonic-gate /* %D => krb5_data * */ 2527c478bd9Sstevel@tonic-gate d = va_arg(args, krb5_data *); 253*159d09a2SMark Phalan /* Solaris Kerberos */ 2547c478bd9Sstevel@tonic-gate p = d->data; 2557c478bd9Sstevel@tonic-gate putstr("0x"); 2567c478bd9Sstevel@tonic-gate for (i = 0; i < d->length; i++) { 2577c478bd9Sstevel@tonic-gate putf("%.2x", *p++); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate break; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate va_end(args); 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* Solaris kerberos: use syslog() for debug output */ 2657c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, global_err_str); 266*159d09a2SMark Phalan #endif 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 269*159d09a2SMark Phalan #define print_addrlist krb5int_print_addrlist 270*159d09a2SMark Phalan static void 271*159d09a2SMark Phalan print_addrlist (const struct addrlist *a) 272*159d09a2SMark Phalan { 273*159d09a2SMark Phalan int i; 274*159d09a2SMark Phalan dprint("%d{", a->naddrs); 275*159d09a2SMark Phalan for (i = 0; i < a->naddrs; i++) 276*159d09a2SMark Phalan dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai); 277*159d09a2SMark Phalan dprint("}"); 278*159d09a2SMark Phalan } 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate static int 2817c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src) 2827c478bd9Sstevel@tonic-gate { 283*159d09a2SMark Phalan /* Wouldn't it be nice if we could filter out duplicates? The 284*159d09a2SMark Phalan alloc/free handling makes that pretty difficult though. */ 2857c478bd9Sstevel@tonic-gate int err, i; 2867c478bd9Sstevel@tonic-gate 287*159d09a2SMark Phalan /* Solaris Kerberos */ 2887c478bd9Sstevel@tonic-gate #ifdef DEBUG 2897c478bd9Sstevel@tonic-gate /*LINTED*/ 2907c478bd9Sstevel@tonic-gate dprint("merging addrlists:\n\tlist1: "); 2917c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 2927c478bd9Sstevel@tonic-gate /*LINTED*/ 293*159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai); 2947c478bd9Sstevel@tonic-gate /*LINTED*/ 2957c478bd9Sstevel@tonic-gate dprint("\n\tlist2: "); 2967c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) 2977c478bd9Sstevel@tonic-gate /*LINTED*/ 298*159d09a2SMark Phalan dprint(" %A", src->addrs[i].ai); 2997c478bd9Sstevel@tonic-gate /*LINTED*/ 3007c478bd9Sstevel@tonic-gate dprint("\n"); 3017c478bd9Sstevel@tonic-gate #endif 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate err = krb5int_grow_addrlist (dest, src->naddrs); 3047c478bd9Sstevel@tonic-gate if (err) 3057c478bd9Sstevel@tonic-gate return err; 3067c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) { 3077c478bd9Sstevel@tonic-gate dest->addrs[dest->naddrs + i] = src->addrs[i]; 308*159d09a2SMark Phalan src->addrs[i].ai = 0; 309*159d09a2SMark Phalan src->addrs[i].freefn = 0; 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate dest->naddrs += i; 3127c478bd9Sstevel@tonic-gate src->naddrs = 0; 3137c478bd9Sstevel@tonic-gate 314*159d09a2SMark Phalan /* Solaris Kerberos */ 3157c478bd9Sstevel@tonic-gate #ifdef DEBUG 3167c478bd9Sstevel@tonic-gate /*LINTED*/ 3177c478bd9Sstevel@tonic-gate dprint("\tout: "); 3187c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 3197c478bd9Sstevel@tonic-gate /*LINTED*/ 320*159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai); 3217c478bd9Sstevel@tonic-gate /*LINTED*/ 3227c478bd9Sstevel@tonic-gate dprint("\n"); 3237c478bd9Sstevel@tonic-gate #endif 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate return 0; 3267c478bd9Sstevel@tonic-gate } 3277c478bd9Sstevel@tonic-gate 328*159d09a2SMark Phalan static int 329*159d09a2SMark Phalan in_addrlist (struct addrinfo *thisaddr, struct addrlist *list) 330*159d09a2SMark Phalan { 331*159d09a2SMark Phalan int i; 332*159d09a2SMark Phalan for (i = 0; i < list->naddrs; i++) { 333*159d09a2SMark Phalan if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen 334*159d09a2SMark Phalan && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr, 335*159d09a2SMark Phalan thisaddr->ai_addrlen)) 336*159d09a2SMark Phalan return 1; 337*159d09a2SMark Phalan } 338*159d09a2SMark Phalan return 0; 339*159d09a2SMark Phalan } 340*159d09a2SMark Phalan 341*159d09a2SMark Phalan static int 342*159d09a2SMark Phalan check_for_svc_unavailable (krb5_context context, 343*159d09a2SMark Phalan const krb5_data *reply, 344*159d09a2SMark Phalan void *msg_handler_data) 345*159d09a2SMark Phalan { 346*159d09a2SMark Phalan krb5_error_code *retval = (krb5_error_code *)msg_handler_data; 347*159d09a2SMark Phalan 348*159d09a2SMark Phalan *retval = 0; 349*159d09a2SMark Phalan 350*159d09a2SMark Phalan if (krb5_is_krb_error(reply)) { 351*159d09a2SMark Phalan krb5_error *err_reply; 352*159d09a2SMark Phalan 353*159d09a2SMark Phalan if (decode_krb5_error(reply, &err_reply) == 0) { 354*159d09a2SMark Phalan *retval = err_reply->error; 355*159d09a2SMark Phalan krb5_free_error(context, err_reply); 356*159d09a2SMark Phalan 357*159d09a2SMark Phalan /* Returning 0 means continue to next KDC */ 358*159d09a2SMark Phalan return (*retval != KDC_ERR_SVC_UNAVAILABLE); 359*159d09a2SMark Phalan } 360*159d09a2SMark Phalan } 361*159d09a2SMark Phalan 362*159d09a2SMark Phalan return 1; 363*159d09a2SMark Phalan } 364*159d09a2SMark Phalan 3657c478bd9Sstevel@tonic-gate /* 3667c478bd9Sstevel@tonic-gate * send the formatted request 'message' to a KDC for realm 'realm' and 3677c478bd9Sstevel@tonic-gate * return the response (if any) in 'reply'. 3687c478bd9Sstevel@tonic-gate * 3697c478bd9Sstevel@tonic-gate * If the message is sent and a response is received, 0 is returned, 3707c478bd9Sstevel@tonic-gate * otherwise an error code is returned. 3717c478bd9Sstevel@tonic-gate * 3727c478bd9Sstevel@tonic-gate * The storage for 'reply' is allocated and should be freed by the caller 3737c478bd9Sstevel@tonic-gate * when finished. 3747c478bd9Sstevel@tonic-gate */ 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate krb5_error_code 3777c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message, 3787c478bd9Sstevel@tonic-gate const krb5_data *realm, krb5_data *reply, 379505d05c7Sgtb int *use_master, int tcp_only) 3807c478bd9Sstevel@tonic-gate { 381*159d09a2SMark Phalan krb5_error_code retval, retval2; 3827c478bd9Sstevel@tonic-gate struct addrlist addrs; 383505d05c7Sgtb int socktype1 = 0, socktype2 = 0, addr_used; 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate /* 3867c478bd9Sstevel@tonic-gate * find KDC location(s) for realm 3877c478bd9Sstevel@tonic-gate */ 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate /* 3907c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem, 3917c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be 3927c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or 3937c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also, 3947c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} 3957c478bd9Sstevel@tonic-gate * should probably be returned as well. 3967c478bd9Sstevel@tonic-gate */ 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate /*LINTED*/ 3997c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", 4007c478bd9Sstevel@tonic-gate /*LINTED*/ 401505d05c7Sgtb message->length, message->data, realm, *use_master, tcp_only); 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) { 4047c478bd9Sstevel@tonic-gate int tmp; 4057c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile, 4067c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0, 4077c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp); 4087c478bd9Sstevel@tonic-gate if (retval) 4097c478bd9Sstevel@tonic-gate return retval; 4107c478bd9Sstevel@tonic-gate if (tmp < 0) 4117c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT; 41256a424ccSmp else if (tmp > HARD_UDP_LIMIT) 4137c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is 4147c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can 4157c478bd9Sstevel@tonic-gate support. */ 4167c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT; 4177c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp; 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 420505d05c7Sgtb retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate if (tcp_only) 4237c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0; 4247c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit) 4257c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; 4267c478bd9Sstevel@tonic-gate else 4277c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; 4287c478bd9Sstevel@tonic-gate 429505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); 4307c478bd9Sstevel@tonic-gate if (socktype2) { 4317c478bd9Sstevel@tonic-gate struct addrlist addrs2; 4327c478bd9Sstevel@tonic-gate 433*159d09a2SMark Phalan retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master, 434*159d09a2SMark Phalan socktype2, 0); 435*159d09a2SMark Phalan #if 0 436*159d09a2SMark Phalan if (retval2 == 0) { 437*159d09a2SMark Phalan (void) merge_addrlists(&addrs, &addrs2); 438*159d09a2SMark Phalan krb5int_free_addrlist(&addrs2); 439*159d09a2SMark Phalan retval = 0; 440*159d09a2SMark Phalan } else if (retval == KRB5_REALM_CANT_RESOLVE) { 441*159d09a2SMark Phalan retval = retval2; 442*159d09a2SMark Phalan } 443*159d09a2SMark Phalan #else 444*159d09a2SMark Phalan retval = retval2; 4457c478bd9Sstevel@tonic-gate if (retval == 0) { 4467c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2); 4477c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2); 4487c478bd9Sstevel@tonic-gate } 449*159d09a2SMark Phalan #endif 4507c478bd9Sstevel@tonic-gate } 451*159d09a2SMark Phalan 4527c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) { 453*159d09a2SMark Phalan krb5_error_code err = 0; 454*159d09a2SMark Phalan 455*159d09a2SMark Phalan retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, 456*159d09a2SMark Phalan 0, 0, &addr_used, check_for_svc_unavailable, &err); 457*159d09a2SMark Phalan switch (retval) { 458*159d09a2SMark Phalan case 0: 459505d05c7Sgtb /* 460*159d09a2SMark Phalan * Set use_master to 1 if we ended up talking to a master when 461*159d09a2SMark Phalan * we didn't explicitly request to 462*159d09a2SMark Phalan */ 463*159d09a2SMark Phalan if (*use_master == 0) { 464*159d09a2SMark Phalan struct addrlist addrs3; 465*159d09a2SMark Phalan retval = krb5_locate_kdc(context, realm, &addrs3, 1, 466*159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_socktype, 467*159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_family); 468*159d09a2SMark Phalan if (retval == 0) { 469*159d09a2SMark Phalan if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3)) 470*159d09a2SMark Phalan *use_master = 1; 471*159d09a2SMark Phalan krb5int_free_addrlist (&addrs3); 472*159d09a2SMark Phalan } 473*159d09a2SMark Phalan } 474*159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 475*159d09a2SMark Phalan return 0; 476*159d09a2SMark Phalan default: 477*159d09a2SMark Phalan break; 478*159d09a2SMark Phalan /* Cases here are for constructing useful error messages. */ 479*159d09a2SMark Phalan case KRB5_KDC_UNREACH: 480*159d09a2SMark Phalan if (err == KDC_ERR_SVC_UNAVAILABLE) { 481*159d09a2SMark Phalan retval = KRB5KDC_ERR_SVC_UNAVAILABLE; 482*159d09a2SMark Phalan } else { 483*159d09a2SMark Phalan krb5_set_error_message(context, retval, 484*159d09a2SMark Phalan "Cannot contact any KDC for realm '%.*s'", 485*159d09a2SMark Phalan realm->length, realm->data); 486505d05c7Sgtb } 487*159d09a2SMark Phalan break; 488505d05c7Sgtb } 489*159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate return retval; 4927c478bd9Sstevel@tonic-gate } 4937c478bd9Sstevel@tonic-gate 494*159d09a2SMark Phalan #ifdef DEBUG 495*159d09a2SMark Phalan 496*159d09a2SMark Phalan #ifdef _WIN32 497*159d09a2SMark Phalan #define dperror(MSG) \ 498*159d09a2SMark Phalan dprint("%s: an error occurred ... " \ 499*159d09a2SMark Phalan "\tline=%d errno=%m socketerrno=%m\n", \ 500*159d09a2SMark Phalan (MSG), __LINE__, errno, SOCKET_ERRNO) 501*159d09a2SMark Phalan #else 502*159d09a2SMark Phalan #define dperror(MSG) dprint("%s: %m\n", MSG, errno) 503*159d09a2SMark Phalan #endif 504*159d09a2SMark Phalan #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0) 505*159d09a2SMark Phalan 506*159d09a2SMark Phalan #else /* ! DEBUG */ 507*159d09a2SMark Phalan 508*159d09a2SMark Phalan #define dperror(MSG) ((void)(MSG)) 509*159d09a2SMark Phalan #define dfprintf(ARGLIST) ((void)0) 510*159d09a2SMark Phalan 511*159d09a2SMark Phalan #endif 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate /* 5147c478bd9Sstevel@tonic-gate * Notes: 5157c478bd9Sstevel@tonic-gate * 5167c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes 5177c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up 5187c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags 5197c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it 5207c478bd9Sstevel@tonic-gate * system-specific. 5217c478bd9Sstevel@tonic-gate * 5227c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually 5237c478bd9Sstevel@tonic-gate * fix the UDP code to do the same. 5247c478bd9Sstevel@tonic-gate * 5257c478bd9Sstevel@tonic-gate * To do: 5267c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options? 5277c478bd9Sstevel@tonic-gate * - error codes that don't suck 5287c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status 5297c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP 5307c478bd9Sstevel@tonic-gate * connections already in progress 5317c478bd9Sstevel@tonic-gate */ 5327c478bd9Sstevel@tonic-gate 533*159d09a2SMark Phalan #include "cm.h" 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp) 5367c478bd9Sstevel@tonic-gate { 537*159d09a2SMark Phalan #ifdef _WIN32 538*159d09a2SMark Phalan struct _timeb tb; 539*159d09a2SMark Phalan _ftime(&tb); 540*159d09a2SMark Phalan tvp->tv_sec = tb.time; 541*159d09a2SMark Phalan tvp->tv_usec = tb.millitm * 1000; 542*159d09a2SMark Phalan /* Can _ftime fail? */ 543*159d09a2SMark Phalan return 0; 544*159d09a2SMark Phalan #else 5457c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) { 5467c478bd9Sstevel@tonic-gate dperror("gettimeofday"); 5477c478bd9Sstevel@tonic-gate return errno; 5487c478bd9Sstevel@tonic-gate } 5497c478bd9Sstevel@tonic-gate return 0; 550*159d09a2SMark Phalan #endif 5517c478bd9Sstevel@tonic-gate } 5527c478bd9Sstevel@tonic-gate 5537c478bd9Sstevel@tonic-gate /* 5547c478bd9Sstevel@tonic-gate * Call select and return results. 5557c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout 5567c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets 5577c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code. 5587c478bd9Sstevel@tonic-gate */ 5597c478bd9Sstevel@tonic-gate krb5_error_code 5607c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in, 5617c478bd9Sstevel@tonic-gate struct select_state *out, int *sret) 5627c478bd9Sstevel@tonic-gate { 5637c478bd9Sstevel@tonic-gate struct timeval now, *timo; 5647c478bd9Sstevel@tonic-gate krb5_error_code e; 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate *out = *in; 5677c478bd9Sstevel@tonic-gate e = getcurtime(&now); 5687c478bd9Sstevel@tonic-gate if (e) 5697c478bd9Sstevel@tonic-gate return e; 5707c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0) 5717c478bd9Sstevel@tonic-gate timo = 0; 5727c478bd9Sstevel@tonic-gate else { 5737c478bd9Sstevel@tonic-gate timo = &out->end_time; 5747c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec; 5757c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec; 5767c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) { 5777c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000; 5787c478bd9Sstevel@tonic-gate out->end_time.tv_sec--; 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) { 5817c478bd9Sstevel@tonic-gate *sret = 0; 5827c478bd9Sstevel@tonic-gate return 0; 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate } 5857c478bd9Sstevel@tonic-gate /*LINTED*/ 5867c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n", 5877c478bd9Sstevel@tonic-gate /*LINTED*/ 588*159d09a2SMark Phalan out->max, 589*159d09a2SMark Phalan &out->rfds, &out->wfds, &out->xfds, out->max, 590*159d09a2SMark Phalan timo); 5917c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo); 5927c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 5937c478bd9Sstevel@tonic-gate 594*159d09a2SMark Phalan /* Solaris Kerberos */ 5957c478bd9Sstevel@tonic-gate #ifdef DEBUG 5967c478bd9Sstevel@tonic-gate /*LINTED*/ 5977c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret); 5987c478bd9Sstevel@tonic-gate if (*sret < 0) 5997c478bd9Sstevel@tonic-gate /*LINTED*/ 6007c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e); 6017c478bd9Sstevel@tonic-gate else if (*sret == 0) 6027c478bd9Sstevel@tonic-gate /*LINTED*/ 6037c478bd9Sstevel@tonic-gate dprint(" (timeout)\n"); 6047c478bd9Sstevel@tonic-gate else 6057c478bd9Sstevel@tonic-gate /*LINTED*/ 6067c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max); 6077c478bd9Sstevel@tonic-gate #endif 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate if (*sret < 0) 6107c478bd9Sstevel@tonic-gate return e; 6117c478bd9Sstevel@tonic-gate return 0; 6127c478bd9Sstevel@tonic-gate } 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn, 6157c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6167c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn, 6177c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6187c478bd9Sstevel@tonic-gate 619*159d09a2SMark Phalan static void 620*159d09a2SMark Phalan set_conn_state_msg_length (struct conn_state *state, const krb5_data *message) 621*159d09a2SMark Phalan { 622*159d09a2SMark Phalan if (!message || message->length == 0) 623*159d09a2SMark Phalan return; 624*159d09a2SMark Phalan 625*159d09a2SMark Phalan if (!state->is_udp) { 626*159d09a2SMark Phalan 627*159d09a2SMark Phalan state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff; 628*159d09a2SMark Phalan state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff; 629*159d09a2SMark Phalan state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff; 630*159d09a2SMark Phalan state->x.out.msg_len_buf[3] = message->length & 0xff; 631*159d09a2SMark Phalan 632*159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4); 633*159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 634*159d09a2SMark Phalan state->x.out.sg_count = 2; 635*159d09a2SMark Phalan 636*159d09a2SMark Phalan } else { 637*159d09a2SMark Phalan 638*159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 639*159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], 0, 0); 640*159d09a2SMark Phalan state->x.out.sg_count = 1; 641*159d09a2SMark Phalan 642*159d09a2SMark Phalan } 643*159d09a2SMark Phalan } 644*159d09a2SMark Phalan 645*159d09a2SMark Phalan 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate static int 6487c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai, 649*159d09a2SMark Phalan const krb5_data *message, char **udpbufp) 6507c478bd9Sstevel@tonic-gate { 6517c478bd9Sstevel@tonic-gate state->state = INITIALIZING; 6527c478bd9Sstevel@tonic-gate state->err = 0; 6537c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf; 6547c478bd9Sstevel@tonic-gate state->addr = ai; 6557c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6567c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 6577c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 658*159d09a2SMark Phalan /* 6597c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4); 6607c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 6617c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2; 662*159d09a2SMark Phalan */ 663*159d09a2SMark Phalan 6647c478bd9Sstevel@tonic-gate state->is_udp = 0; 6657c478bd9Sstevel@tonic-gate state->service = service_tcp_fd; 666*159d09a2SMark Phalan set_conn_state_msg_length (state, message); 6677c478bd9Sstevel@tonic-gate } else { 668*159d09a2SMark Phalan /* 6697c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 6707c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 6717c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1; 672*159d09a2SMark Phalan */ 673*159d09a2SMark Phalan 6747c478bd9Sstevel@tonic-gate state->is_udp = 1; 6757c478bd9Sstevel@tonic-gate state->service = service_udp_fd; 676*159d09a2SMark Phalan set_conn_state_msg_length (state, message); 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 6797c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size); 6807c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 6817c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)"); 6827c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 6837c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6847c478bd9Sstevel@tonic-gate state->state = FAILED; 6857c478bd9Sstevel@tonic-gate return 1; 6867c478bd9Sstevel@tonic-gate } 6877c478bd9Sstevel@tonic-gate } 6887c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp; 6897c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size; 6907c478bd9Sstevel@tonic-gate } 6917c478bd9Sstevel@tonic-gate return 0; 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate static int 695*159d09a2SMark Phalan start_connection (struct conn_state *state, 696*159d09a2SMark Phalan struct select_state *selstate, 697*159d09a2SMark Phalan struct sendto_callback_info* callback_info, 698*159d09a2SMark Phalan krb5_data* callback_buffer) 6997c478bd9Sstevel@tonic-gate { 7007c478bd9Sstevel@tonic-gate int fd, e; 7017c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr; 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate /*LINTED*/ 7047c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, 7057c478bd9Sstevel@tonic-gate /*LINTED*/ 7067c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family); 7077c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0); 7087c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) { 7097c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7107c478bd9Sstevel@tonic-gate /*LINTED*/ 7117c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family); 7127c478bd9Sstevel@tonic-gate return -1; /* try other hosts */ 7137c478bd9Sstevel@tonic-gate } 7147c478bd9Sstevel@tonic-gate /* Make it non-blocking. */ 7157c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 7167c478bd9Sstevel@tonic-gate static const int one = 1; 7177c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 }; 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one)) 7207c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)"); 7217c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) 7227c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)"); 7237c478bd9Sstevel@tonic-gate } 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */ 7267c478bd9Sstevel@tonic-gate /*LINTED*/ 7277c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai); 7287c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen); 7297c478bd9Sstevel@tonic-gate if (e != 0) { 7307c478bd9Sstevel@tonic-gate /* 7317c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking 7327c478bd9Sstevel@tonic-gate * connections. 7337c478bd9Sstevel@tonic-gate */ 7347c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { 7357c478bd9Sstevel@tonic-gate state->state = CONNECTING; 736*159d09a2SMark Phalan state->fd = fd; 7377c478bd9Sstevel@tonic-gate } else { 7387c478bd9Sstevel@tonic-gate /*LINTED*/ 7397c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO); 740*159d09a2SMark Phalan (void) closesocket(fd); 7417c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7427c478bd9Sstevel@tonic-gate state->state = FAILED; 7437c478bd9Sstevel@tonic-gate return -2; 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate } else { 7467c478bd9Sstevel@tonic-gate /* 7477c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it 7487c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before 7497c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network 7507c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it. 7517c478bd9Sstevel@tonic-gate */ 7527c478bd9Sstevel@tonic-gate state->state = WRITING; 753*159d09a2SMark Phalan state->fd = fd; 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate /*LINTED*/ 7567c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]); 7577c478bd9Sstevel@tonic-gate 758*159d09a2SMark Phalan 759*159d09a2SMark Phalan /* 760*159d09a2SMark Phalan * Here's where KPASSWD callback gets the socket information it needs for 761*159d09a2SMark Phalan * a kpasswd request 762*159d09a2SMark Phalan */ 763*159d09a2SMark Phalan if (callback_info) { 764*159d09a2SMark Phalan 765*159d09a2SMark Phalan e = callback_info->pfn_callback(state, 766*159d09a2SMark Phalan callback_info->context, 767*159d09a2SMark Phalan callback_buffer); 768*159d09a2SMark Phalan if (e != 0) { 769*159d09a2SMark Phalan dprint("callback failed: %m\n", e); 770*159d09a2SMark Phalan (void) closesocket(fd); 771*159d09a2SMark Phalan state->err = e; 772*159d09a2SMark Phalan state->fd = INVALID_SOCKET; 773*159d09a2SMark Phalan state->state = FAILED; 774*159d09a2SMark Phalan return -3; 775*159d09a2SMark Phalan } 776*159d09a2SMark Phalan 777*159d09a2SMark Phalan dprint("callback %p (message=%d@%p)\n", 778*159d09a2SMark Phalan state, 779*159d09a2SMark Phalan callback_buffer->length, 780*159d09a2SMark Phalan callback_buffer->data); 781*159d09a2SMark Phalan 782*159d09a2SMark Phalan set_conn_state_msg_length( state, callback_buffer ); 783*159d09a2SMark Phalan } 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) { 7867c478bd9Sstevel@tonic-gate /* Send it now. */ 7877c478bd9Sstevel@tonic-gate int ret; 7887c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0]; 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate /*LINTED*/ 7917c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); 7927c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); 7937c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) { 7947c478bd9Sstevel@tonic-gate dperror("sendto"); 7957c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 7967c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 7977c478bd9Sstevel@tonic-gate state->state = FAILED; 798*159d09a2SMark Phalan return -4; 7997c478bd9Sstevel@tonic-gate } else { 8007c478bd9Sstevel@tonic-gate state->state = READING; 8017c478bd9Sstevel@tonic-gate } 8027c478bd9Sstevel@tonic-gate } 803*159d09a2SMark Phalan #ifdef DEBUG 804*159d09a2SMark Phalan if (debug) { 805*159d09a2SMark Phalan struct sockaddr_storage ss; 806*159d09a2SMark Phalan socklen_t sslen = sizeof(ss); 807*159d09a2SMark Phalan if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) { 808*159d09a2SMark Phalan struct addrinfo hack_ai; 809*159d09a2SMark Phalan memset(&hack_ai, 0, sizeof(hack_ai)); 810*159d09a2SMark Phalan hack_ai.ai_addr = (struct sockaddr *) &ss; 811*159d09a2SMark Phalan hack_ai.ai_addrlen = sslen; 812*159d09a2SMark Phalan hack_ai.ai_socktype = SOCK_DGRAM; 813*159d09a2SMark Phalan hack_ai.ai_family = ai->ai_family; 814*159d09a2SMark Phalan dprint("local socket address is %A\n", &hack_ai); 815*159d09a2SMark Phalan } 816*159d09a2SMark Phalan } 817*159d09a2SMark Phalan #endif 8187c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds); 8197c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING) 8207c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds); 8217c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds); 8227c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd) 8237c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1; 8247c478bd9Sstevel@tonic-gate selstate->nfds++; 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate /*LINTED*/ 8277c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n", 8287c478bd9Sstevel@tonic-gate /*LINTED*/ 8297c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate return 0; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise. 8357c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response. 8367c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the 8377c478bd9Sstevel@tonic-gate next connection. */ 8387c478bd9Sstevel@tonic-gate static int 839*159d09a2SMark Phalan maybe_send (struct conn_state *conn, 840*159d09a2SMark Phalan struct select_state *selstate, 841*159d09a2SMark Phalan struct sendto_callback_info* callback_info, 842*159d09a2SMark Phalan krb5_data* callback_buffer) 8437c478bd9Sstevel@tonic-gate { 8447c478bd9Sstevel@tonic-gate sg_buf *sg; 8457c478bd9Sstevel@tonic-gate 8467c478bd9Sstevel@tonic-gate /*LINTED*/ 8477c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn, 8487c478bd9Sstevel@tonic-gate /*LINTED*/ 849*159d09a2SMark Phalan state_strings[conn->state], 850*159d09a2SMark Phalan conn->is_udp ? "udp" : "tcp"); 8517c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING) 852*159d09a2SMark Phalan return start_connection(conn, selstate, callback_info, callback_buffer); 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */ 8557c478bd9Sstevel@tonic-gate if (conn->state == FAILED) { 8567c478bd9Sstevel@tonic-gate dprint("connection already closed\n"); 8577c478bd9Sstevel@tonic-gate return -1; 8587c478bd9Sstevel@tonic-gate } 8597c478bd9Sstevel@tonic-gate 8607c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) { 8617c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n"); 8627c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we 8637c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */ 8647c478bd9Sstevel@tonic-gate return -1; 8657c478bd9Sstevel@tonic-gate } 8667c478bd9Sstevel@tonic-gate 8677c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a 8687c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */ 8697c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0]; 8707c478bd9Sstevel@tonic-gate /*LINTED*/ 8717c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd); 8727c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) { 8737c478bd9Sstevel@tonic-gate dperror("send"); 8747c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass. 8757c478bd9Sstevel@tonic-gate 8767c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the 8777c478bd9Sstevel@tonic-gate select callbacks? */ 8787c478bd9Sstevel@tonic-gate return -1; 8797c478bd9Sstevel@tonic-gate } 8807c478bd9Sstevel@tonic-gate /* Yay, it worked. */ 8817c478bd9Sstevel@tonic-gate return 0; 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate 8847c478bd9Sstevel@tonic-gate static void 8857c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err) 8867c478bd9Sstevel@tonic-gate { 8877c478bd9Sstevel@tonic-gate conn->state = FAILED; 8887c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH); 8897c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds); 8907c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 8917c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds); 8927c478bd9Sstevel@tonic-gate conn->err = err; 8937c478bd9Sstevel@tonic-gate /*LINTED*/ 8947c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err); 8957c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */ 8967c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) { 8977c478bd9Sstevel@tonic-gate while (selstate->max > 0 8987c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds) 8997c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds) 9007c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds)) 9017c478bd9Sstevel@tonic-gate selstate->max--; 9027c478bd9Sstevel@tonic-gate /*LINTED*/ 9037c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max); 9047c478bd9Sstevel@tonic-gate } 9057c478bd9Sstevel@tonic-gate selstate->nfds--; 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate 908*159d09a2SMark Phalan /* Check socket for error. */ 909*159d09a2SMark Phalan static int 910*159d09a2SMark Phalan get_so_error(int fd) 911*159d09a2SMark Phalan { 912*159d09a2SMark Phalan int e, sockerr; 913*159d09a2SMark Phalan socklen_t sockerrlen; 914*159d09a2SMark Phalan 915*159d09a2SMark Phalan sockerr = 0; 916*159d09a2SMark Phalan sockerrlen = sizeof(sockerr); 917*159d09a2SMark Phalan e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen); 918*159d09a2SMark Phalan if (e != 0) { 919*159d09a2SMark Phalan /* What to do now? */ 920*159d09a2SMark Phalan e = SOCKET_ERRNO; 921*159d09a2SMark Phalan dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e); 922*159d09a2SMark Phalan return e; 923*159d09a2SMark Phalan } 924*159d09a2SMark Phalan return sockerr; 925*159d09a2SMark Phalan } 926*159d09a2SMark Phalan 9277c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit 9287c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message, 9297c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */ 9307c478bd9Sstevel@tonic-gate 9317c478bd9Sstevel@tonic-gate static int 9327c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate, 9337c478bd9Sstevel@tonic-gate int ssflags) 9347c478bd9Sstevel@tonic-gate { 9357c478bd9Sstevel@tonic-gate krb5_error_code e = 0; 9367c478bd9Sstevel@tonic-gate int nwritten, nread; 9377c478bd9Sstevel@tonic-gate 9387c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) 9397c478bd9Sstevel@tonic-gate abort(); 9407c478bd9Sstevel@tonic-gate switch (conn->state) { 9417c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp; 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate case CONNECTING: 9447c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 9457c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */ 9467c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */; 9477c478bd9Sstevel@tonic-gate kill_conn: 9487c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e); 9497c478bd9Sstevel@tonic-gate if (e == EINVAL) { 9507c478bd9Sstevel@tonic-gate closesocket(conn->fd); 9517c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET; 9527c478bd9Sstevel@tonic-gate } 9537c478bd9Sstevel@tonic-gate return e == 0; 9547c478bd9Sstevel@tonic-gate } 9557c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 9567c478bd9Sstevel@tonic-gate handle_exception: 957*159d09a2SMark Phalan e = get_so_error(conn->fd); 958*159d09a2SMark Phalan if (e) 959*159d09a2SMark Phalan dprint("socket error on exception fd: %m", e); 960*159d09a2SMark Phalan else 961*159d09a2SMark Phalan dprint("no socket error info available on exception fd"); 9627c478bd9Sstevel@tonic-gate goto kill_conn; 9637c478bd9Sstevel@tonic-gate } 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate /* 9667c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail? 9677c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed. 968*159d09a2SMark Phalan * Call getsockopt to see if error pending. 969*159d09a2SMark Phalan * 970*159d09a2SMark Phalan * (For most UNIX systems it works to just try writing the 971*159d09a2SMark Phalan * first time and detect an error. But Bill Dodd at IBM 972*159d09a2SMark Phalan * reports that some version of AIX, SIGPIPE can result.) 9737c478bd9Sstevel@tonic-gate */ 974*159d09a2SMark Phalan e = get_so_error(conn->fd); 975*159d09a2SMark Phalan if (e) { 976*159d09a2SMark Phalan dprint("socket error on write fd: %m", e); 977*159d09a2SMark Phalan goto kill_conn; 978*159d09a2SMark Phalan } 9797c478bd9Sstevel@tonic-gate conn->state = WRITING; 9807c478bd9Sstevel@tonic-gate goto try_writing; 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate case WRITING: 9837c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 9847c478bd9Sstevel@tonic-gate e = E2BIG; 9857c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */ 9867c478bd9Sstevel@tonic-gate goto kill_conn; 9877c478bd9Sstevel@tonic-gate } 9887c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) 9897c478bd9Sstevel@tonic-gate goto handle_exception; 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate try_writing: 9927c478bd9Sstevel@tonic-gate /*LINTED*/ 9937c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n", 9947c478bd9Sstevel@tonic-gate /*LINTED*/ 9957c478bd9Sstevel@tonic-gate conn->x.out.sg_count, 9967c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) 9977c478bd9Sstevel@tonic-gate /*LINTED*/ 9987c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])), 9997c478bd9Sstevel@tonic-gate conn->fd); 10007c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, 10017c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp); 10027c478bd9Sstevel@tonic-gate if (nwritten < 0) { 10037c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 10047c478bd9Sstevel@tonic-gate /*LINTED*/ 10057c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e); 10067c478bd9Sstevel@tonic-gate goto kill_conn; 10077c478bd9Sstevel@tonic-gate } 10087c478bd9Sstevel@tonic-gate /*LINTED*/ 10097c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten); 10107c478bd9Sstevel@tonic-gate while (nwritten) { 10117c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp; 10127c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) { 10137c478bd9Sstevel@tonic-gate /*LINTED*/ 10147c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten); 10157c478bd9Sstevel@tonic-gate nwritten = 0; 10167c478bd9Sstevel@tonic-gate } else { 10177c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp); 10187c478bd9Sstevel@tonic-gate conn->x.out.sgp++; 10197c478bd9Sstevel@tonic-gate conn->x.out.sg_count--; 10207c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0) 10217c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */ 10227c478bd9Sstevel@tonic-gate abort(); 10237c478bd9Sstevel@tonic-gate } 10247c478bd9Sstevel@tonic-gate } 10257c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) { 10267c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */ 10277c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because 10287c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/ 10297c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 10307c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data 10317c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode? 10327c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */ 10337c478bd9Sstevel@tonic-gate /*LINTED*/ 10347c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd); 10357c478bd9Sstevel@tonic-gate conn->state = READING; 10367c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0; 10377c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0; 10387c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10397c478bd9Sstevel@tonic-gate conn->x.in.pos = 0; 10407c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0; 10417c478bd9Sstevel@tonic-gate } 10427c478bd9Sstevel@tonic-gate return 0; 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate case READING: 10457c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 10467c478bd9Sstevel@tonic-gate if (conn->x.in.buf) { 10477c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10487c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10497c478bd9Sstevel@tonic-gate } 10507c478bd9Sstevel@tonic-gate goto handle_exception; 10517c478bd9Sstevel@tonic-gate } 10527c478bd9Sstevel@tonic-gate 10537c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 10547c478bd9Sstevel@tonic-gate /* Reading data. */ 10557c478bd9Sstevel@tonic-gate /*LINTED*/ 10567c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n", 10577c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd); 10587c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); 10597c478bd9Sstevel@tonic-gate if (nread <= 0) { 10607c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET; 10617c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10627c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10637c478bd9Sstevel@tonic-gate goto kill_conn; 10647c478bd9Sstevel@tonic-gate } 10657c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread; 10667c478bd9Sstevel@tonic-gate conn->x.in.pos += nread; 1067*159d09a2SMark Phalan /* Solaris Kerberos */ 10687c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) { 10697c478bd9Sstevel@tonic-gate /* We win! */ 10707c478bd9Sstevel@tonic-gate return 1; 10717c478bd9Sstevel@tonic-gate } 10727c478bd9Sstevel@tonic-gate } else { 10737c478bd9Sstevel@tonic-gate /* Reading length. */ 10747c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, 10757c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 10767c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read); 10777c478bd9Sstevel@tonic-gate if (nread < 0) { 10787c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 10797c478bd9Sstevel@tonic-gate goto kill_conn; 10807c478bd9Sstevel@tonic-gate } 10817c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread; 10827c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 10837c478bd9Sstevel@tonic-gate unsigned long len; 10847c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0]; 10857c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1]; 10867c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2]; 10877c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3]; 10887c478bd9Sstevel@tonic-gate /*LINTED*/ 10897c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len); 10907c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */ 10917c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) { 10927c478bd9Sstevel@tonic-gate e = E2BIG; 10937c478bd9Sstevel@tonic-gate goto kill_conn; 10947c478bd9Sstevel@tonic-gate } 10957c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len; 10967c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len); 10977c478bd9Sstevel@tonic-gate /*LINTED*/ 10987c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len, 10997c478bd9Sstevel@tonic-gate conn->x.in.buf); 11007c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) { 11017c478bd9Sstevel@tonic-gate /* allocation failure */ 11027c478bd9Sstevel@tonic-gate e = errno; 11037c478bd9Sstevel@tonic-gate goto kill_conn; 11047c478bd9Sstevel@tonic-gate } 11057c478bd9Sstevel@tonic-gate } 11067c478bd9Sstevel@tonic-gate } 11077c478bd9Sstevel@tonic-gate break; 11087c478bd9Sstevel@tonic-gate 11097c478bd9Sstevel@tonic-gate default: 11107c478bd9Sstevel@tonic-gate abort(); 11117c478bd9Sstevel@tonic-gate } 11127c478bd9Sstevel@tonic-gate return 0; 11137c478bd9Sstevel@tonic-gate } 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate static int 11167c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate, 11177c478bd9Sstevel@tonic-gate int ssflags) 11187c478bd9Sstevel@tonic-gate { 11197c478bd9Sstevel@tonic-gate int nread; 11207c478bd9Sstevel@tonic-gate 11217c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) 11227c478bd9Sstevel@tonic-gate abort(); 11237c478bd9Sstevel@tonic-gate if (conn->state != READING) 11247c478bd9Sstevel@tonic-gate abort(); 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); 11277c478bd9Sstevel@tonic-gate if (nread < 0) { 11287c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO); 11297c478bd9Sstevel@tonic-gate return 0; 11307c478bd9Sstevel@tonic-gate } 11317c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread; 11327c478bd9Sstevel@tonic-gate return 1; 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate static int 1136*159d09a2SMark Phalan service_fds (krb5_context context, 1137*159d09a2SMark Phalan struct select_state *selstate, 1138*159d09a2SMark Phalan struct conn_state *conns, size_t n_conns, int *winning_conn, 1139*159d09a2SMark Phalan struct select_state *seltemp, 1140*159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1141*159d09a2SMark Phalan void *msg_handler_data) 11427c478bd9Sstevel@tonic-gate { 11437c478bd9Sstevel@tonic-gate int e, selret; 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate e = 0; 11467c478bd9Sstevel@tonic-gate while (selstate->nfds > 0 1147*159d09a2SMark Phalan && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) { 11487c478bd9Sstevel@tonic-gate int i; 11497c478bd9Sstevel@tonic-gate 11507c478bd9Sstevel@tonic-gate /*LINTED*/ 11517c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret); 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate if (selret == 0) 11547c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */ 11557c478bd9Sstevel@tonic-gate return 0; 11567c478bd9Sstevel@tonic-gate 11577c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */ 11587c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) { 11597c478bd9Sstevel@tonic-gate int ssflags; 11607c478bd9Sstevel@tonic-gate 11617c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET) 11627c478bd9Sstevel@tonic-gate continue; 11637c478bd9Sstevel@tonic-gate ssflags = 0; 1164*159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->rfds)) 11657c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--; 1166*159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->wfds)) 11677c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--; 1168*159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->xfds)) 11697c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--; 11707c478bd9Sstevel@tonic-gate if (!ssflags) 11717c478bd9Sstevel@tonic-gate continue; 11727c478bd9Sstevel@tonic-gate 11737c478bd9Sstevel@tonic-gate /*LINTED*/ 11747c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n", 11757c478bd9Sstevel@tonic-gate /*LINTED*/ 11767c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "", 11777c478bd9Sstevel@tonic-gate /*LINTED*/ 11787c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "", 11797c478bd9Sstevel@tonic-gate /*LINTED*/ 11807c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "", 11817c478bd9Sstevel@tonic-gate /*LINTED*/ 11827c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr, 11837c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]); 11847c478bd9Sstevel@tonic-gate 11857c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) { 1186*159d09a2SMark Phalan int stop = 1; 1187*159d09a2SMark Phalan 1188*159d09a2SMark Phalan if (msg_handler != NULL) { 1189*159d09a2SMark Phalan krb5_data reply; 1190*159d09a2SMark Phalan 1191*159d09a2SMark Phalan reply.data = conns[i].x.in.buf; 1192*159d09a2SMark Phalan reply.length = conns[i].x.in.pos - conns[i].x.in.buf; 1193*159d09a2SMark Phalan 1194*159d09a2SMark Phalan stop = (msg_handler(context, &reply, msg_handler_data) != 0); 1195*159d09a2SMark Phalan } 1196*159d09a2SMark Phalan 1197*159d09a2SMark Phalan if (stop) { 1198*159d09a2SMark Phalan dprint("fd service routine says we're done\n"); 1199*159d09a2SMark Phalan *winning_conn = i; 1200*159d09a2SMark Phalan return 1; 1201*159d09a2SMark Phalan } 12027c478bd9Sstevel@tonic-gate } 12037c478bd9Sstevel@tonic-gate } 12047c478bd9Sstevel@tonic-gate } 12057c478bd9Sstevel@tonic-gate if (e != 0) { 12067c478bd9Sstevel@tonic-gate /*LINTED*/ 12077c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e); 12087c478bd9Sstevel@tonic-gate *winning_conn = -1; 12097c478bd9Sstevel@tonic-gate return 1; 12107c478bd9Sstevel@tonic-gate } 12117c478bd9Sstevel@tonic-gate return 0; 12127c478bd9Sstevel@tonic-gate } 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate /* 12157c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior: 12167c478bd9Sstevel@tonic-gate * 12177c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end. 12187c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s. 12197c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s. 12207c478bd9Sstevel@tonic-gate * Fourth => 16s, etc. 12217c478bd9Sstevel@tonic-gate * 12227c478bd9Sstevel@tonic-gate * Restated: 12237c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass. 12247c478bd9Sstevel@tonic-gate * Per TCP server, 1s. 12257c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes. 12267c478bd9Sstevel@tonic-gate * 12277c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2. 12287c478bd9Sstevel@tonic-gate * 12297c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14. 12307c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30. 12317c478bd9Sstevel@tonic-gate * 12327c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on 12337c478bd9Sstevel@tonic-gate * one server, it counts as two. 12347c478bd9Sstevel@tonic-gate */ 12357c478bd9Sstevel@tonic-gate 12367c478bd9Sstevel@tonic-gate krb5_error_code 12377c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 12387c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 1239*159d09a2SMark Phalan const struct addrlist *addrs, 1240*159d09a2SMark Phalan struct sendto_callback_info* callback_info, krb5_data *reply, 1241*159d09a2SMark Phalan struct sockaddr *localaddr, socklen_t *localaddrlen, 1242*159d09a2SMark Phalan struct sockaddr *remoteaddr, socklen_t *remoteaddrlen, 1243*159d09a2SMark Phalan int *addr_used, 1244*159d09a2SMark Phalan /* return 0 -> keep going, 1 -> quit */ 1245*159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1246*159d09a2SMark Phalan void *msg_handler_data) 12477c478bd9Sstevel@tonic-gate { 12487c478bd9Sstevel@tonic-gate int i, pass; 12497c478bd9Sstevel@tonic-gate int delay_this_pass = 2; 12507c478bd9Sstevel@tonic-gate krb5_error_code retval; 12517c478bd9Sstevel@tonic-gate struct conn_state *conns; 1252*159d09a2SMark Phalan krb5_data *callback_data = 0; 12537c478bd9Sstevel@tonic-gate size_t n_conns, host; 1254*159d09a2SMark Phalan struct select_state *sel_state; 12557c478bd9Sstevel@tonic-gate struct timeval now; 12567c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0; 12577c478bd9Sstevel@tonic-gate char *udpbuf = 0; 12587c478bd9Sstevel@tonic-gate 1259*159d09a2SMark Phalan if (message) 1260*159d09a2SMark Phalan dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data); 1261*159d09a2SMark Phalan else 1262*159d09a2SMark Phalan dprint("krb5int_sendto(callback=%p, addrlist=", callback_info); 1263*159d09a2SMark Phalan print_addrlist(addrs); 1264*159d09a2SMark Phalan dprint(")\n"); 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate reply->data = 0; 12677c478bd9Sstevel@tonic-gate reply->length = 0; 12687c478bd9Sstevel@tonic-gate 12697c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs; 12707c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state)); 12717c478bd9Sstevel@tonic-gate if (conns == NULL) { 12727c478bd9Sstevel@tonic-gate return ENOMEM; 12737c478bd9Sstevel@tonic-gate } 1274*159d09a2SMark Phalan 1275*159d09a2SMark Phalan memset(conns, 0, n_conns * sizeof(struct conn_state)); 1276*159d09a2SMark Phalan 1277*159d09a2SMark Phalan if (callback_info) { 1278*159d09a2SMark Phalan callback_data = malloc(n_conns * sizeof(krb5_data)); 1279*159d09a2SMark Phalan if (callback_data == NULL) { 1280*159d09a2SMark Phalan return ENOMEM; 1281*159d09a2SMark Phalan } 1282*159d09a2SMark Phalan 1283*159d09a2SMark Phalan memset(callback_data, 0, n_conns * sizeof(krb5_data)); 1284*159d09a2SMark Phalan } 1285*159d09a2SMark Phalan 12867c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 12877c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET; 12887c478bd9Sstevel@tonic-gate } 12897c478bd9Sstevel@tonic-gate 1290*159d09a2SMark Phalan /* One for use here, listing all our fds in use, and one for 1291*159d09a2SMark Phalan temporary use in service_fds, for the fds of interest. */ 1292*159d09a2SMark Phalan sel_state = malloc(2 * sizeof(*sel_state)); 1293*159d09a2SMark Phalan if (sel_state == NULL) { 1294*159d09a2SMark Phalan free(conns); 1295*159d09a2SMark Phalan return ENOMEM; 1296*159d09a2SMark Phalan } 1297*159d09a2SMark Phalan sel_state->max = 0; 1298*159d09a2SMark Phalan sel_state->nfds = 0; 1299*159d09a2SMark Phalan sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0; 1300*159d09a2SMark Phalan FD_ZERO(&sel_state->rfds); 1301*159d09a2SMark Phalan FD_ZERO(&sel_state->wfds); 1302*159d09a2SMark Phalan FD_ZERO(&sel_state->xfds); 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate /* Set up connections. */ 13067c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 1307*159d09a2SMark Phalan retval = setup_connection(&conns[host], 1308*159d09a2SMark Phalan addrs->addrs[host].ai, 1309*159d09a2SMark Phalan message, 1310*159d09a2SMark Phalan &udpbuf); 13117c478bd9Sstevel@tonic-gate if (retval) 13127c478bd9Sstevel@tonic-gate continue; 13137c478bd9Sstevel@tonic-gate } 13147c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) { 13157c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only. 13167c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */ 13177c478bd9Sstevel@tonic-gate /*LINTED*/ 13187c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass); 13197c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 13207c478bd9Sstevel@tonic-gate /*LINTED*/ 13217c478bd9Sstevel@tonic-gate dprint("host %d\n", host); 13227c478bd9Sstevel@tonic-gate 13237c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */ 1324*159d09a2SMark Phalan if (maybe_send(&conns[host], 1325*159d09a2SMark Phalan sel_state, 1326*159d09a2SMark Phalan callback_info, 1327*159d09a2SMark Phalan (callback_info ? &callback_data[host] : NULL))) 13287c478bd9Sstevel@tonic-gate continue; 13297c478bd9Sstevel@tonic-gate 13307c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13317c478bd9Sstevel@tonic-gate if (retval) 13327c478bd9Sstevel@tonic-gate goto egress; 1333*159d09a2SMark Phalan sel_state->end_time = now; 1334*159d09a2SMark Phalan sel_state->end_time.tv_sec += 1; 1335*159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1336*159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13377c478bd9Sstevel@tonic-gate if (e) 13387c478bd9Sstevel@tonic-gate break; 1339*159d09a2SMark Phalan if (pass > 0 && sel_state->nfds == 0) 13407c478bd9Sstevel@tonic-gate /* 13417c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break 13427c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay, 13437c478bd9Sstevel@tonic-gate * we're probably about to open another connection. 13447c478bd9Sstevel@tonic-gate */ 13457c478bd9Sstevel@tonic-gate break; 13467c478bd9Sstevel@tonic-gate } 13477c478bd9Sstevel@tonic-gate if (e) 13487c478bd9Sstevel@tonic-gate break; 13497c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13507c478bd9Sstevel@tonic-gate if (retval) 13517c478bd9Sstevel@tonic-gate goto egress; 13527c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select 13537c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop 13547c478bd9Sstevel@tonic-gate actually calls select. */ 1355*159d09a2SMark Phalan sel_state->end_time.tv_sec += delay_this_pass; 1356*159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1357*159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13587c478bd9Sstevel@tonic-gate if (e) 13597c478bd9Sstevel@tonic-gate break; 1360*159d09a2SMark Phalan if (sel_state->nfds == 0) 13617c478bd9Sstevel@tonic-gate break; 13627c478bd9Sstevel@tonic-gate delay_this_pass *= 2; 13637c478bd9Sstevel@tonic-gate } 13647c478bd9Sstevel@tonic-gate 1365*159d09a2SMark Phalan if (sel_state->nfds == 0) { 13667c478bd9Sstevel@tonic-gate /* No addresses? */ 13677c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 13687c478bd9Sstevel@tonic-gate goto egress; 13697c478bd9Sstevel@tonic-gate } 13707c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) { 13717c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 13727c478bd9Sstevel@tonic-gate goto egress; 13737c478bd9Sstevel@tonic-gate } 13747c478bd9Sstevel@tonic-gate /* Success! */ 13757c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf; 13767c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos 13777c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf); 13787c478bd9Sstevel@tonic-gate /*LINTED*/ 1379*159d09a2SMark Phalan dprint("returning %d bytes in buffer %p\n", 1380*159d09a2SMark Phalan (int) reply->length, reply->data); 13817c478bd9Sstevel@tonic-gate retval = 0; 13827c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0; 1383505d05c7Sgtb if (addr_used) 1384*159d09a2SMark Phalan *addr_used = winning_conn; 13857c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0) 1386*159d09a2SMark Phalan (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen); 1387*159d09a2SMark Phalan 1388*159d09a2SMark Phalan if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0) 1389*159d09a2SMark Phalan (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen); 1390*159d09a2SMark Phalan 13917c478bd9Sstevel@tonic-gate egress: 13927c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 13937c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET) 1394*159d09a2SMark Phalan closesocket(conns[i].fd); 13957c478bd9Sstevel@tonic-gate if (conns[i].state == READING 13967c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0 13977c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf) 13987c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf); 1399*159d09a2SMark Phalan if (callback_info) { 1400*159d09a2SMark Phalan callback_info->pfn_cleanup( callback_info->context, &callback_data[i]); 1401*159d09a2SMark Phalan } 14027c478bd9Sstevel@tonic-gate } 1403*159d09a2SMark Phalan 1404*159d09a2SMark Phalan if (callback_data) 1405*159d09a2SMark Phalan free(callback_data); 1406*159d09a2SMark Phalan 14077c478bd9Sstevel@tonic-gate free(conns); 14087c478bd9Sstevel@tonic-gate if (reply->data != udpbuf) 14097c478bd9Sstevel@tonic-gate free(udpbuf); 1410*159d09a2SMark Phalan free(sel_state); 14117c478bd9Sstevel@tonic-gate return retval; 14127c478bd9Sstevel@tonic-gate } 1413