17c478bd9Sstevel@tonic-gate /* 2*56bbb0b2SPeter Shoults * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 37c478bd9Sstevel@tonic-gate */ 47c478bd9Sstevel@tonic-gate /* 57c478bd9Sstevel@tonic-gate * lib/krb5/os/sendto_kdc.c 67c478bd9Sstevel@tonic-gate * 7159d09a2SMark Phalan * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology. 87c478bd9Sstevel@tonic-gate * All Rights Reserved. 97c478bd9Sstevel@tonic-gate * 107c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 117c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 127c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 137c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 147c478bd9Sstevel@tonic-gate * 157c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 167c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 177c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 187c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 197c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 207c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 217c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 227c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 237c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 247c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 257c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 267c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 277c478bd9Sstevel@tonic-gate * or implied warranty. 287c478bd9Sstevel@tonic-gate * 297c478bd9Sstevel@tonic-gate * 307c478bd9Sstevel@tonic-gate * Send packet to KDC for realm; wait for response, retransmitting 317c478bd9Sstevel@tonic-gate * as necessary. 327c478bd9Sstevel@tonic-gate */ 337c478bd9Sstevel@tonic-gate 34159d09a2SMark Phalan #include "fake-addrinfo.h" 35159d09a2SMark Phalan #include "k5-int.h" 36159d09a2SMark Phalan 37159d09a2SMark Phalan /* Solaris Kerberos */ 38159d09a2SMark Phalan #include <syslog.h> 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H 417c478bd9Sstevel@tonic-gate #include <sys/time.h> 427c478bd9Sstevel@tonic-gate #else 437c478bd9Sstevel@tonic-gate #include <time.h> 447c478bd9Sstevel@tonic-gate #endif 457c478bd9Sstevel@tonic-gate #include "os-proto.h" 46159d09a2SMark Phalan #ifdef _WIN32 47159d09a2SMark Phalan #include <sys/timeb.h> 48159d09a2SMark Phalan #endif 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate #ifdef _AIX 517c478bd9Sstevel@tonic-gate #include <sys/select.h> 527c478bd9Sstevel@tonic-gate #endif 537c478bd9Sstevel@tonic-gate 54159d09a2SMark Phalan #ifndef _WIN32 557c478bd9Sstevel@tonic-gate /* For FIONBIO. */ 567c478bd9Sstevel@tonic-gate #include <sys/ioctl.h> 577c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H 587c478bd9Sstevel@tonic-gate #include <sys/filio.h> 597c478bd9Sstevel@tonic-gate #endif 60159d09a2SMark Phalan #endif 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate #define MAX_PASS 3 637c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */ 647c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT 1465 */ 657c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */ 667c478bd9Sstevel@tonic-gate 67159d09a2SMark Phalan #undef DEBUG 68159d09a2SMark Phalan 69159d09a2SMark Phalan #ifdef DEBUG 70159d09a2SMark Phalan int krb5int_debug_sendto_kdc = 0; 71159d09a2SMark Phalan #define debug krb5int_debug_sendto_kdc 72159d09a2SMark Phalan 737c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len) 747c478bd9Sstevel@tonic-gate { 75159d09a2SMark Phalan #if 0 76159d09a2SMark Phalan FILE *logfile; 77159d09a2SMark Phalan logfile = fopen("/tmp/sendto_kdc.log", "a"); 78159d09a2SMark Phalan if (logfile == NULL) 79159d09a2SMark Phalan return; 80159d09a2SMark Phalan fwrite(data, 1, len, logfile); 81159d09a2SMark Phalan fclose(logfile); 82159d09a2SMark Phalan #else 837c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr); 847c478bd9Sstevel@tonic-gate /* stderr is unbuffered */ 85159d09a2SMark Phalan #endif 867c478bd9Sstevel@tonic-gate } 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler; 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate /* 917c478bd9Sstevel@tonic-gate * Solaris Kerberos: only including the debug stuff if DEBUG defined outside 927c478bd9Sstevel@tonic-gate * this file. 937c478bd9Sstevel@tonic-gate */ 947c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024]; 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */ 97159d09a2SMark Phalan #if 0 98159d09a2SMark Phalan static void put(const void *ptr, size_t len) 99159d09a2SMark Phalan { 100159d09a2SMark Phalan (*krb5int_sendtokdc_debug_handler)(ptr, len); 101159d09a2SMark Phalan } 102159d09a2SMark Phalan #endif 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate static void putstr(const char *str) 1057c478bd9Sstevel@tonic-gate { 1067c478bd9Sstevel@tonic-gate /* Solaris kerberos: build the string which will be passed to syslog later */ 1077c478bd9Sstevel@tonic-gate strlcat(global_err_str, str, sizeof (global_err_str)); 1087c478bd9Sstevel@tonic-gate } 109159d09a2SMark Phalan #else 110159d09a2SMark Phalan void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0; 111159d09a2SMark Phalan #endif 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint 114159d09a2SMark Phalan void 1157c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...) 1167c478bd9Sstevel@tonic-gate { 117159d09a2SMark Phalan #ifdef DEBUG 1187c478bd9Sstevel@tonic-gate va_list args; 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* Temporaries for variable arguments, etc. */ 1217c478bd9Sstevel@tonic-gate krb5_error_code kerr; 1227c478bd9Sstevel@tonic-gate int err; 1237c478bd9Sstevel@tonic-gate fd_set *rfds, *wfds, *xfds; 1247c478bd9Sstevel@tonic-gate int i; 1257c478bd9Sstevel@tonic-gate int maxfd; 1267c478bd9Sstevel@tonic-gate struct timeval *tv; 1277c478bd9Sstevel@tonic-gate struct addrinfo *ai; 1287c478bd9Sstevel@tonic-gate const krb5_data *d; 1297c478bd9Sstevel@tonic-gate char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 1307c478bd9Sstevel@tonic-gate const char *p; 131159d09a2SMark Phalan #ifndef max 132159d09a2SMark Phalan #define max(a,b) ((a) > (b) ? (a) : (b)) 133159d09a2SMark Phalan #endif 134159d09a2SMark Phalan char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)]; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate * Solaris kerberos: modified this function to create a string to pass to 1387c478bd9Sstevel@tonic-gate * syslog() 1397c478bd9Sstevel@tonic-gate */ 1407c478bd9Sstevel@tonic-gate global_err_str[0] = NULL; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate va_start(args, fmt); 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate #define putf(FMT,X) (sprintf(tmpbuf,FMT,X),putstr(tmpbuf)) 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate for (; *fmt; fmt++) { 1477c478bd9Sstevel@tonic-gate if (*fmt != '%') { 1487c478bd9Sstevel@tonic-gate /* Possible optimization: Look for % and print all chars 1497c478bd9Sstevel@tonic-gate up to it in one call. */ 1507c478bd9Sstevel@tonic-gate putf("%c", *fmt); 1517c478bd9Sstevel@tonic-gate continue; 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate /* After this, always processing a '%' sequence. */ 1547c478bd9Sstevel@tonic-gate fmt++; 1557c478bd9Sstevel@tonic-gate switch (*fmt) { 1567c478bd9Sstevel@tonic-gate case 0: 1577c478bd9Sstevel@tonic-gate default: 1587c478bd9Sstevel@tonic-gate abort(); 1597c478bd9Sstevel@tonic-gate case 'E': 1607c478bd9Sstevel@tonic-gate /* %E => krb5_error_code */ 1617c478bd9Sstevel@tonic-gate kerr = va_arg(args, krb5_error_code); 1627c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%lu/", (unsigned long) kerr); 1637c478bd9Sstevel@tonic-gate putstr(tmpbuf); 1647c478bd9Sstevel@tonic-gate p = error_message(kerr); 1657c478bd9Sstevel@tonic-gate putstr(p); 1667c478bd9Sstevel@tonic-gate break; 1677c478bd9Sstevel@tonic-gate case 'm': 1687c478bd9Sstevel@tonic-gate /* %m => errno value (int) */ 1697c478bd9Sstevel@tonic-gate /* Like syslog's %m except the errno value is passed in 1707c478bd9Sstevel@tonic-gate rather than the current value. */ 1717c478bd9Sstevel@tonic-gate err = va_arg(args, int); 1727c478bd9Sstevel@tonic-gate putf("%d/", err); 173159d09a2SMark Phalan p = NULL; 174159d09a2SMark Phalan #ifdef HAVE_STRERROR_R 175159d09a2SMark Phalan if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0) 176159d09a2SMark Phalan p = tmpbuf; 177159d09a2SMark Phalan #endif 178159d09a2SMark Phalan if (p == NULL) 179159d09a2SMark Phalan p = strerror(err); 1807c478bd9Sstevel@tonic-gate putstr(p); 1817c478bd9Sstevel@tonic-gate break; 1827c478bd9Sstevel@tonic-gate case 'F': 1837c478bd9Sstevel@tonic-gate /* %F => fd_set *, fd_set *, fd_set *, int */ 1847c478bd9Sstevel@tonic-gate rfds = va_arg(args, fd_set *); 1857c478bd9Sstevel@tonic-gate wfds = va_arg(args, fd_set *); 1867c478bd9Sstevel@tonic-gate xfds = va_arg(args, fd_set *); 1877c478bd9Sstevel@tonic-gate maxfd = va_arg(args, int); 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate for (i = 0; i < maxfd; i++) { 1907c478bd9Sstevel@tonic-gate int r = FD_ISSET(i, rfds); 1917c478bd9Sstevel@tonic-gate int w = wfds && FD_ISSET(i, wfds); 1927c478bd9Sstevel@tonic-gate int x = xfds && FD_ISSET(i, xfds); 1937c478bd9Sstevel@tonic-gate if (r || w || x) { 1947c478bd9Sstevel@tonic-gate putf(" %d", i); 1957c478bd9Sstevel@tonic-gate if (r) 1967c478bd9Sstevel@tonic-gate putstr("r"); 1977c478bd9Sstevel@tonic-gate if (w) 1987c478bd9Sstevel@tonic-gate putstr("w"); 1997c478bd9Sstevel@tonic-gate if (x) 2007c478bd9Sstevel@tonic-gate putstr("x"); 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate putstr(" "); 2047c478bd9Sstevel@tonic-gate break; 2057c478bd9Sstevel@tonic-gate case 's': 2067c478bd9Sstevel@tonic-gate /* %s => char * */ 2077c478bd9Sstevel@tonic-gate p = va_arg(args, const char *); 2087c478bd9Sstevel@tonic-gate putstr(p); 2097c478bd9Sstevel@tonic-gate break; 2107c478bd9Sstevel@tonic-gate case 't': 2117c478bd9Sstevel@tonic-gate /* %t => struct timeval * */ 2127c478bd9Sstevel@tonic-gate tv = va_arg(args, struct timeval *); 2137c478bd9Sstevel@tonic-gate if (tv) { 2147c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%ld.%06ld", 2157c478bd9Sstevel@tonic-gate (long) tv->tv_sec, (long) tv->tv_usec); 2167c478bd9Sstevel@tonic-gate putstr(tmpbuf); 2177c478bd9Sstevel@tonic-gate } else 2187c478bd9Sstevel@tonic-gate putstr("never"); 2197c478bd9Sstevel@tonic-gate break; 2207c478bd9Sstevel@tonic-gate case 'd': 2217c478bd9Sstevel@tonic-gate /* %d => int */ 2227c478bd9Sstevel@tonic-gate putf("%d", va_arg(args, int)); 2237c478bd9Sstevel@tonic-gate break; 2247c478bd9Sstevel@tonic-gate case 'p': 2257c478bd9Sstevel@tonic-gate /* %p => pointer */ 2267c478bd9Sstevel@tonic-gate putf("%p", va_arg(args, void*)); 2277c478bd9Sstevel@tonic-gate break; 2287c478bd9Sstevel@tonic-gate case 'A': 2297c478bd9Sstevel@tonic-gate /* %A => addrinfo */ 2307c478bd9Sstevel@tonic-gate ai = va_arg(args, struct addrinfo *); 231159d09a2SMark Phalan if (ai->ai_socktype == SOCK_DGRAM) 232159d09a2SMark Phalan strcpy(tmpbuf, "dgram"); 233159d09a2SMark Phalan else if (ai->ai_socktype == SOCK_STREAM) 234159d09a2SMark Phalan strcpy(tmpbuf, "stream"); 235159d09a2SMark Phalan else 236159d09a2SMark Phalan sprintf(tmpbuf, "socktype%d", ai->ai_socktype); 2377c478bd9Sstevel@tonic-gate if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen, 2387c478bd9Sstevel@tonic-gate addrbuf, sizeof (addrbuf), 2397c478bd9Sstevel@tonic-gate portbuf, sizeof (portbuf), 240159d09a2SMark Phalan NI_NUMERICHOST | NI_NUMERICSERV)) { 241159d09a2SMark Phalan if (ai->ai_addr->sa_family == AF_UNSPEC) 242159d09a2SMark Phalan strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC"); 243159d09a2SMark Phalan else 244159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family); 245159d09a2SMark Phalan } else 246159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " %s.%s", addrbuf, portbuf); 2477c478bd9Sstevel@tonic-gate putstr(tmpbuf); 2487c478bd9Sstevel@tonic-gate break; 2497c478bd9Sstevel@tonic-gate case 'D': 2507c478bd9Sstevel@tonic-gate /* %D => krb5_data * */ 2517c478bd9Sstevel@tonic-gate d = va_arg(args, krb5_data *); 252159d09a2SMark Phalan /* Solaris Kerberos */ 2537c478bd9Sstevel@tonic-gate p = d->data; 2547c478bd9Sstevel@tonic-gate putstr("0x"); 2557c478bd9Sstevel@tonic-gate for (i = 0; i < d->length; i++) { 2567c478bd9Sstevel@tonic-gate putf("%.2x", *p++); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate break; 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate va_end(args); 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate /* Solaris kerberos: use syslog() for debug output */ 2647c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, global_err_str); 265159d09a2SMark Phalan #endif 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate 268159d09a2SMark Phalan #define print_addrlist krb5int_print_addrlist 269159d09a2SMark Phalan static void 270159d09a2SMark Phalan print_addrlist (const struct addrlist *a) 271159d09a2SMark Phalan { 272159d09a2SMark Phalan int i; 273159d09a2SMark Phalan dprint("%d{", a->naddrs); 274159d09a2SMark Phalan for (i = 0; i < a->naddrs; i++) 275159d09a2SMark Phalan dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai); 276159d09a2SMark Phalan dprint("}"); 277159d09a2SMark Phalan } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate static int 2807c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src) 2817c478bd9Sstevel@tonic-gate { 282159d09a2SMark Phalan /* Wouldn't it be nice if we could filter out duplicates? The 283159d09a2SMark Phalan alloc/free handling makes that pretty difficult though. */ 2847c478bd9Sstevel@tonic-gate int err, i; 2857c478bd9Sstevel@tonic-gate 286159d09a2SMark Phalan /* Solaris Kerberos */ 2877c478bd9Sstevel@tonic-gate #ifdef DEBUG 2887c478bd9Sstevel@tonic-gate /*LINTED*/ 2897c478bd9Sstevel@tonic-gate dprint("merging addrlists:\n\tlist1: "); 2907c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 2917c478bd9Sstevel@tonic-gate /*LINTED*/ 292159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai); 2937c478bd9Sstevel@tonic-gate /*LINTED*/ 2947c478bd9Sstevel@tonic-gate dprint("\n\tlist2: "); 2957c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) 2967c478bd9Sstevel@tonic-gate /*LINTED*/ 297159d09a2SMark Phalan dprint(" %A", src->addrs[i].ai); 2987c478bd9Sstevel@tonic-gate /*LINTED*/ 2997c478bd9Sstevel@tonic-gate dprint("\n"); 3007c478bd9Sstevel@tonic-gate #endif 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate err = krb5int_grow_addrlist (dest, src->naddrs); 3037c478bd9Sstevel@tonic-gate if (err) 3047c478bd9Sstevel@tonic-gate return err; 3057c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) { 3067c478bd9Sstevel@tonic-gate dest->addrs[dest->naddrs + i] = src->addrs[i]; 307159d09a2SMark Phalan src->addrs[i].ai = 0; 308159d09a2SMark Phalan src->addrs[i].freefn = 0; 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate dest->naddrs += i; 3117c478bd9Sstevel@tonic-gate src->naddrs = 0; 3127c478bd9Sstevel@tonic-gate 313159d09a2SMark Phalan /* Solaris Kerberos */ 3147c478bd9Sstevel@tonic-gate #ifdef DEBUG 3157c478bd9Sstevel@tonic-gate /*LINTED*/ 3167c478bd9Sstevel@tonic-gate dprint("\tout: "); 3177c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 3187c478bd9Sstevel@tonic-gate /*LINTED*/ 319159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai); 3207c478bd9Sstevel@tonic-gate /*LINTED*/ 3217c478bd9Sstevel@tonic-gate dprint("\n"); 3227c478bd9Sstevel@tonic-gate #endif 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate return 0; 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate 327159d09a2SMark Phalan static int 328159d09a2SMark Phalan in_addrlist (struct addrinfo *thisaddr, struct addrlist *list) 329159d09a2SMark Phalan { 330159d09a2SMark Phalan int i; 331159d09a2SMark Phalan for (i = 0; i < list->naddrs; i++) { 332159d09a2SMark Phalan if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen 333159d09a2SMark Phalan && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr, 334159d09a2SMark Phalan thisaddr->ai_addrlen)) 335159d09a2SMark Phalan return 1; 336159d09a2SMark Phalan } 337159d09a2SMark Phalan return 0; 338159d09a2SMark Phalan } 339159d09a2SMark Phalan 340159d09a2SMark Phalan static int 341159d09a2SMark Phalan check_for_svc_unavailable (krb5_context context, 342159d09a2SMark Phalan const krb5_data *reply, 343159d09a2SMark Phalan void *msg_handler_data) 344159d09a2SMark Phalan { 345159d09a2SMark Phalan krb5_error_code *retval = (krb5_error_code *)msg_handler_data; 346159d09a2SMark Phalan 347159d09a2SMark Phalan *retval = 0; 348159d09a2SMark Phalan 349159d09a2SMark Phalan if (krb5_is_krb_error(reply)) { 350159d09a2SMark Phalan krb5_error *err_reply; 351159d09a2SMark Phalan 352159d09a2SMark Phalan if (decode_krb5_error(reply, &err_reply) == 0) { 353159d09a2SMark Phalan *retval = err_reply->error; 354159d09a2SMark Phalan krb5_free_error(context, err_reply); 355159d09a2SMark Phalan 356159d09a2SMark Phalan /* Returning 0 means continue to next KDC */ 357159d09a2SMark Phalan return (*retval != KDC_ERR_SVC_UNAVAILABLE); 358159d09a2SMark Phalan } 359159d09a2SMark Phalan } 360159d09a2SMark Phalan 361159d09a2SMark Phalan return 1; 362159d09a2SMark Phalan } 363159d09a2SMark Phalan 3647c478bd9Sstevel@tonic-gate /* 3657c478bd9Sstevel@tonic-gate * send the formatted request 'message' to a KDC for realm 'realm' and 3667c478bd9Sstevel@tonic-gate * return the response (if any) in 'reply'. 3677c478bd9Sstevel@tonic-gate * 3687c478bd9Sstevel@tonic-gate * If the message is sent and a response is received, 0 is returned, 3697c478bd9Sstevel@tonic-gate * otherwise an error code is returned. 3707c478bd9Sstevel@tonic-gate * 3717c478bd9Sstevel@tonic-gate * The storage for 'reply' is allocated and should be freed by the caller 3727c478bd9Sstevel@tonic-gate * when finished. 3737c478bd9Sstevel@tonic-gate */ 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate krb5_error_code 3767c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message, 3777c478bd9Sstevel@tonic-gate const krb5_data *realm, krb5_data *reply, 378505d05c7Sgtb int *use_master, int tcp_only) 3797c478bd9Sstevel@tonic-gate { 380159d09a2SMark Phalan krb5_error_code retval, retval2; 381*56bbb0b2SPeter Shoults struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */ 382505d05c7Sgtb int socktype1 = 0, socktype2 = 0, addr_used; 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate /* 3857c478bd9Sstevel@tonic-gate * find KDC location(s) for realm 3867c478bd9Sstevel@tonic-gate */ 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate /* 3897c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem, 3907c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be 3917c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or 3927c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also, 3937c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} 3947c478bd9Sstevel@tonic-gate * should probably be returned as well. 3957c478bd9Sstevel@tonic-gate */ 3967c478bd9Sstevel@tonic-gate 3977c478bd9Sstevel@tonic-gate /*LINTED*/ 3987c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", 3997c478bd9Sstevel@tonic-gate /*LINTED*/ 400505d05c7Sgtb message->length, message->data, realm, *use_master, tcp_only); 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) { 4037c478bd9Sstevel@tonic-gate int tmp; 4047c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile, 4057c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0, 4067c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp); 4077c478bd9Sstevel@tonic-gate if (retval) 4087c478bd9Sstevel@tonic-gate return retval; 4097c478bd9Sstevel@tonic-gate if (tmp < 0) 4107c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT; 41156a424ccSmp else if (tmp > HARD_UDP_LIMIT) 4127c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is 4137c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can 4147c478bd9Sstevel@tonic-gate support. */ 4157c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT; 4167c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp; 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate 419505d05c7Sgtb retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate if (tcp_only) 4227c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0; 4237c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit) 4247c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; 4257c478bd9Sstevel@tonic-gate else 4267c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; 4277c478bd9Sstevel@tonic-gate 428505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); 4297c478bd9Sstevel@tonic-gate if (socktype2) { 4307c478bd9Sstevel@tonic-gate struct addrlist addrs2; 4317c478bd9Sstevel@tonic-gate 432159d09a2SMark Phalan retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master, 433159d09a2SMark Phalan socktype2, 0); 434159d09a2SMark Phalan #if 0 435159d09a2SMark Phalan if (retval2 == 0) { 436159d09a2SMark Phalan (void) merge_addrlists(&addrs, &addrs2); 437159d09a2SMark Phalan krb5int_free_addrlist(&addrs2); 438159d09a2SMark Phalan retval = 0; 439159d09a2SMark Phalan } else if (retval == KRB5_REALM_CANT_RESOLVE) { 440159d09a2SMark Phalan retval = retval2; 441159d09a2SMark Phalan } 442159d09a2SMark Phalan #else 443159d09a2SMark Phalan retval = retval2; 4447c478bd9Sstevel@tonic-gate if (retval == 0) { 4457c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2); 4467c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2); 4477c478bd9Sstevel@tonic-gate } 448159d09a2SMark Phalan #endif 4497c478bd9Sstevel@tonic-gate } 450159d09a2SMark Phalan 4517c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) { 452159d09a2SMark Phalan krb5_error_code err = 0; 453159d09a2SMark Phalan 454159d09a2SMark Phalan retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, 455159d09a2SMark Phalan 0, 0, &addr_used, check_for_svc_unavailable, &err); 456159d09a2SMark Phalan switch (retval) { 457159d09a2SMark Phalan case 0: 458505d05c7Sgtb /* 459159d09a2SMark Phalan * Set use_master to 1 if we ended up talking to a master when 460159d09a2SMark Phalan * we didn't explicitly request to 461159d09a2SMark Phalan */ 462159d09a2SMark Phalan if (*use_master == 0) { 463159d09a2SMark Phalan struct addrlist addrs3; 464159d09a2SMark Phalan retval = krb5_locate_kdc(context, realm, &addrs3, 1, 465159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_socktype, 466159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_family); 467159d09a2SMark Phalan if (retval == 0) { 468159d09a2SMark Phalan if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3)) 469159d09a2SMark Phalan *use_master = 1; 470159d09a2SMark Phalan krb5int_free_addrlist (&addrs3); 471159d09a2SMark Phalan } 472159d09a2SMark Phalan } 473159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 474159d09a2SMark Phalan return 0; 475159d09a2SMark Phalan default: 476159d09a2SMark Phalan break; 477159d09a2SMark Phalan /* Cases here are for constructing useful error messages. */ 478159d09a2SMark Phalan case KRB5_KDC_UNREACH: 479159d09a2SMark Phalan if (err == KDC_ERR_SVC_UNAVAILABLE) { 480159d09a2SMark Phalan retval = KRB5KDC_ERR_SVC_UNAVAILABLE; 481159d09a2SMark Phalan } else { 482159d09a2SMark Phalan krb5_set_error_message(context, retval, 483159d09a2SMark Phalan "Cannot contact any KDC for realm '%.*s'", 484159d09a2SMark Phalan realm->length, realm->data); 485505d05c7Sgtb } 486159d09a2SMark Phalan break; 487505d05c7Sgtb } 488159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate return retval; 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 493159d09a2SMark Phalan #ifdef DEBUG 494159d09a2SMark Phalan 495159d09a2SMark Phalan #ifdef _WIN32 496159d09a2SMark Phalan #define dperror(MSG) \ 497159d09a2SMark Phalan dprint("%s: an error occurred ... " \ 498159d09a2SMark Phalan "\tline=%d errno=%m socketerrno=%m\n", \ 499159d09a2SMark Phalan (MSG), __LINE__, errno, SOCKET_ERRNO) 500159d09a2SMark Phalan #else 501159d09a2SMark Phalan #define dperror(MSG) dprint("%s: %m\n", MSG, errno) 502159d09a2SMark Phalan #endif 503159d09a2SMark Phalan #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0) 504159d09a2SMark Phalan 505159d09a2SMark Phalan #else /* ! DEBUG */ 506159d09a2SMark Phalan 507159d09a2SMark Phalan #define dperror(MSG) ((void)(MSG)) 508159d09a2SMark Phalan #define dfprintf(ARGLIST) ((void)0) 509159d09a2SMark Phalan 510159d09a2SMark Phalan #endif 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate /* 5137c478bd9Sstevel@tonic-gate * Notes: 5147c478bd9Sstevel@tonic-gate * 5157c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes 5167c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up 5177c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags 5187c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it 5197c478bd9Sstevel@tonic-gate * system-specific. 5207c478bd9Sstevel@tonic-gate * 5217c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually 5227c478bd9Sstevel@tonic-gate * fix the UDP code to do the same. 5237c478bd9Sstevel@tonic-gate * 5247c478bd9Sstevel@tonic-gate * To do: 5257c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options? 5267c478bd9Sstevel@tonic-gate * - error codes that don't suck 5277c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status 5287c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP 5297c478bd9Sstevel@tonic-gate * connections already in progress 5307c478bd9Sstevel@tonic-gate */ 5317c478bd9Sstevel@tonic-gate 532159d09a2SMark Phalan #include "cm.h" 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp) 5357c478bd9Sstevel@tonic-gate { 536159d09a2SMark Phalan #ifdef _WIN32 537159d09a2SMark Phalan struct _timeb tb; 538159d09a2SMark Phalan _ftime(&tb); 539159d09a2SMark Phalan tvp->tv_sec = tb.time; 540159d09a2SMark Phalan tvp->tv_usec = tb.millitm * 1000; 541159d09a2SMark Phalan /* Can _ftime fail? */ 542159d09a2SMark Phalan return 0; 543159d09a2SMark Phalan #else 5447c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) { 5457c478bd9Sstevel@tonic-gate dperror("gettimeofday"); 5467c478bd9Sstevel@tonic-gate return errno; 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate return 0; 549159d09a2SMark Phalan #endif 5507c478bd9Sstevel@tonic-gate } 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate /* 5537c478bd9Sstevel@tonic-gate * Call select and return results. 5547c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout 5557c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets 5567c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code. 5577c478bd9Sstevel@tonic-gate */ 5587c478bd9Sstevel@tonic-gate krb5_error_code 5597c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in, 5607c478bd9Sstevel@tonic-gate struct select_state *out, int *sret) 5617c478bd9Sstevel@tonic-gate { 5627c478bd9Sstevel@tonic-gate struct timeval now, *timo; 5637c478bd9Sstevel@tonic-gate krb5_error_code e; 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate *out = *in; 5667c478bd9Sstevel@tonic-gate e = getcurtime(&now); 5677c478bd9Sstevel@tonic-gate if (e) 5687c478bd9Sstevel@tonic-gate return e; 5697c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0) 5707c478bd9Sstevel@tonic-gate timo = 0; 5717c478bd9Sstevel@tonic-gate else { 5727c478bd9Sstevel@tonic-gate timo = &out->end_time; 5737c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec; 5747c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec; 5757c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) { 5767c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000; 5777c478bd9Sstevel@tonic-gate out->end_time.tv_sec--; 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) { 5807c478bd9Sstevel@tonic-gate *sret = 0; 5817c478bd9Sstevel@tonic-gate return 0; 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate /*LINTED*/ 5857c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n", 5867c478bd9Sstevel@tonic-gate /*LINTED*/ 587159d09a2SMark Phalan out->max, 588159d09a2SMark Phalan &out->rfds, &out->wfds, &out->xfds, out->max, 589159d09a2SMark Phalan timo); 5907c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo); 5917c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 5927c478bd9Sstevel@tonic-gate 593159d09a2SMark Phalan /* Solaris Kerberos */ 5947c478bd9Sstevel@tonic-gate #ifdef DEBUG 5957c478bd9Sstevel@tonic-gate /*LINTED*/ 5967c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret); 5977c478bd9Sstevel@tonic-gate if (*sret < 0) 5987c478bd9Sstevel@tonic-gate /*LINTED*/ 5997c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e); 6007c478bd9Sstevel@tonic-gate else if (*sret == 0) 6017c478bd9Sstevel@tonic-gate /*LINTED*/ 6027c478bd9Sstevel@tonic-gate dprint(" (timeout)\n"); 6037c478bd9Sstevel@tonic-gate else 6047c478bd9Sstevel@tonic-gate /*LINTED*/ 6057c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max); 6067c478bd9Sstevel@tonic-gate #endif 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate if (*sret < 0) 6097c478bd9Sstevel@tonic-gate return e; 6107c478bd9Sstevel@tonic-gate return 0; 6117c478bd9Sstevel@tonic-gate } 6127c478bd9Sstevel@tonic-gate 6137c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn, 6147c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6157c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn, 6167c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6177c478bd9Sstevel@tonic-gate 618159d09a2SMark Phalan static void 619159d09a2SMark Phalan set_conn_state_msg_length (struct conn_state *state, const krb5_data *message) 620159d09a2SMark Phalan { 621159d09a2SMark Phalan if (!message || message->length == 0) 622159d09a2SMark Phalan return; 623159d09a2SMark Phalan 624159d09a2SMark Phalan if (!state->is_udp) { 625159d09a2SMark Phalan 626159d09a2SMark Phalan state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff; 627159d09a2SMark Phalan state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff; 628159d09a2SMark Phalan state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff; 629159d09a2SMark Phalan state->x.out.msg_len_buf[3] = message->length & 0xff; 630159d09a2SMark Phalan 631159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4); 632159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 633159d09a2SMark Phalan state->x.out.sg_count = 2; 634159d09a2SMark Phalan 635159d09a2SMark Phalan } else { 636159d09a2SMark Phalan 637159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 638159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], 0, 0); 639159d09a2SMark Phalan state->x.out.sg_count = 1; 640159d09a2SMark Phalan 641159d09a2SMark Phalan } 642159d09a2SMark Phalan } 643159d09a2SMark Phalan 644159d09a2SMark Phalan 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate static int 6477c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai, 648159d09a2SMark Phalan const krb5_data *message, char **udpbufp) 6497c478bd9Sstevel@tonic-gate { 6507c478bd9Sstevel@tonic-gate state->state = INITIALIZING; 6517c478bd9Sstevel@tonic-gate state->err = 0; 6527c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf; 6537c478bd9Sstevel@tonic-gate state->addr = ai; 6547c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6557c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 6567c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 657159d09a2SMark Phalan /* 6587c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4); 6597c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 6607c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2; 661159d09a2SMark Phalan */ 662159d09a2SMark Phalan 6637c478bd9Sstevel@tonic-gate state->is_udp = 0; 6647c478bd9Sstevel@tonic-gate state->service = service_tcp_fd; 665159d09a2SMark Phalan set_conn_state_msg_length (state, message); 6667c478bd9Sstevel@tonic-gate } else { 667159d09a2SMark Phalan /* 6687c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 6697c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 6707c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1; 671159d09a2SMark Phalan */ 672159d09a2SMark Phalan 6737c478bd9Sstevel@tonic-gate state->is_udp = 1; 6747c478bd9Sstevel@tonic-gate state->service = service_udp_fd; 675159d09a2SMark Phalan set_conn_state_msg_length (state, message); 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 6787c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size); 6797c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 6807c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)"); 6817c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 6827c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6837c478bd9Sstevel@tonic-gate state->state = FAILED; 6847c478bd9Sstevel@tonic-gate return 1; 6857c478bd9Sstevel@tonic-gate } 6867c478bd9Sstevel@tonic-gate } 6877c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp; 6887c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size; 6897c478bd9Sstevel@tonic-gate } 6907c478bd9Sstevel@tonic-gate return 0; 6917c478bd9Sstevel@tonic-gate } 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate static int 694159d09a2SMark Phalan start_connection (struct conn_state *state, 695159d09a2SMark Phalan struct select_state *selstate, 696159d09a2SMark Phalan struct sendto_callback_info* callback_info, 697159d09a2SMark Phalan krb5_data* callback_buffer) 6987c478bd9Sstevel@tonic-gate { 6997c478bd9Sstevel@tonic-gate int fd, e; 7007c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr; 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate /*LINTED*/ 7037c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, 7047c478bd9Sstevel@tonic-gate /*LINTED*/ 7057c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family); 7067c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0); 7077c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) { 7087c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7097c478bd9Sstevel@tonic-gate /*LINTED*/ 7107c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family); 7117c478bd9Sstevel@tonic-gate return -1; /* try other hosts */ 7127c478bd9Sstevel@tonic-gate } 7137c478bd9Sstevel@tonic-gate /* Make it non-blocking. */ 7147c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 7157c478bd9Sstevel@tonic-gate static const int one = 1; 7167c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 }; 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one)) 7197c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)"); 7207c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) 7217c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)"); 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */ 7257c478bd9Sstevel@tonic-gate /*LINTED*/ 7267c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai); 7277c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen); 7287c478bd9Sstevel@tonic-gate if (e != 0) { 7297c478bd9Sstevel@tonic-gate /* 7307c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking 7317c478bd9Sstevel@tonic-gate * connections. 7327c478bd9Sstevel@tonic-gate */ 7337c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { 7347c478bd9Sstevel@tonic-gate state->state = CONNECTING; 735159d09a2SMark Phalan state->fd = fd; 7367c478bd9Sstevel@tonic-gate } else { 7377c478bd9Sstevel@tonic-gate /*LINTED*/ 7387c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO); 739159d09a2SMark Phalan (void) closesocket(fd); 7407c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7417c478bd9Sstevel@tonic-gate state->state = FAILED; 7427c478bd9Sstevel@tonic-gate return -2; 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate } else { 7457c478bd9Sstevel@tonic-gate /* 7467c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it 7477c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before 7487c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network 7497c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it. 7507c478bd9Sstevel@tonic-gate */ 7517c478bd9Sstevel@tonic-gate state->state = WRITING; 752159d09a2SMark Phalan state->fd = fd; 7537c478bd9Sstevel@tonic-gate } 7547c478bd9Sstevel@tonic-gate /*LINTED*/ 7557c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]); 7567c478bd9Sstevel@tonic-gate 757159d09a2SMark Phalan 758159d09a2SMark Phalan /* 759159d09a2SMark Phalan * Here's where KPASSWD callback gets the socket information it needs for 760159d09a2SMark Phalan * a kpasswd request 761159d09a2SMark Phalan */ 762159d09a2SMark Phalan if (callback_info) { 763159d09a2SMark Phalan 764159d09a2SMark Phalan e = callback_info->pfn_callback(state, 765159d09a2SMark Phalan callback_info->context, 766159d09a2SMark Phalan callback_buffer); 767159d09a2SMark Phalan if (e != 0) { 768159d09a2SMark Phalan dprint("callback failed: %m\n", e); 769159d09a2SMark Phalan (void) closesocket(fd); 770159d09a2SMark Phalan state->err = e; 771159d09a2SMark Phalan state->fd = INVALID_SOCKET; 772159d09a2SMark Phalan state->state = FAILED; 773159d09a2SMark Phalan return -3; 774159d09a2SMark Phalan } 775159d09a2SMark Phalan 776159d09a2SMark Phalan dprint("callback %p (message=%d@%p)\n", 777159d09a2SMark Phalan state, 778159d09a2SMark Phalan callback_buffer->length, 779159d09a2SMark Phalan callback_buffer->data); 780159d09a2SMark Phalan 781159d09a2SMark Phalan set_conn_state_msg_length( state, callback_buffer ); 782159d09a2SMark Phalan } 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) { 7857c478bd9Sstevel@tonic-gate /* Send it now. */ 7867c478bd9Sstevel@tonic-gate int ret; 7877c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0]; 7887c478bd9Sstevel@tonic-gate 7897c478bd9Sstevel@tonic-gate /*LINTED*/ 7907c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); 7917c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); 7927c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) { 7937c478bd9Sstevel@tonic-gate dperror("sendto"); 7947c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 7957c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 7967c478bd9Sstevel@tonic-gate state->state = FAILED; 797159d09a2SMark Phalan return -4; 7987c478bd9Sstevel@tonic-gate } else { 7997c478bd9Sstevel@tonic-gate state->state = READING; 8007c478bd9Sstevel@tonic-gate } 8017c478bd9Sstevel@tonic-gate } 802159d09a2SMark Phalan #ifdef DEBUG 803159d09a2SMark Phalan if (debug) { 804159d09a2SMark Phalan struct sockaddr_storage ss; 805159d09a2SMark Phalan socklen_t sslen = sizeof(ss); 806159d09a2SMark Phalan if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) { 807159d09a2SMark Phalan struct addrinfo hack_ai; 808159d09a2SMark Phalan memset(&hack_ai, 0, sizeof(hack_ai)); 809159d09a2SMark Phalan hack_ai.ai_addr = (struct sockaddr *) &ss; 810159d09a2SMark Phalan hack_ai.ai_addrlen = sslen; 811159d09a2SMark Phalan hack_ai.ai_socktype = SOCK_DGRAM; 812159d09a2SMark Phalan hack_ai.ai_family = ai->ai_family; 813159d09a2SMark Phalan dprint("local socket address is %A\n", &hack_ai); 814159d09a2SMark Phalan } 815159d09a2SMark Phalan } 816159d09a2SMark Phalan #endif 8177c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds); 8187c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING) 8197c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds); 8207c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds); 8217c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd) 8227c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1; 8237c478bd9Sstevel@tonic-gate selstate->nfds++; 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate /*LINTED*/ 8267c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n", 8277c478bd9Sstevel@tonic-gate /*LINTED*/ 8287c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); 8297c478bd9Sstevel@tonic-gate 8307c478bd9Sstevel@tonic-gate return 0; 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise. 8347c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response. 8357c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the 8367c478bd9Sstevel@tonic-gate next connection. */ 8377c478bd9Sstevel@tonic-gate static int 838159d09a2SMark Phalan maybe_send (struct conn_state *conn, 839159d09a2SMark Phalan struct select_state *selstate, 840159d09a2SMark Phalan struct sendto_callback_info* callback_info, 841159d09a2SMark Phalan krb5_data* callback_buffer) 8427c478bd9Sstevel@tonic-gate { 8437c478bd9Sstevel@tonic-gate sg_buf *sg; 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate /*LINTED*/ 8467c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn, 8477c478bd9Sstevel@tonic-gate /*LINTED*/ 848159d09a2SMark Phalan state_strings[conn->state], 849159d09a2SMark Phalan conn->is_udp ? "udp" : "tcp"); 8507c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING) 851159d09a2SMark Phalan return start_connection(conn, selstate, callback_info, callback_buffer); 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */ 8547c478bd9Sstevel@tonic-gate if (conn->state == FAILED) { 8557c478bd9Sstevel@tonic-gate dprint("connection already closed\n"); 8567c478bd9Sstevel@tonic-gate return -1; 8577c478bd9Sstevel@tonic-gate } 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) { 8607c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n"); 8617c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we 8627c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */ 8637c478bd9Sstevel@tonic-gate return -1; 8647c478bd9Sstevel@tonic-gate } 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a 8677c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */ 8687c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0]; 8697c478bd9Sstevel@tonic-gate /*LINTED*/ 8707c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd); 8717c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) { 8727c478bd9Sstevel@tonic-gate dperror("send"); 8737c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass. 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the 8767c478bd9Sstevel@tonic-gate select callbacks? */ 8777c478bd9Sstevel@tonic-gate return -1; 8787c478bd9Sstevel@tonic-gate } 8797c478bd9Sstevel@tonic-gate /* Yay, it worked. */ 8807c478bd9Sstevel@tonic-gate return 0; 8817c478bd9Sstevel@tonic-gate } 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate static void 8847c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err) 8857c478bd9Sstevel@tonic-gate { 8867c478bd9Sstevel@tonic-gate conn->state = FAILED; 8877c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH); 8887c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds); 8897c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 8907c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds); 8917c478bd9Sstevel@tonic-gate conn->err = err; 8927c478bd9Sstevel@tonic-gate /*LINTED*/ 8937c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err); 8947c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */ 8957c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) { 8967c478bd9Sstevel@tonic-gate while (selstate->max > 0 8977c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds) 8987c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds) 8997c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds)) 9007c478bd9Sstevel@tonic-gate selstate->max--; 9017c478bd9Sstevel@tonic-gate /*LINTED*/ 9027c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max); 9037c478bd9Sstevel@tonic-gate } 9047c478bd9Sstevel@tonic-gate selstate->nfds--; 9057c478bd9Sstevel@tonic-gate } 9067c478bd9Sstevel@tonic-gate 907159d09a2SMark Phalan /* Check socket for error. */ 908159d09a2SMark Phalan static int 909159d09a2SMark Phalan get_so_error(int fd) 910159d09a2SMark Phalan { 911159d09a2SMark Phalan int e, sockerr; 912159d09a2SMark Phalan socklen_t sockerrlen; 913159d09a2SMark Phalan 914159d09a2SMark Phalan sockerr = 0; 915159d09a2SMark Phalan sockerrlen = sizeof(sockerr); 916159d09a2SMark Phalan e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen); 917159d09a2SMark Phalan if (e != 0) { 918159d09a2SMark Phalan /* What to do now? */ 919159d09a2SMark Phalan e = SOCKET_ERRNO; 920159d09a2SMark Phalan dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e); 921159d09a2SMark Phalan return e; 922159d09a2SMark Phalan } 923159d09a2SMark Phalan return sockerr; 924159d09a2SMark Phalan } 925159d09a2SMark Phalan 9267c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit 9277c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message, 9287c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */ 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate static int 9317c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate, 9327c478bd9Sstevel@tonic-gate int ssflags) 9337c478bd9Sstevel@tonic-gate { 9347c478bd9Sstevel@tonic-gate krb5_error_code e = 0; 9357c478bd9Sstevel@tonic-gate int nwritten, nread; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) 9387c478bd9Sstevel@tonic-gate abort(); 9397c478bd9Sstevel@tonic-gate switch (conn->state) { 9407c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp; 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate case CONNECTING: 9437c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 9447c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */ 9457c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */; 9467c478bd9Sstevel@tonic-gate kill_conn: 9477c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e); 9487c478bd9Sstevel@tonic-gate if (e == EINVAL) { 9497c478bd9Sstevel@tonic-gate closesocket(conn->fd); 9507c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET; 9517c478bd9Sstevel@tonic-gate } 9527c478bd9Sstevel@tonic-gate return e == 0; 9537c478bd9Sstevel@tonic-gate } 9547c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 9557c478bd9Sstevel@tonic-gate handle_exception: 956159d09a2SMark Phalan e = get_so_error(conn->fd); 957159d09a2SMark Phalan if (e) 958159d09a2SMark Phalan dprint("socket error on exception fd: %m", e); 959159d09a2SMark Phalan else 960159d09a2SMark Phalan dprint("no socket error info available on exception fd"); 9617c478bd9Sstevel@tonic-gate goto kill_conn; 9627c478bd9Sstevel@tonic-gate } 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate /* 9657c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail? 9667c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed. 967159d09a2SMark Phalan * Call getsockopt to see if error pending. 968159d09a2SMark Phalan * 969159d09a2SMark Phalan * (For most UNIX systems it works to just try writing the 970159d09a2SMark Phalan * first time and detect an error. But Bill Dodd at IBM 971159d09a2SMark Phalan * reports that some version of AIX, SIGPIPE can result.) 9727c478bd9Sstevel@tonic-gate */ 973159d09a2SMark Phalan e = get_so_error(conn->fd); 974159d09a2SMark Phalan if (e) { 975159d09a2SMark Phalan dprint("socket error on write fd: %m", e); 976159d09a2SMark Phalan goto kill_conn; 977159d09a2SMark Phalan } 9787c478bd9Sstevel@tonic-gate conn->state = WRITING; 9797c478bd9Sstevel@tonic-gate goto try_writing; 9807c478bd9Sstevel@tonic-gate 9817c478bd9Sstevel@tonic-gate case WRITING: 9827c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 9837c478bd9Sstevel@tonic-gate e = E2BIG; 9847c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */ 9857c478bd9Sstevel@tonic-gate goto kill_conn; 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) 9887c478bd9Sstevel@tonic-gate goto handle_exception; 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate try_writing: 9917c478bd9Sstevel@tonic-gate /*LINTED*/ 9927c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n", 9937c478bd9Sstevel@tonic-gate /*LINTED*/ 9947c478bd9Sstevel@tonic-gate conn->x.out.sg_count, 9957c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) 9967c478bd9Sstevel@tonic-gate /*LINTED*/ 9977c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])), 9987c478bd9Sstevel@tonic-gate conn->fd); 9997c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, 10007c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp); 10017c478bd9Sstevel@tonic-gate if (nwritten < 0) { 10027c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 10037c478bd9Sstevel@tonic-gate /*LINTED*/ 10047c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e); 10057c478bd9Sstevel@tonic-gate goto kill_conn; 10067c478bd9Sstevel@tonic-gate } 10077c478bd9Sstevel@tonic-gate /*LINTED*/ 10087c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten); 10097c478bd9Sstevel@tonic-gate while (nwritten) { 10107c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp; 10117c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) { 10127c478bd9Sstevel@tonic-gate /*LINTED*/ 10137c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten); 10147c478bd9Sstevel@tonic-gate nwritten = 0; 10157c478bd9Sstevel@tonic-gate } else { 10167c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp); 10177c478bd9Sstevel@tonic-gate conn->x.out.sgp++; 10187c478bd9Sstevel@tonic-gate conn->x.out.sg_count--; 10197c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0) 10207c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */ 10217c478bd9Sstevel@tonic-gate abort(); 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate } 10247c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) { 10257c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */ 10267c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because 10277c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/ 10287c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 10297c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data 10307c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode? 10317c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */ 10327c478bd9Sstevel@tonic-gate /*LINTED*/ 10337c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd); 10347c478bd9Sstevel@tonic-gate conn->state = READING; 10357c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0; 10367c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0; 10377c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10387c478bd9Sstevel@tonic-gate conn->x.in.pos = 0; 10397c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0; 10407c478bd9Sstevel@tonic-gate } 10417c478bd9Sstevel@tonic-gate return 0; 10427c478bd9Sstevel@tonic-gate 10437c478bd9Sstevel@tonic-gate case READING: 10447c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 10457c478bd9Sstevel@tonic-gate if (conn->x.in.buf) { 10467c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10477c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10487c478bd9Sstevel@tonic-gate } 10497c478bd9Sstevel@tonic-gate goto handle_exception; 10507c478bd9Sstevel@tonic-gate } 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 10537c478bd9Sstevel@tonic-gate /* Reading data. */ 10547c478bd9Sstevel@tonic-gate /*LINTED*/ 10557c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n", 10567c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd); 10577c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); 10587c478bd9Sstevel@tonic-gate if (nread <= 0) { 10597c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET; 10607c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10617c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10627c478bd9Sstevel@tonic-gate goto kill_conn; 10637c478bd9Sstevel@tonic-gate } 10647c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread; 10657c478bd9Sstevel@tonic-gate conn->x.in.pos += nread; 1066159d09a2SMark Phalan /* Solaris Kerberos */ 10677c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) { 10687c478bd9Sstevel@tonic-gate /* We win! */ 10697c478bd9Sstevel@tonic-gate return 1; 10707c478bd9Sstevel@tonic-gate } 10717c478bd9Sstevel@tonic-gate } else { 10727c478bd9Sstevel@tonic-gate /* Reading length. */ 10737c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, 10747c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 10757c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read); 10767c478bd9Sstevel@tonic-gate if (nread < 0) { 10777c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 10787c478bd9Sstevel@tonic-gate goto kill_conn; 10797c478bd9Sstevel@tonic-gate } 10807c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread; 10817c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 10827c478bd9Sstevel@tonic-gate unsigned long len; 10837c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0]; 10847c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1]; 10857c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2]; 10867c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3]; 10877c478bd9Sstevel@tonic-gate /*LINTED*/ 10887c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len); 10897c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */ 10907c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) { 10917c478bd9Sstevel@tonic-gate e = E2BIG; 10927c478bd9Sstevel@tonic-gate goto kill_conn; 10937c478bd9Sstevel@tonic-gate } 10947c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len; 10957c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len); 10967c478bd9Sstevel@tonic-gate /*LINTED*/ 10977c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len, 10987c478bd9Sstevel@tonic-gate conn->x.in.buf); 10997c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) { 11007c478bd9Sstevel@tonic-gate /* allocation failure */ 11017c478bd9Sstevel@tonic-gate e = errno; 11027c478bd9Sstevel@tonic-gate goto kill_conn; 11037c478bd9Sstevel@tonic-gate } 11047c478bd9Sstevel@tonic-gate } 11057c478bd9Sstevel@tonic-gate } 11067c478bd9Sstevel@tonic-gate break; 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate default: 11097c478bd9Sstevel@tonic-gate abort(); 11107c478bd9Sstevel@tonic-gate } 11117c478bd9Sstevel@tonic-gate return 0; 11127c478bd9Sstevel@tonic-gate } 11137c478bd9Sstevel@tonic-gate 11147c478bd9Sstevel@tonic-gate static int 11157c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate, 11167c478bd9Sstevel@tonic-gate int ssflags) 11177c478bd9Sstevel@tonic-gate { 11187c478bd9Sstevel@tonic-gate int nread; 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) 11217c478bd9Sstevel@tonic-gate abort(); 11227c478bd9Sstevel@tonic-gate if (conn->state != READING) 11237c478bd9Sstevel@tonic-gate abort(); 11247c478bd9Sstevel@tonic-gate 11257c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); 11267c478bd9Sstevel@tonic-gate if (nread < 0) { 11277c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO); 11287c478bd9Sstevel@tonic-gate return 0; 11297c478bd9Sstevel@tonic-gate } 11307c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread; 11317c478bd9Sstevel@tonic-gate return 1; 11327c478bd9Sstevel@tonic-gate } 11337c478bd9Sstevel@tonic-gate 11347c478bd9Sstevel@tonic-gate static int 1135159d09a2SMark Phalan service_fds (krb5_context context, 1136159d09a2SMark Phalan struct select_state *selstate, 1137159d09a2SMark Phalan struct conn_state *conns, size_t n_conns, int *winning_conn, 1138159d09a2SMark Phalan struct select_state *seltemp, 1139159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1140159d09a2SMark Phalan void *msg_handler_data) 11417c478bd9Sstevel@tonic-gate { 11427c478bd9Sstevel@tonic-gate int e, selret; 11437c478bd9Sstevel@tonic-gate 11447c478bd9Sstevel@tonic-gate e = 0; 11457c478bd9Sstevel@tonic-gate while (selstate->nfds > 0 1146159d09a2SMark Phalan && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) { 11477c478bd9Sstevel@tonic-gate int i; 11487c478bd9Sstevel@tonic-gate 11497c478bd9Sstevel@tonic-gate /*LINTED*/ 11507c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret); 11517c478bd9Sstevel@tonic-gate 11527c478bd9Sstevel@tonic-gate if (selret == 0) 11537c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */ 11547c478bd9Sstevel@tonic-gate return 0; 11557c478bd9Sstevel@tonic-gate 11567c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */ 11577c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) { 11587c478bd9Sstevel@tonic-gate int ssflags; 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET) 11617c478bd9Sstevel@tonic-gate continue; 11627c478bd9Sstevel@tonic-gate ssflags = 0; 1163159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->rfds)) 11647c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--; 1165159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->wfds)) 11667c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--; 1167159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->xfds)) 11687c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--; 11697c478bd9Sstevel@tonic-gate if (!ssflags) 11707c478bd9Sstevel@tonic-gate continue; 11717c478bd9Sstevel@tonic-gate 11727c478bd9Sstevel@tonic-gate /*LINTED*/ 11737c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n", 11747c478bd9Sstevel@tonic-gate /*LINTED*/ 11757c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "", 11767c478bd9Sstevel@tonic-gate /*LINTED*/ 11777c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "", 11787c478bd9Sstevel@tonic-gate /*LINTED*/ 11797c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "", 11807c478bd9Sstevel@tonic-gate /*LINTED*/ 11817c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr, 11827c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]); 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) { 1185159d09a2SMark Phalan int stop = 1; 1186159d09a2SMark Phalan 1187159d09a2SMark Phalan if (msg_handler != NULL) { 1188159d09a2SMark Phalan krb5_data reply; 1189159d09a2SMark Phalan 1190159d09a2SMark Phalan reply.data = conns[i].x.in.buf; 1191159d09a2SMark Phalan reply.length = conns[i].x.in.pos - conns[i].x.in.buf; 1192159d09a2SMark Phalan 1193159d09a2SMark Phalan stop = (msg_handler(context, &reply, msg_handler_data) != 0); 1194159d09a2SMark Phalan } 1195159d09a2SMark Phalan 1196159d09a2SMark Phalan if (stop) { 1197159d09a2SMark Phalan dprint("fd service routine says we're done\n"); 1198159d09a2SMark Phalan *winning_conn = i; 1199159d09a2SMark Phalan return 1; 1200159d09a2SMark Phalan } 12017c478bd9Sstevel@tonic-gate } 12027c478bd9Sstevel@tonic-gate } 12037c478bd9Sstevel@tonic-gate } 12047c478bd9Sstevel@tonic-gate if (e != 0) { 12057c478bd9Sstevel@tonic-gate /*LINTED*/ 12067c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e); 12077c478bd9Sstevel@tonic-gate *winning_conn = -1; 12087c478bd9Sstevel@tonic-gate return 1; 12097c478bd9Sstevel@tonic-gate } 12107c478bd9Sstevel@tonic-gate return 0; 12117c478bd9Sstevel@tonic-gate } 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate /* 12147c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior: 12157c478bd9Sstevel@tonic-gate * 12167c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end. 12177c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s. 12187c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s. 12197c478bd9Sstevel@tonic-gate * Fourth => 16s, etc. 12207c478bd9Sstevel@tonic-gate * 12217c478bd9Sstevel@tonic-gate * Restated: 12227c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass. 12237c478bd9Sstevel@tonic-gate * Per TCP server, 1s. 12247c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes. 12257c478bd9Sstevel@tonic-gate * 12267c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2. 12277c478bd9Sstevel@tonic-gate * 12287c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14. 12297c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30. 12307c478bd9Sstevel@tonic-gate * 12317c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on 12327c478bd9Sstevel@tonic-gate * one server, it counts as two. 12337c478bd9Sstevel@tonic-gate */ 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate krb5_error_code 12367c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 12377c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 1238159d09a2SMark Phalan const struct addrlist *addrs, 1239159d09a2SMark Phalan struct sendto_callback_info* callback_info, krb5_data *reply, 1240159d09a2SMark Phalan struct sockaddr *localaddr, socklen_t *localaddrlen, 1241159d09a2SMark Phalan struct sockaddr *remoteaddr, socklen_t *remoteaddrlen, 1242159d09a2SMark Phalan int *addr_used, 1243159d09a2SMark Phalan /* return 0 -> keep going, 1 -> quit */ 1244159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1245159d09a2SMark Phalan void *msg_handler_data) 12467c478bd9Sstevel@tonic-gate { 12477c478bd9Sstevel@tonic-gate int i, pass; 12487c478bd9Sstevel@tonic-gate int delay_this_pass = 2; 12497c478bd9Sstevel@tonic-gate krb5_error_code retval; 12507c478bd9Sstevel@tonic-gate struct conn_state *conns; 1251159d09a2SMark Phalan krb5_data *callback_data = 0; 12527c478bd9Sstevel@tonic-gate size_t n_conns, host; 1253159d09a2SMark Phalan struct select_state *sel_state; 12547c478bd9Sstevel@tonic-gate struct timeval now; 12557c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0; 12567c478bd9Sstevel@tonic-gate char *udpbuf = 0; 12577c478bd9Sstevel@tonic-gate 1258159d09a2SMark Phalan if (message) 1259159d09a2SMark Phalan dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data); 1260159d09a2SMark Phalan else 1261159d09a2SMark Phalan dprint("krb5int_sendto(callback=%p, addrlist=", callback_info); 1262159d09a2SMark Phalan print_addrlist(addrs); 1263159d09a2SMark Phalan dprint(")\n"); 12647c478bd9Sstevel@tonic-gate 12657c478bd9Sstevel@tonic-gate reply->data = 0; 12667c478bd9Sstevel@tonic-gate reply->length = 0; 12677c478bd9Sstevel@tonic-gate 12687c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs; 12697c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state)); 12707c478bd9Sstevel@tonic-gate if (conns == NULL) { 12717c478bd9Sstevel@tonic-gate return ENOMEM; 12727c478bd9Sstevel@tonic-gate } 1273159d09a2SMark Phalan 1274159d09a2SMark Phalan memset(conns, 0, n_conns * sizeof(struct conn_state)); 1275159d09a2SMark Phalan 1276159d09a2SMark Phalan if (callback_info) { 1277159d09a2SMark Phalan callback_data = malloc(n_conns * sizeof(krb5_data)); 1278159d09a2SMark Phalan if (callback_data == NULL) { 1279159d09a2SMark Phalan return ENOMEM; 1280159d09a2SMark Phalan } 1281159d09a2SMark Phalan 1282159d09a2SMark Phalan memset(callback_data, 0, n_conns * sizeof(krb5_data)); 1283159d09a2SMark Phalan } 1284159d09a2SMark Phalan 12857c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 12867c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET; 12877c478bd9Sstevel@tonic-gate } 12887c478bd9Sstevel@tonic-gate 1289159d09a2SMark Phalan /* One for use here, listing all our fds in use, and one for 1290159d09a2SMark Phalan temporary use in service_fds, for the fds of interest. */ 1291159d09a2SMark Phalan sel_state = malloc(2 * sizeof(*sel_state)); 1292159d09a2SMark Phalan if (sel_state == NULL) { 1293159d09a2SMark Phalan free(conns); 1294159d09a2SMark Phalan return ENOMEM; 1295159d09a2SMark Phalan } 1296159d09a2SMark Phalan sel_state->max = 0; 1297159d09a2SMark Phalan sel_state->nfds = 0; 1298159d09a2SMark Phalan sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0; 1299159d09a2SMark Phalan FD_ZERO(&sel_state->rfds); 1300159d09a2SMark Phalan FD_ZERO(&sel_state->wfds); 1301159d09a2SMark Phalan FD_ZERO(&sel_state->xfds); 13027c478bd9Sstevel@tonic-gate 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate /* Set up connections. */ 13057c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 1306159d09a2SMark Phalan retval = setup_connection(&conns[host], 1307159d09a2SMark Phalan addrs->addrs[host].ai, 1308159d09a2SMark Phalan message, 1309159d09a2SMark Phalan &udpbuf); 13107c478bd9Sstevel@tonic-gate if (retval) 13117c478bd9Sstevel@tonic-gate continue; 13127c478bd9Sstevel@tonic-gate } 13137c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) { 13147c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only. 13157c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */ 13167c478bd9Sstevel@tonic-gate /*LINTED*/ 13177c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass); 13187c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 13197c478bd9Sstevel@tonic-gate /*LINTED*/ 13207c478bd9Sstevel@tonic-gate dprint("host %d\n", host); 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */ 1323159d09a2SMark Phalan if (maybe_send(&conns[host], 1324159d09a2SMark Phalan sel_state, 1325159d09a2SMark Phalan callback_info, 1326159d09a2SMark Phalan (callback_info ? &callback_data[host] : NULL))) 13277c478bd9Sstevel@tonic-gate continue; 13287c478bd9Sstevel@tonic-gate 13297c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13307c478bd9Sstevel@tonic-gate if (retval) 13317c478bd9Sstevel@tonic-gate goto egress; 1332159d09a2SMark Phalan sel_state->end_time = now; 1333159d09a2SMark Phalan sel_state->end_time.tv_sec += 1; 1334159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1335159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13367c478bd9Sstevel@tonic-gate if (e) 13377c478bd9Sstevel@tonic-gate break; 1338159d09a2SMark Phalan if (pass > 0 && sel_state->nfds == 0) 13397c478bd9Sstevel@tonic-gate /* 13407c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break 13417c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay, 13427c478bd9Sstevel@tonic-gate * we're probably about to open another connection. 13437c478bd9Sstevel@tonic-gate */ 13447c478bd9Sstevel@tonic-gate break; 13457c478bd9Sstevel@tonic-gate } 13467c478bd9Sstevel@tonic-gate if (e) 13477c478bd9Sstevel@tonic-gate break; 13487c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13497c478bd9Sstevel@tonic-gate if (retval) 13507c478bd9Sstevel@tonic-gate goto egress; 13517c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select 13527c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop 13537c478bd9Sstevel@tonic-gate actually calls select. */ 1354159d09a2SMark Phalan sel_state->end_time.tv_sec += delay_this_pass; 1355159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1356159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13577c478bd9Sstevel@tonic-gate if (e) 13587c478bd9Sstevel@tonic-gate break; 1359159d09a2SMark Phalan if (sel_state->nfds == 0) 13607c478bd9Sstevel@tonic-gate break; 13617c478bd9Sstevel@tonic-gate delay_this_pass *= 2; 13627c478bd9Sstevel@tonic-gate } 13637c478bd9Sstevel@tonic-gate 1364159d09a2SMark Phalan if (sel_state->nfds == 0) { 13657c478bd9Sstevel@tonic-gate /* No addresses? */ 13667c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 13677c478bd9Sstevel@tonic-gate goto egress; 13687c478bd9Sstevel@tonic-gate } 13697c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) { 13707c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 13717c478bd9Sstevel@tonic-gate goto egress; 13727c478bd9Sstevel@tonic-gate } 13737c478bd9Sstevel@tonic-gate /* Success! */ 13747c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf; 13757c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos 13767c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf); 13777c478bd9Sstevel@tonic-gate /*LINTED*/ 1378159d09a2SMark Phalan dprint("returning %d bytes in buffer %p\n", 1379159d09a2SMark Phalan (int) reply->length, reply->data); 13807c478bd9Sstevel@tonic-gate retval = 0; 13817c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0; 1382505d05c7Sgtb if (addr_used) 1383159d09a2SMark Phalan *addr_used = winning_conn; 13847c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0) 1385159d09a2SMark Phalan (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen); 1386159d09a2SMark Phalan 1387159d09a2SMark Phalan if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0) 1388159d09a2SMark Phalan (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen); 1389159d09a2SMark Phalan 13907c478bd9Sstevel@tonic-gate egress: 13917c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 13927c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET) 1393159d09a2SMark Phalan closesocket(conns[i].fd); 13947c478bd9Sstevel@tonic-gate if (conns[i].state == READING 13957c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0 13967c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf) 13977c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf); 1398159d09a2SMark Phalan if (callback_info) { 1399159d09a2SMark Phalan callback_info->pfn_cleanup( callback_info->context, &callback_data[i]); 1400159d09a2SMark Phalan } 14017c478bd9Sstevel@tonic-gate } 1402159d09a2SMark Phalan 1403159d09a2SMark Phalan if (callback_data) 1404159d09a2SMark Phalan free(callback_data); 1405159d09a2SMark Phalan 14067c478bd9Sstevel@tonic-gate free(conns); 14077c478bd9Sstevel@tonic-gate if (reply->data != udpbuf) 14087c478bd9Sstevel@tonic-gate free(udpbuf); 1409159d09a2SMark Phalan free(sel_state); 14107c478bd9Sstevel@tonic-gate return retval; 14117c478bd9Sstevel@tonic-gate } 1412