17c478bd9Sstevel@tonic-gate /* 256bbb0b2SPeter 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> 39*5e01956fSGlenn Barry #include <locale.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" 47159d09a2SMark Phalan #ifdef _WIN32 48159d09a2SMark Phalan #include <sys/timeb.h> 49159d09a2SMark Phalan #endif 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #ifdef _AIX 527c478bd9Sstevel@tonic-gate #include <sys/select.h> 537c478bd9Sstevel@tonic-gate #endif 547c478bd9Sstevel@tonic-gate 55159d09a2SMark 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 61159d09a2SMark 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 68159d09a2SMark Phalan #undef DEBUG 69159d09a2SMark Phalan 70159d09a2SMark Phalan #ifdef DEBUG 71159d09a2SMark Phalan int krb5int_debug_sendto_kdc = 0; 72159d09a2SMark Phalan #define debug krb5int_debug_sendto_kdc 73159d09a2SMark Phalan 747c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len) 757c478bd9Sstevel@tonic-gate { 76159d09a2SMark Phalan #if 0 77159d09a2SMark Phalan FILE *logfile; 78159d09a2SMark Phalan logfile = fopen("/tmp/sendto_kdc.log", "a"); 79159d09a2SMark Phalan if (logfile == NULL) 80159d09a2SMark Phalan return; 81159d09a2SMark Phalan fwrite(data, 1, len, logfile); 82159d09a2SMark Phalan fclose(logfile); 83159d09a2SMark Phalan #else 847c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr); 857c478bd9Sstevel@tonic-gate /* stderr is unbuffered */ 86159d09a2SMark 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. */ 98159d09a2SMark Phalan #if 0 99159d09a2SMark Phalan static void put(const void *ptr, size_t len) 100159d09a2SMark Phalan { 101159d09a2SMark Phalan (*krb5int_sendtokdc_debug_handler)(ptr, len); 102159d09a2SMark Phalan } 103159d09a2SMark 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 } 110159d09a2SMark Phalan #else 111159d09a2SMark Phalan void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0; 112159d09a2SMark Phalan #endif 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint 115159d09a2SMark Phalan void 1167c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...) 1177c478bd9Sstevel@tonic-gate { 118159d09a2SMark 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; 132159d09a2SMark Phalan #ifndef max 133159d09a2SMark Phalan #define max(a,b) ((a) > (b) ? (a) : (b)) 134159d09a2SMark Phalan #endif 135159d09a2SMark 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); 174159d09a2SMark Phalan p = NULL; 175159d09a2SMark Phalan #ifdef HAVE_STRERROR_R 176159d09a2SMark Phalan if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0) 177159d09a2SMark Phalan p = tmpbuf; 178159d09a2SMark Phalan #endif 179159d09a2SMark Phalan if (p == NULL) 180159d09a2SMark 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 *); 232159d09a2SMark Phalan if (ai->ai_socktype == SOCK_DGRAM) 233159d09a2SMark Phalan strcpy(tmpbuf, "dgram"); 234159d09a2SMark Phalan else if (ai->ai_socktype == SOCK_STREAM) 235159d09a2SMark Phalan strcpy(tmpbuf, "stream"); 236159d09a2SMark Phalan else 237159d09a2SMark 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), 241159d09a2SMark Phalan NI_NUMERICHOST | NI_NUMERICSERV)) { 242159d09a2SMark Phalan if (ai->ai_addr->sa_family == AF_UNSPEC) 243159d09a2SMark Phalan strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC"); 244159d09a2SMark Phalan else 245159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family); 246159d09a2SMark Phalan } else 247159d09a2SMark 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 *); 253159d09a2SMark 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); 266159d09a2SMark Phalan #endif 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 269159d09a2SMark Phalan #define print_addrlist krb5int_print_addrlist 270159d09a2SMark Phalan static void 271159d09a2SMark Phalan print_addrlist (const struct addrlist *a) 272159d09a2SMark Phalan { 273159d09a2SMark Phalan int i; 274159d09a2SMark Phalan dprint("%d{", a->naddrs); 275159d09a2SMark Phalan for (i = 0; i < a->naddrs; i++) 276159d09a2SMark Phalan dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai); 277159d09a2SMark Phalan dprint("}"); 278159d09a2SMark Phalan } 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate static int 2817c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src) 2827c478bd9Sstevel@tonic-gate { 283159d09a2SMark Phalan /* Wouldn't it be nice if we could filter out duplicates? The 284159d09a2SMark Phalan alloc/free handling makes that pretty difficult though. */ 2857c478bd9Sstevel@tonic-gate int err, i; 2867c478bd9Sstevel@tonic-gate 287159d09a2SMark 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*/ 293159d09a2SMark 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*/ 298159d09a2SMark 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]; 308159d09a2SMark Phalan src->addrs[i].ai = 0; 309159d09a2SMark Phalan src->addrs[i].freefn = 0; 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate dest->naddrs += i; 3127c478bd9Sstevel@tonic-gate src->naddrs = 0; 3137c478bd9Sstevel@tonic-gate 314159d09a2SMark 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*/ 320159d09a2SMark 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 328159d09a2SMark Phalan static int 329159d09a2SMark Phalan in_addrlist (struct addrinfo *thisaddr, struct addrlist *list) 330159d09a2SMark Phalan { 331159d09a2SMark Phalan int i; 332159d09a2SMark Phalan for (i = 0; i < list->naddrs; i++) { 333159d09a2SMark Phalan if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen 334159d09a2SMark Phalan && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr, 335159d09a2SMark Phalan thisaddr->ai_addrlen)) 336159d09a2SMark Phalan return 1; 337159d09a2SMark Phalan } 338159d09a2SMark Phalan return 0; 339159d09a2SMark Phalan } 340159d09a2SMark Phalan 341159d09a2SMark Phalan static int 342159d09a2SMark Phalan check_for_svc_unavailable (krb5_context context, 343159d09a2SMark Phalan const krb5_data *reply, 344159d09a2SMark Phalan void *msg_handler_data) 345159d09a2SMark Phalan { 346159d09a2SMark Phalan krb5_error_code *retval = (krb5_error_code *)msg_handler_data; 347159d09a2SMark Phalan 348159d09a2SMark Phalan *retval = 0; 349159d09a2SMark Phalan 350159d09a2SMark Phalan if (krb5_is_krb_error(reply)) { 351159d09a2SMark Phalan krb5_error *err_reply; 352159d09a2SMark Phalan 353159d09a2SMark Phalan if (decode_krb5_error(reply, &err_reply) == 0) { 354159d09a2SMark Phalan *retval = err_reply->error; 355159d09a2SMark Phalan krb5_free_error(context, err_reply); 356159d09a2SMark Phalan 357159d09a2SMark Phalan /* Returning 0 means continue to next KDC */ 358159d09a2SMark Phalan return (*retval != KDC_ERR_SVC_UNAVAILABLE); 359159d09a2SMark Phalan } 360159d09a2SMark Phalan } 361159d09a2SMark Phalan 362159d09a2SMark Phalan return 1; 363159d09a2SMark Phalan } 364159d09a2SMark 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) 380*5e01956fSGlenn Barry { 381*5e01956fSGlenn Barry return (krb5_sendto_kdc2(context, message, realm, reply, use_master, 382*5e01956fSGlenn Barry tcp_only, NULL)); 383*5e01956fSGlenn Barry } 384*5e01956fSGlenn Barry 385*5e01956fSGlenn Barry /* 386*5e01956fSGlenn Barry * Solaris Kerberos 387*5e01956fSGlenn Barry * Same as krb5_sendto_kdc plus an extra arg to return the FQDN 388*5e01956fSGlenn Barry * of the KDC sent the request. 389*5e01956fSGlenn Barry * Caller (at top of stack) needs to free hostname_used. 390*5e01956fSGlenn Barry */ 391*5e01956fSGlenn Barry krb5_error_code 392*5e01956fSGlenn Barry krb5_sendto_kdc2 (krb5_context context, const krb5_data *message, 393*5e01956fSGlenn Barry const krb5_data *realm, krb5_data *reply, 394*5e01956fSGlenn Barry int *use_master, int tcp_only, char **hostname_used) 3957c478bd9Sstevel@tonic-gate { 396159d09a2SMark Phalan krb5_error_code retval, retval2; 39756bbb0b2SPeter Shoults struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */ 398505d05c7Sgtb int socktype1 = 0, socktype2 = 0, addr_used; 3997c478bd9Sstevel@tonic-gate 4007c478bd9Sstevel@tonic-gate /* 4017c478bd9Sstevel@tonic-gate * find KDC location(s) for realm 4027c478bd9Sstevel@tonic-gate */ 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* 4057c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem, 4067c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be 4077c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or 4087c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also, 4097c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} 4107c478bd9Sstevel@tonic-gate * should probably be returned as well. 4117c478bd9Sstevel@tonic-gate */ 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate /*LINTED*/ 4147c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", 4157c478bd9Sstevel@tonic-gate /*LINTED*/ 416505d05c7Sgtb message->length, message->data, realm, *use_master, tcp_only); 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) { 4197c478bd9Sstevel@tonic-gate int tmp; 4207c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile, 4217c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0, 4227c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp); 4237c478bd9Sstevel@tonic-gate if (retval) 4247c478bd9Sstevel@tonic-gate return retval; 4257c478bd9Sstevel@tonic-gate if (tmp < 0) 4267c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT; 42756a424ccSmp else if (tmp > HARD_UDP_LIMIT) 4287c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is 4297c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can 4307c478bd9Sstevel@tonic-gate support. */ 4317c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT; 4327c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp; 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate 435505d05c7Sgtb retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate if (tcp_only) 4387c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0; 4397c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit) 4407c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; 4417c478bd9Sstevel@tonic-gate else 4427c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; 4437c478bd9Sstevel@tonic-gate 444505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); 4457c478bd9Sstevel@tonic-gate if (socktype2) { 4467c478bd9Sstevel@tonic-gate struct addrlist addrs2; 4477c478bd9Sstevel@tonic-gate 448159d09a2SMark Phalan retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master, 449159d09a2SMark Phalan socktype2, 0); 450159d09a2SMark Phalan #if 0 451159d09a2SMark Phalan if (retval2 == 0) { 452159d09a2SMark Phalan (void) merge_addrlists(&addrs, &addrs2); 453159d09a2SMark Phalan krb5int_free_addrlist(&addrs2); 454159d09a2SMark Phalan retval = 0; 455159d09a2SMark Phalan } else if (retval == KRB5_REALM_CANT_RESOLVE) { 456159d09a2SMark Phalan retval = retval2; 457159d09a2SMark Phalan } 458159d09a2SMark Phalan #else 459159d09a2SMark Phalan retval = retval2; 4607c478bd9Sstevel@tonic-gate if (retval == 0) { 4617c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2); 4627c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2); 4637c478bd9Sstevel@tonic-gate } 464159d09a2SMark Phalan #endif 4657c478bd9Sstevel@tonic-gate } 466159d09a2SMark Phalan 4677c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) { 468159d09a2SMark Phalan krb5_error_code err = 0; 469159d09a2SMark Phalan 470159d09a2SMark Phalan retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, 471159d09a2SMark Phalan 0, 0, &addr_used, check_for_svc_unavailable, &err); 472159d09a2SMark Phalan switch (retval) { 473159d09a2SMark Phalan case 0: 474505d05c7Sgtb /* 475159d09a2SMark Phalan * Set use_master to 1 if we ended up talking to a master when 476159d09a2SMark Phalan * we didn't explicitly request to 477159d09a2SMark Phalan */ 478159d09a2SMark Phalan if (*use_master == 0) { 479159d09a2SMark Phalan struct addrlist addrs3; 480159d09a2SMark Phalan retval = krb5_locate_kdc(context, realm, &addrs3, 1, 481159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_socktype, 482159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_family); 483159d09a2SMark Phalan if (retval == 0) { 484159d09a2SMark Phalan if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3)) 485159d09a2SMark Phalan *use_master = 1; 486159d09a2SMark Phalan krb5int_free_addrlist (&addrs3); 487159d09a2SMark Phalan } 488159d09a2SMark Phalan } 489*5e01956fSGlenn Barry 490*5e01956fSGlenn Barry if (hostname_used) { 491*5e01956fSGlenn Barry struct sockaddr *sa; 492*5e01956fSGlenn Barry char buf[NI_MAXHOST]; 493*5e01956fSGlenn Barry int err; 494*5e01956fSGlenn Barry 495*5e01956fSGlenn Barry *hostname_used = NULL; 496*5e01956fSGlenn Barry sa = addrs.addrs[addr_used].ai->ai_addr; 497*5e01956fSGlenn Barry err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, 498*5e01956fSGlenn Barry AI_CANONNAME); 499*5e01956fSGlenn Barry if (err) 500*5e01956fSGlenn Barry err = getnameinfo (sa, socklen (sa), buf, 501*5e01956fSGlenn Barry sizeof (buf), 0, 0, 502*5e01956fSGlenn Barry NI_NUMERICHOST); 503*5e01956fSGlenn Barry if (!err) 504*5e01956fSGlenn Barry *hostname_used = strdup(buf); 505*5e01956fSGlenn Barry /* don't sweat strdup fail */ 506*5e01956fSGlenn Barry } 507159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 508159d09a2SMark Phalan return 0; 509159d09a2SMark Phalan default: 510159d09a2SMark Phalan break; 511159d09a2SMark Phalan /* Cases here are for constructing useful error messages. */ 512159d09a2SMark Phalan case KRB5_KDC_UNREACH: 513159d09a2SMark Phalan if (err == KDC_ERR_SVC_UNAVAILABLE) { 514159d09a2SMark Phalan retval = KRB5KDC_ERR_SVC_UNAVAILABLE; 515159d09a2SMark Phalan } else { 516159d09a2SMark Phalan krb5_set_error_message(context, retval, 517*5e01956fSGlenn Barry dgettext(TEXT_DOMAIN, 518*5e01956fSGlenn Barry "Cannot contact any KDC for realm '%.*s'"), 519*5e01956fSGlenn Barry realm->length, realm->data); 520505d05c7Sgtb } 521159d09a2SMark Phalan break; 522505d05c7Sgtb } 523159d09a2SMark Phalan krb5int_free_addrlist (&addrs); 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate return retval; 5267c478bd9Sstevel@tonic-gate } 5277c478bd9Sstevel@tonic-gate 528159d09a2SMark Phalan #ifdef DEBUG 529159d09a2SMark Phalan 530159d09a2SMark Phalan #ifdef _WIN32 531159d09a2SMark Phalan #define dperror(MSG) \ 532159d09a2SMark Phalan dprint("%s: an error occurred ... " \ 533159d09a2SMark Phalan "\tline=%d errno=%m socketerrno=%m\n", \ 534159d09a2SMark Phalan (MSG), __LINE__, errno, SOCKET_ERRNO) 535159d09a2SMark Phalan #else 536159d09a2SMark Phalan #define dperror(MSG) dprint("%s: %m\n", MSG, errno) 537159d09a2SMark Phalan #endif 538159d09a2SMark Phalan #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0) 539159d09a2SMark Phalan 540159d09a2SMark Phalan #else /* ! DEBUG */ 541159d09a2SMark Phalan 542159d09a2SMark Phalan #define dperror(MSG) ((void)(MSG)) 543159d09a2SMark Phalan #define dfprintf(ARGLIST) ((void)0) 544159d09a2SMark Phalan 545159d09a2SMark Phalan #endif 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate /* 5487c478bd9Sstevel@tonic-gate * Notes: 5497c478bd9Sstevel@tonic-gate * 5507c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes 5517c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up 5527c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags 5537c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it 5547c478bd9Sstevel@tonic-gate * system-specific. 5557c478bd9Sstevel@tonic-gate * 5567c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually 5577c478bd9Sstevel@tonic-gate * fix the UDP code to do the same. 5587c478bd9Sstevel@tonic-gate * 5597c478bd9Sstevel@tonic-gate * To do: 5607c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options? 5617c478bd9Sstevel@tonic-gate * - error codes that don't suck 5627c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status 5637c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP 5647c478bd9Sstevel@tonic-gate * connections already in progress 5657c478bd9Sstevel@tonic-gate */ 5667c478bd9Sstevel@tonic-gate 567159d09a2SMark Phalan #include "cm.h" 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp) 5707c478bd9Sstevel@tonic-gate { 571159d09a2SMark Phalan #ifdef _WIN32 572159d09a2SMark Phalan struct _timeb tb; 573159d09a2SMark Phalan _ftime(&tb); 574159d09a2SMark Phalan tvp->tv_sec = tb.time; 575159d09a2SMark Phalan tvp->tv_usec = tb.millitm * 1000; 576159d09a2SMark Phalan /* Can _ftime fail? */ 577159d09a2SMark Phalan return 0; 578159d09a2SMark Phalan #else 5797c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) { 5807c478bd9Sstevel@tonic-gate dperror("gettimeofday"); 5817c478bd9Sstevel@tonic-gate return errno; 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate return 0; 584159d09a2SMark Phalan #endif 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate /* 5887c478bd9Sstevel@tonic-gate * Call select and return results. 5897c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout 5907c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets 5917c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code. 5927c478bd9Sstevel@tonic-gate */ 5937c478bd9Sstevel@tonic-gate krb5_error_code 5947c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in, 5957c478bd9Sstevel@tonic-gate struct select_state *out, int *sret) 5967c478bd9Sstevel@tonic-gate { 5977c478bd9Sstevel@tonic-gate struct timeval now, *timo; 5987c478bd9Sstevel@tonic-gate krb5_error_code e; 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate *out = *in; 6017c478bd9Sstevel@tonic-gate e = getcurtime(&now); 6027c478bd9Sstevel@tonic-gate if (e) 6037c478bd9Sstevel@tonic-gate return e; 6047c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0) 6057c478bd9Sstevel@tonic-gate timo = 0; 6067c478bd9Sstevel@tonic-gate else { 6077c478bd9Sstevel@tonic-gate timo = &out->end_time; 6087c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec; 6097c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec; 6107c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) { 6117c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000; 6127c478bd9Sstevel@tonic-gate out->end_time.tv_sec--; 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) { 6157c478bd9Sstevel@tonic-gate *sret = 0; 6167c478bd9Sstevel@tonic-gate return 0; 6177c478bd9Sstevel@tonic-gate } 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate /*LINTED*/ 6207c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n", 6217c478bd9Sstevel@tonic-gate /*LINTED*/ 622159d09a2SMark Phalan out->max, 623159d09a2SMark Phalan &out->rfds, &out->wfds, &out->xfds, out->max, 624159d09a2SMark Phalan timo); 6257c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo); 6267c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 6277c478bd9Sstevel@tonic-gate 628159d09a2SMark Phalan /* Solaris Kerberos */ 6297c478bd9Sstevel@tonic-gate #ifdef DEBUG 6307c478bd9Sstevel@tonic-gate /*LINTED*/ 6317c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret); 6327c478bd9Sstevel@tonic-gate if (*sret < 0) 6337c478bd9Sstevel@tonic-gate /*LINTED*/ 6347c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e); 6357c478bd9Sstevel@tonic-gate else if (*sret == 0) 6367c478bd9Sstevel@tonic-gate /*LINTED*/ 6377c478bd9Sstevel@tonic-gate dprint(" (timeout)\n"); 6387c478bd9Sstevel@tonic-gate else 6397c478bd9Sstevel@tonic-gate /*LINTED*/ 6407c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max); 6417c478bd9Sstevel@tonic-gate #endif 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate if (*sret < 0) 6447c478bd9Sstevel@tonic-gate return e; 6457c478bd9Sstevel@tonic-gate return 0; 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn, 6497c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6507c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn, 6517c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 6527c478bd9Sstevel@tonic-gate 653159d09a2SMark Phalan static void 654159d09a2SMark Phalan set_conn_state_msg_length (struct conn_state *state, const krb5_data *message) 655159d09a2SMark Phalan { 656159d09a2SMark Phalan if (!message || message->length == 0) 657159d09a2SMark Phalan return; 658159d09a2SMark Phalan 659159d09a2SMark Phalan if (!state->is_udp) { 660159d09a2SMark Phalan 661159d09a2SMark Phalan state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff; 662159d09a2SMark Phalan state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff; 663159d09a2SMark Phalan state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff; 664159d09a2SMark Phalan state->x.out.msg_len_buf[3] = message->length & 0xff; 665159d09a2SMark Phalan 666159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4); 667159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 668159d09a2SMark Phalan state->x.out.sg_count = 2; 669159d09a2SMark Phalan 670159d09a2SMark Phalan } else { 671159d09a2SMark Phalan 672159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 673159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], 0, 0); 674159d09a2SMark Phalan state->x.out.sg_count = 1; 675159d09a2SMark Phalan 676159d09a2SMark Phalan } 677159d09a2SMark Phalan } 678159d09a2SMark Phalan 679159d09a2SMark Phalan 6807c478bd9Sstevel@tonic-gate 6817c478bd9Sstevel@tonic-gate static int 6827c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai, 683159d09a2SMark Phalan const krb5_data *message, char **udpbufp) 6847c478bd9Sstevel@tonic-gate { 6857c478bd9Sstevel@tonic-gate state->state = INITIALIZING; 6867c478bd9Sstevel@tonic-gate state->err = 0; 6877c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf; 6887c478bd9Sstevel@tonic-gate state->addr = ai; 6897c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 6907c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 6917c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 692159d09a2SMark Phalan /* 6937c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4); 6947c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 6957c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2; 696159d09a2SMark Phalan */ 697159d09a2SMark Phalan 6987c478bd9Sstevel@tonic-gate state->is_udp = 0; 6997c478bd9Sstevel@tonic-gate state->service = service_tcp_fd; 700159d09a2SMark Phalan set_conn_state_msg_length (state, message); 7017c478bd9Sstevel@tonic-gate } else { 702159d09a2SMark Phalan /* 7037c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 7047c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 7057c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1; 706159d09a2SMark Phalan */ 707159d09a2SMark Phalan 7087c478bd9Sstevel@tonic-gate state->is_udp = 1; 7097c478bd9Sstevel@tonic-gate state->service = service_udp_fd; 710159d09a2SMark Phalan set_conn_state_msg_length (state, message); 7117c478bd9Sstevel@tonic-gate 7127c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 7137c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size); 7147c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 7157c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)"); 7167c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 7177c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 7187c478bd9Sstevel@tonic-gate state->state = FAILED; 7197c478bd9Sstevel@tonic-gate return 1; 7207c478bd9Sstevel@tonic-gate } 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp; 7237c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size; 7247c478bd9Sstevel@tonic-gate } 7257c478bd9Sstevel@tonic-gate return 0; 7267c478bd9Sstevel@tonic-gate } 7277c478bd9Sstevel@tonic-gate 7287c478bd9Sstevel@tonic-gate static int 729159d09a2SMark Phalan start_connection (struct conn_state *state, 730159d09a2SMark Phalan struct select_state *selstate, 731159d09a2SMark Phalan struct sendto_callback_info* callback_info, 732159d09a2SMark Phalan krb5_data* callback_buffer) 7337c478bd9Sstevel@tonic-gate { 7347c478bd9Sstevel@tonic-gate int fd, e; 7357c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr; 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate /*LINTED*/ 7387c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, 7397c478bd9Sstevel@tonic-gate /*LINTED*/ 7407c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family); 7417c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0); 7427c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) { 7437c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7447c478bd9Sstevel@tonic-gate /*LINTED*/ 7457c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family); 7467c478bd9Sstevel@tonic-gate return -1; /* try other hosts */ 7477c478bd9Sstevel@tonic-gate } 7487c478bd9Sstevel@tonic-gate /* Make it non-blocking. */ 7497c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 7507c478bd9Sstevel@tonic-gate static const int one = 1; 7517c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 }; 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one)) 7547c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)"); 7557c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) 7567c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)"); 7577c478bd9Sstevel@tonic-gate } 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */ 7607c478bd9Sstevel@tonic-gate /*LINTED*/ 7617c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai); 7627c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen); 7637c478bd9Sstevel@tonic-gate if (e != 0) { 7647c478bd9Sstevel@tonic-gate /* 7657c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking 7667c478bd9Sstevel@tonic-gate * connections. 7677c478bd9Sstevel@tonic-gate */ 7687c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { 7697c478bd9Sstevel@tonic-gate state->state = CONNECTING; 770159d09a2SMark Phalan state->fd = fd; 7717c478bd9Sstevel@tonic-gate } else { 7727c478bd9Sstevel@tonic-gate /*LINTED*/ 7737c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO); 774159d09a2SMark Phalan (void) closesocket(fd); 7757c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 7767c478bd9Sstevel@tonic-gate state->state = FAILED; 7777c478bd9Sstevel@tonic-gate return -2; 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate } else { 7807c478bd9Sstevel@tonic-gate /* 7817c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it 7827c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before 7837c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network 7847c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it. 7857c478bd9Sstevel@tonic-gate */ 7867c478bd9Sstevel@tonic-gate state->state = WRITING; 787159d09a2SMark Phalan state->fd = fd; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate /*LINTED*/ 7907c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]); 7917c478bd9Sstevel@tonic-gate 792159d09a2SMark Phalan 793159d09a2SMark Phalan /* 794159d09a2SMark Phalan * Here's where KPASSWD callback gets the socket information it needs for 795159d09a2SMark Phalan * a kpasswd request 796159d09a2SMark Phalan */ 797159d09a2SMark Phalan if (callback_info) { 798159d09a2SMark Phalan 799159d09a2SMark Phalan e = callback_info->pfn_callback(state, 800159d09a2SMark Phalan callback_info->context, 801159d09a2SMark Phalan callback_buffer); 802159d09a2SMark Phalan if (e != 0) { 803159d09a2SMark Phalan dprint("callback failed: %m\n", e); 804159d09a2SMark Phalan (void) closesocket(fd); 805159d09a2SMark Phalan state->err = e; 806159d09a2SMark Phalan state->fd = INVALID_SOCKET; 807159d09a2SMark Phalan state->state = FAILED; 808159d09a2SMark Phalan return -3; 809159d09a2SMark Phalan } 810159d09a2SMark Phalan 811159d09a2SMark Phalan dprint("callback %p (message=%d@%p)\n", 812159d09a2SMark Phalan state, 813159d09a2SMark Phalan callback_buffer->length, 814159d09a2SMark Phalan callback_buffer->data); 815159d09a2SMark Phalan 816159d09a2SMark Phalan set_conn_state_msg_length( state, callback_buffer ); 817159d09a2SMark Phalan } 8187c478bd9Sstevel@tonic-gate 8197c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) { 8207c478bd9Sstevel@tonic-gate /* Send it now. */ 8217c478bd9Sstevel@tonic-gate int ret; 8227c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0]; 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate /*LINTED*/ 8257c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); 8267c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); 8277c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) { 8287c478bd9Sstevel@tonic-gate dperror("sendto"); 8297c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 8307c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 8317c478bd9Sstevel@tonic-gate state->state = FAILED; 832159d09a2SMark Phalan return -4; 8337c478bd9Sstevel@tonic-gate } else { 8347c478bd9Sstevel@tonic-gate state->state = READING; 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate } 837159d09a2SMark Phalan #ifdef DEBUG 838159d09a2SMark Phalan if (debug) { 839159d09a2SMark Phalan struct sockaddr_storage ss; 840159d09a2SMark Phalan socklen_t sslen = sizeof(ss); 841159d09a2SMark Phalan if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) { 842159d09a2SMark Phalan struct addrinfo hack_ai; 843159d09a2SMark Phalan memset(&hack_ai, 0, sizeof(hack_ai)); 844159d09a2SMark Phalan hack_ai.ai_addr = (struct sockaddr *) &ss; 845159d09a2SMark Phalan hack_ai.ai_addrlen = sslen; 846159d09a2SMark Phalan hack_ai.ai_socktype = SOCK_DGRAM; 847159d09a2SMark Phalan hack_ai.ai_family = ai->ai_family; 848159d09a2SMark Phalan dprint("local socket address is %A\n", &hack_ai); 849159d09a2SMark Phalan } 850159d09a2SMark Phalan } 851159d09a2SMark Phalan #endif 8527c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds); 8537c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING) 8547c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds); 8557c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds); 8567c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd) 8577c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1; 8587c478bd9Sstevel@tonic-gate selstate->nfds++; 8597c478bd9Sstevel@tonic-gate 8607c478bd9Sstevel@tonic-gate /*LINTED*/ 8617c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n", 8627c478bd9Sstevel@tonic-gate /*LINTED*/ 8637c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate return 0; 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise. 8697c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response. 8707c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the 8717c478bd9Sstevel@tonic-gate next connection. */ 8727c478bd9Sstevel@tonic-gate static int 873159d09a2SMark Phalan maybe_send (struct conn_state *conn, 874159d09a2SMark Phalan struct select_state *selstate, 875159d09a2SMark Phalan struct sendto_callback_info* callback_info, 876159d09a2SMark Phalan krb5_data* callback_buffer) 8777c478bd9Sstevel@tonic-gate { 8787c478bd9Sstevel@tonic-gate sg_buf *sg; 8797c478bd9Sstevel@tonic-gate 8807c478bd9Sstevel@tonic-gate /*LINTED*/ 8817c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn, 8827c478bd9Sstevel@tonic-gate /*LINTED*/ 883159d09a2SMark Phalan state_strings[conn->state], 884159d09a2SMark Phalan conn->is_udp ? "udp" : "tcp"); 8857c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING) 886159d09a2SMark Phalan return start_connection(conn, selstate, callback_info, callback_buffer); 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */ 8897c478bd9Sstevel@tonic-gate if (conn->state == FAILED) { 8907c478bd9Sstevel@tonic-gate dprint("connection already closed\n"); 8917c478bd9Sstevel@tonic-gate return -1; 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) { 8957c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n"); 8967c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we 8977c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */ 8987c478bd9Sstevel@tonic-gate return -1; 8997c478bd9Sstevel@tonic-gate } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a 9027c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */ 9037c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0]; 9047c478bd9Sstevel@tonic-gate /*LINTED*/ 9057c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd); 9067c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) { 9077c478bd9Sstevel@tonic-gate dperror("send"); 9087c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass. 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the 9117c478bd9Sstevel@tonic-gate select callbacks? */ 9127c478bd9Sstevel@tonic-gate return -1; 9137c478bd9Sstevel@tonic-gate } 9147c478bd9Sstevel@tonic-gate /* Yay, it worked. */ 9157c478bd9Sstevel@tonic-gate return 0; 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate static void 9197c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err) 9207c478bd9Sstevel@tonic-gate { 9217c478bd9Sstevel@tonic-gate conn->state = FAILED; 9227c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH); 9237c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds); 9247c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 9257c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds); 9267c478bd9Sstevel@tonic-gate conn->err = err; 9277c478bd9Sstevel@tonic-gate /*LINTED*/ 9287c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err); 9297c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */ 9307c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) { 9317c478bd9Sstevel@tonic-gate while (selstate->max > 0 9327c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds) 9337c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds) 9347c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds)) 9357c478bd9Sstevel@tonic-gate selstate->max--; 9367c478bd9Sstevel@tonic-gate /*LINTED*/ 9377c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max); 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate selstate->nfds--; 9407c478bd9Sstevel@tonic-gate } 9417c478bd9Sstevel@tonic-gate 942159d09a2SMark Phalan /* Check socket for error. */ 943159d09a2SMark Phalan static int 944159d09a2SMark Phalan get_so_error(int fd) 945159d09a2SMark Phalan { 946159d09a2SMark Phalan int e, sockerr; 947159d09a2SMark Phalan socklen_t sockerrlen; 948159d09a2SMark Phalan 949159d09a2SMark Phalan sockerr = 0; 950159d09a2SMark Phalan sockerrlen = sizeof(sockerr); 951159d09a2SMark Phalan e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen); 952159d09a2SMark Phalan if (e != 0) { 953159d09a2SMark Phalan /* What to do now? */ 954159d09a2SMark Phalan e = SOCKET_ERRNO; 955159d09a2SMark Phalan dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e); 956159d09a2SMark Phalan return e; 957159d09a2SMark Phalan } 958159d09a2SMark Phalan return sockerr; 959159d09a2SMark Phalan } 960159d09a2SMark Phalan 9617c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit 9627c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message, 9637c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */ 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate static int 9667c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate, 9677c478bd9Sstevel@tonic-gate int ssflags) 9687c478bd9Sstevel@tonic-gate { 9697c478bd9Sstevel@tonic-gate krb5_error_code e = 0; 9707c478bd9Sstevel@tonic-gate int nwritten, nread; 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) 9737c478bd9Sstevel@tonic-gate abort(); 9747c478bd9Sstevel@tonic-gate switch (conn->state) { 9757c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp; 9767c478bd9Sstevel@tonic-gate 9777c478bd9Sstevel@tonic-gate case CONNECTING: 9787c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 9797c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */ 9807c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */; 9817c478bd9Sstevel@tonic-gate kill_conn: 9827c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e); 9837c478bd9Sstevel@tonic-gate if (e == EINVAL) { 9847c478bd9Sstevel@tonic-gate closesocket(conn->fd); 9857c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET; 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate return e == 0; 9887c478bd9Sstevel@tonic-gate } 9897c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 9907c478bd9Sstevel@tonic-gate handle_exception: 991159d09a2SMark Phalan e = get_so_error(conn->fd); 992159d09a2SMark Phalan if (e) 993159d09a2SMark Phalan dprint("socket error on exception fd: %m", e); 994159d09a2SMark Phalan else 995159d09a2SMark Phalan dprint("no socket error info available on exception fd"); 9967c478bd9Sstevel@tonic-gate goto kill_conn; 9977c478bd9Sstevel@tonic-gate } 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate /* 10007c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail? 10017c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed. 1002159d09a2SMark Phalan * Call getsockopt to see if error pending. 1003159d09a2SMark Phalan * 1004159d09a2SMark Phalan * (For most UNIX systems it works to just try writing the 1005159d09a2SMark Phalan * first time and detect an error. But Bill Dodd at IBM 1006159d09a2SMark Phalan * reports that some version of AIX, SIGPIPE can result.) 10077c478bd9Sstevel@tonic-gate */ 1008159d09a2SMark Phalan e = get_so_error(conn->fd); 1009159d09a2SMark Phalan if (e) { 1010159d09a2SMark Phalan dprint("socket error on write fd: %m", e); 1011159d09a2SMark Phalan goto kill_conn; 1012159d09a2SMark Phalan } 10137c478bd9Sstevel@tonic-gate conn->state = WRITING; 10147c478bd9Sstevel@tonic-gate goto try_writing; 10157c478bd9Sstevel@tonic-gate 10167c478bd9Sstevel@tonic-gate case WRITING: 10177c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 10187c478bd9Sstevel@tonic-gate e = E2BIG; 10197c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */ 10207c478bd9Sstevel@tonic-gate goto kill_conn; 10217c478bd9Sstevel@tonic-gate } 10227c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) 10237c478bd9Sstevel@tonic-gate goto handle_exception; 10247c478bd9Sstevel@tonic-gate 10257c478bd9Sstevel@tonic-gate try_writing: 10267c478bd9Sstevel@tonic-gate /*LINTED*/ 10277c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n", 10287c478bd9Sstevel@tonic-gate /*LINTED*/ 10297c478bd9Sstevel@tonic-gate conn->x.out.sg_count, 10307c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) 10317c478bd9Sstevel@tonic-gate /*LINTED*/ 10327c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])), 10337c478bd9Sstevel@tonic-gate conn->fd); 10347c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, 10357c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp); 10367c478bd9Sstevel@tonic-gate if (nwritten < 0) { 10377c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 10387c478bd9Sstevel@tonic-gate /*LINTED*/ 10397c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e); 10407c478bd9Sstevel@tonic-gate goto kill_conn; 10417c478bd9Sstevel@tonic-gate } 10427c478bd9Sstevel@tonic-gate /*LINTED*/ 10437c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten); 10447c478bd9Sstevel@tonic-gate while (nwritten) { 10457c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp; 10467c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) { 10477c478bd9Sstevel@tonic-gate /*LINTED*/ 10487c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten); 10497c478bd9Sstevel@tonic-gate nwritten = 0; 10507c478bd9Sstevel@tonic-gate } else { 10517c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp); 10527c478bd9Sstevel@tonic-gate conn->x.out.sgp++; 10537c478bd9Sstevel@tonic-gate conn->x.out.sg_count--; 10547c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0) 10557c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */ 10567c478bd9Sstevel@tonic-gate abort(); 10577c478bd9Sstevel@tonic-gate } 10587c478bd9Sstevel@tonic-gate } 10597c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) { 10607c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */ 10617c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because 10627c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/ 10637c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 10647c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data 10657c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode? 10667c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */ 10677c478bd9Sstevel@tonic-gate /*LINTED*/ 10687c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd); 10697c478bd9Sstevel@tonic-gate conn->state = READING; 10707c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0; 10717c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0; 10727c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10737c478bd9Sstevel@tonic-gate conn->x.in.pos = 0; 10747c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0; 10757c478bd9Sstevel@tonic-gate } 10767c478bd9Sstevel@tonic-gate return 0; 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate case READING: 10797c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 10807c478bd9Sstevel@tonic-gate if (conn->x.in.buf) { 10817c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10827c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10837c478bd9Sstevel@tonic-gate } 10847c478bd9Sstevel@tonic-gate goto handle_exception; 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate 10877c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 10887c478bd9Sstevel@tonic-gate /* Reading data. */ 10897c478bd9Sstevel@tonic-gate /*LINTED*/ 10907c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n", 10917c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd); 10927c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); 10937c478bd9Sstevel@tonic-gate if (nread <= 0) { 10947c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET; 10957c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 10967c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 10977c478bd9Sstevel@tonic-gate goto kill_conn; 10987c478bd9Sstevel@tonic-gate } 10997c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread; 11007c478bd9Sstevel@tonic-gate conn->x.in.pos += nread; 1101159d09a2SMark Phalan /* Solaris Kerberos */ 11027c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) { 11037c478bd9Sstevel@tonic-gate /* We win! */ 11047c478bd9Sstevel@tonic-gate return 1; 11057c478bd9Sstevel@tonic-gate } 11067c478bd9Sstevel@tonic-gate } else { 11077c478bd9Sstevel@tonic-gate /* Reading length. */ 11087c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, 11097c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 11107c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read); 11117c478bd9Sstevel@tonic-gate if (nread < 0) { 11127c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 11137c478bd9Sstevel@tonic-gate goto kill_conn; 11147c478bd9Sstevel@tonic-gate } 11157c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread; 11167c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 11177c478bd9Sstevel@tonic-gate unsigned long len; 11187c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0]; 11197c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1]; 11207c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2]; 11217c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3]; 11227c478bd9Sstevel@tonic-gate /*LINTED*/ 11237c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len); 11247c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */ 11257c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) { 11267c478bd9Sstevel@tonic-gate e = E2BIG; 11277c478bd9Sstevel@tonic-gate goto kill_conn; 11287c478bd9Sstevel@tonic-gate } 11297c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len; 11307c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len); 11317c478bd9Sstevel@tonic-gate /*LINTED*/ 11327c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len, 11337c478bd9Sstevel@tonic-gate conn->x.in.buf); 11347c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) { 11357c478bd9Sstevel@tonic-gate /* allocation failure */ 11367c478bd9Sstevel@tonic-gate e = errno; 11377c478bd9Sstevel@tonic-gate goto kill_conn; 11387c478bd9Sstevel@tonic-gate } 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate } 11417c478bd9Sstevel@tonic-gate break; 11427c478bd9Sstevel@tonic-gate 11437c478bd9Sstevel@tonic-gate default: 11447c478bd9Sstevel@tonic-gate abort(); 11457c478bd9Sstevel@tonic-gate } 11467c478bd9Sstevel@tonic-gate return 0; 11477c478bd9Sstevel@tonic-gate } 11487c478bd9Sstevel@tonic-gate 11497c478bd9Sstevel@tonic-gate static int 11507c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate, 11517c478bd9Sstevel@tonic-gate int ssflags) 11527c478bd9Sstevel@tonic-gate { 11537c478bd9Sstevel@tonic-gate int nread; 11547c478bd9Sstevel@tonic-gate 11557c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) 11567c478bd9Sstevel@tonic-gate abort(); 11577c478bd9Sstevel@tonic-gate if (conn->state != READING) 11587c478bd9Sstevel@tonic-gate abort(); 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); 11617c478bd9Sstevel@tonic-gate if (nread < 0) { 11627c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO); 11637c478bd9Sstevel@tonic-gate return 0; 11647c478bd9Sstevel@tonic-gate } 11657c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread; 11667c478bd9Sstevel@tonic-gate return 1; 11677c478bd9Sstevel@tonic-gate } 11687c478bd9Sstevel@tonic-gate 11697c478bd9Sstevel@tonic-gate static int 1170159d09a2SMark Phalan service_fds (krb5_context context, 1171159d09a2SMark Phalan struct select_state *selstate, 1172159d09a2SMark Phalan struct conn_state *conns, size_t n_conns, int *winning_conn, 1173159d09a2SMark Phalan struct select_state *seltemp, 1174159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1175159d09a2SMark Phalan void *msg_handler_data) 11767c478bd9Sstevel@tonic-gate { 11777c478bd9Sstevel@tonic-gate int e, selret; 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate e = 0; 11807c478bd9Sstevel@tonic-gate while (selstate->nfds > 0 1181159d09a2SMark Phalan && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) { 11827c478bd9Sstevel@tonic-gate int i; 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate /*LINTED*/ 11857c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret); 11867c478bd9Sstevel@tonic-gate 11877c478bd9Sstevel@tonic-gate if (selret == 0) 11887c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */ 11897c478bd9Sstevel@tonic-gate return 0; 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */ 11927c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) { 11937c478bd9Sstevel@tonic-gate int ssflags; 11947c478bd9Sstevel@tonic-gate 11957c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET) 11967c478bd9Sstevel@tonic-gate continue; 11977c478bd9Sstevel@tonic-gate ssflags = 0; 1198159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->rfds)) 11997c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--; 1200159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->wfds)) 12017c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--; 1202159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->xfds)) 12037c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--; 12047c478bd9Sstevel@tonic-gate if (!ssflags) 12057c478bd9Sstevel@tonic-gate continue; 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate /*LINTED*/ 12087c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n", 12097c478bd9Sstevel@tonic-gate /*LINTED*/ 12107c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "", 12117c478bd9Sstevel@tonic-gate /*LINTED*/ 12127c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "", 12137c478bd9Sstevel@tonic-gate /*LINTED*/ 12147c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "", 12157c478bd9Sstevel@tonic-gate /*LINTED*/ 12167c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr, 12177c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]); 12187c478bd9Sstevel@tonic-gate 12197c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) { 1220159d09a2SMark Phalan int stop = 1; 1221159d09a2SMark Phalan 1222159d09a2SMark Phalan if (msg_handler != NULL) { 1223159d09a2SMark Phalan krb5_data reply; 1224159d09a2SMark Phalan 1225159d09a2SMark Phalan reply.data = conns[i].x.in.buf; 1226159d09a2SMark Phalan reply.length = conns[i].x.in.pos - conns[i].x.in.buf; 1227159d09a2SMark Phalan 1228159d09a2SMark Phalan stop = (msg_handler(context, &reply, msg_handler_data) != 0); 1229159d09a2SMark Phalan } 1230159d09a2SMark Phalan 1231159d09a2SMark Phalan if (stop) { 1232159d09a2SMark Phalan dprint("fd service routine says we're done\n"); 1233159d09a2SMark Phalan *winning_conn = i; 1234159d09a2SMark Phalan return 1; 1235159d09a2SMark Phalan } 12367c478bd9Sstevel@tonic-gate } 12377c478bd9Sstevel@tonic-gate } 12387c478bd9Sstevel@tonic-gate } 12397c478bd9Sstevel@tonic-gate if (e != 0) { 12407c478bd9Sstevel@tonic-gate /*LINTED*/ 12417c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e); 12427c478bd9Sstevel@tonic-gate *winning_conn = -1; 12437c478bd9Sstevel@tonic-gate return 1; 12447c478bd9Sstevel@tonic-gate } 12457c478bd9Sstevel@tonic-gate return 0; 12467c478bd9Sstevel@tonic-gate } 12477c478bd9Sstevel@tonic-gate 12487c478bd9Sstevel@tonic-gate /* 12497c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior: 12507c478bd9Sstevel@tonic-gate * 12517c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end. 12527c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s. 12537c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s. 12547c478bd9Sstevel@tonic-gate * Fourth => 16s, etc. 12557c478bd9Sstevel@tonic-gate * 12567c478bd9Sstevel@tonic-gate * Restated: 12577c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass. 12587c478bd9Sstevel@tonic-gate * Per TCP server, 1s. 12597c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes. 12607c478bd9Sstevel@tonic-gate * 12617c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2. 12627c478bd9Sstevel@tonic-gate * 12637c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14. 12647c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30. 12657c478bd9Sstevel@tonic-gate * 12667c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on 12677c478bd9Sstevel@tonic-gate * one server, it counts as two. 12687c478bd9Sstevel@tonic-gate */ 12697c478bd9Sstevel@tonic-gate 12707c478bd9Sstevel@tonic-gate krb5_error_code 12717c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 12727c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 1273159d09a2SMark Phalan const struct addrlist *addrs, 1274159d09a2SMark Phalan struct sendto_callback_info* callback_info, krb5_data *reply, 1275159d09a2SMark Phalan struct sockaddr *localaddr, socklen_t *localaddrlen, 1276159d09a2SMark Phalan struct sockaddr *remoteaddr, socklen_t *remoteaddrlen, 1277159d09a2SMark Phalan int *addr_used, 1278159d09a2SMark Phalan /* return 0 -> keep going, 1 -> quit */ 1279159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *), 1280159d09a2SMark Phalan void *msg_handler_data) 12817c478bd9Sstevel@tonic-gate { 12827c478bd9Sstevel@tonic-gate int i, pass; 12837c478bd9Sstevel@tonic-gate int delay_this_pass = 2; 12847c478bd9Sstevel@tonic-gate krb5_error_code retval; 12857c478bd9Sstevel@tonic-gate struct conn_state *conns; 1286159d09a2SMark Phalan krb5_data *callback_data = 0; 12877c478bd9Sstevel@tonic-gate size_t n_conns, host; 1288159d09a2SMark Phalan struct select_state *sel_state; 12897c478bd9Sstevel@tonic-gate struct timeval now; 12907c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0; 12917c478bd9Sstevel@tonic-gate char *udpbuf = 0; 12927c478bd9Sstevel@tonic-gate 1293159d09a2SMark Phalan if (message) 1294159d09a2SMark Phalan dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data); 1295159d09a2SMark Phalan else 1296159d09a2SMark Phalan dprint("krb5int_sendto(callback=%p, addrlist=", callback_info); 1297159d09a2SMark Phalan print_addrlist(addrs); 1298159d09a2SMark Phalan dprint(")\n"); 12997c478bd9Sstevel@tonic-gate 13007c478bd9Sstevel@tonic-gate reply->data = 0; 13017c478bd9Sstevel@tonic-gate reply->length = 0; 13027c478bd9Sstevel@tonic-gate 13037c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs; 13047c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state)); 13057c478bd9Sstevel@tonic-gate if (conns == NULL) { 13067c478bd9Sstevel@tonic-gate return ENOMEM; 13077c478bd9Sstevel@tonic-gate } 1308159d09a2SMark Phalan 1309159d09a2SMark Phalan memset(conns, 0, n_conns * sizeof(struct conn_state)); 1310159d09a2SMark Phalan 1311159d09a2SMark Phalan if (callback_info) { 1312159d09a2SMark Phalan callback_data = malloc(n_conns * sizeof(krb5_data)); 1313159d09a2SMark Phalan if (callback_data == NULL) { 1314159d09a2SMark Phalan return ENOMEM; 1315159d09a2SMark Phalan } 1316159d09a2SMark Phalan 1317159d09a2SMark Phalan memset(callback_data, 0, n_conns * sizeof(krb5_data)); 1318159d09a2SMark Phalan } 1319159d09a2SMark Phalan 13207c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 13217c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET; 13227c478bd9Sstevel@tonic-gate } 13237c478bd9Sstevel@tonic-gate 1324159d09a2SMark Phalan /* One for use here, listing all our fds in use, and one for 1325159d09a2SMark Phalan temporary use in service_fds, for the fds of interest. */ 1326159d09a2SMark Phalan sel_state = malloc(2 * sizeof(*sel_state)); 1327159d09a2SMark Phalan if (sel_state == NULL) { 1328159d09a2SMark Phalan free(conns); 1329159d09a2SMark Phalan return ENOMEM; 1330159d09a2SMark Phalan } 1331159d09a2SMark Phalan sel_state->max = 0; 1332159d09a2SMark Phalan sel_state->nfds = 0; 1333159d09a2SMark Phalan sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0; 1334159d09a2SMark Phalan FD_ZERO(&sel_state->rfds); 1335159d09a2SMark Phalan FD_ZERO(&sel_state->wfds); 1336159d09a2SMark Phalan FD_ZERO(&sel_state->xfds); 13377c478bd9Sstevel@tonic-gate 13387c478bd9Sstevel@tonic-gate 13397c478bd9Sstevel@tonic-gate /* Set up connections. */ 13407c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 1341159d09a2SMark Phalan retval = setup_connection(&conns[host], 1342159d09a2SMark Phalan addrs->addrs[host].ai, 1343159d09a2SMark Phalan message, 1344159d09a2SMark Phalan &udpbuf); 13457c478bd9Sstevel@tonic-gate if (retval) 13467c478bd9Sstevel@tonic-gate continue; 13477c478bd9Sstevel@tonic-gate } 13487c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) { 13497c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only. 13507c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */ 13517c478bd9Sstevel@tonic-gate /*LINTED*/ 13527c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass); 13537c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 13547c478bd9Sstevel@tonic-gate /*LINTED*/ 13557c478bd9Sstevel@tonic-gate dprint("host %d\n", host); 13567c478bd9Sstevel@tonic-gate 13577c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */ 1358159d09a2SMark Phalan if (maybe_send(&conns[host], 1359159d09a2SMark Phalan sel_state, 1360159d09a2SMark Phalan callback_info, 1361159d09a2SMark Phalan (callback_info ? &callback_data[host] : NULL))) 13627c478bd9Sstevel@tonic-gate continue; 13637c478bd9Sstevel@tonic-gate 13647c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13657c478bd9Sstevel@tonic-gate if (retval) 13667c478bd9Sstevel@tonic-gate goto egress; 1367159d09a2SMark Phalan sel_state->end_time = now; 1368159d09a2SMark Phalan sel_state->end_time.tv_sec += 1; 1369159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1370159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13717c478bd9Sstevel@tonic-gate if (e) 13727c478bd9Sstevel@tonic-gate break; 1373159d09a2SMark Phalan if (pass > 0 && sel_state->nfds == 0) 13747c478bd9Sstevel@tonic-gate /* 13757c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break 13767c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay, 13777c478bd9Sstevel@tonic-gate * we're probably about to open another connection. 13787c478bd9Sstevel@tonic-gate */ 13797c478bd9Sstevel@tonic-gate break; 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate if (e) 13827c478bd9Sstevel@tonic-gate break; 13837c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 13847c478bd9Sstevel@tonic-gate if (retval) 13857c478bd9Sstevel@tonic-gate goto egress; 13867c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select 13877c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop 13887c478bd9Sstevel@tonic-gate actually calls select. */ 1389159d09a2SMark Phalan sel_state->end_time.tv_sec += delay_this_pass; 1390159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn, 1391159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data); 13927c478bd9Sstevel@tonic-gate if (e) 13937c478bd9Sstevel@tonic-gate break; 1394159d09a2SMark Phalan if (sel_state->nfds == 0) 13957c478bd9Sstevel@tonic-gate break; 13967c478bd9Sstevel@tonic-gate delay_this_pass *= 2; 13977c478bd9Sstevel@tonic-gate } 13987c478bd9Sstevel@tonic-gate 1399159d09a2SMark Phalan if (sel_state->nfds == 0) { 14007c478bd9Sstevel@tonic-gate /* No addresses? */ 14017c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 14027c478bd9Sstevel@tonic-gate goto egress; 14037c478bd9Sstevel@tonic-gate } 14047c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) { 14057c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 14067c478bd9Sstevel@tonic-gate goto egress; 14077c478bd9Sstevel@tonic-gate } 14087c478bd9Sstevel@tonic-gate /* Success! */ 14097c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf; 14107c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos 14117c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf); 14127c478bd9Sstevel@tonic-gate /*LINTED*/ 1413159d09a2SMark Phalan dprint("returning %d bytes in buffer %p\n", 1414159d09a2SMark Phalan (int) reply->length, reply->data); 14157c478bd9Sstevel@tonic-gate retval = 0; 14167c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0; 1417505d05c7Sgtb if (addr_used) 1418159d09a2SMark Phalan *addr_used = winning_conn; 14197c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0) 1420159d09a2SMark Phalan (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen); 1421159d09a2SMark Phalan 1422159d09a2SMark Phalan if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0) 1423159d09a2SMark Phalan (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen); 1424159d09a2SMark Phalan 14257c478bd9Sstevel@tonic-gate egress: 14267c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 14277c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET) 1428159d09a2SMark Phalan closesocket(conns[i].fd); 14297c478bd9Sstevel@tonic-gate if (conns[i].state == READING 14307c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0 14317c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf) 14327c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf); 1433159d09a2SMark Phalan if (callback_info) { 1434159d09a2SMark Phalan callback_info->pfn_cleanup( callback_info->context, &callback_data[i]); 1435159d09a2SMark Phalan } 14367c478bd9Sstevel@tonic-gate } 1437159d09a2SMark Phalan 1438159d09a2SMark Phalan if (callback_data) 1439159d09a2SMark Phalan free(callback_data); 1440159d09a2SMark Phalan 14417c478bd9Sstevel@tonic-gate free(conns); 14427c478bd9Sstevel@tonic-gate if (reply->data != udpbuf) 14437c478bd9Sstevel@tonic-gate free(udpbuf); 1444159d09a2SMark Phalan free(sel_state); 14457c478bd9Sstevel@tonic-gate return retval; 14467c478bd9Sstevel@tonic-gate } 1447