1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 4*7c478bd9Sstevel@tonic-gate */ 5*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 6*7c478bd9Sstevel@tonic-gate /* 7*7c478bd9Sstevel@tonic-gate * lib/krb5/os/sendto_kdc.c 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * Copyright 1990,1991,2001,2002 by the Massachusetts Institute of Technology. 10*7c478bd9Sstevel@tonic-gate * All Rights Reserved. 11*7c478bd9Sstevel@tonic-gate * 12*7c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 13*7c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 14*7c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 15*7c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 16*7c478bd9Sstevel@tonic-gate * 17*7c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18*7c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 19*7c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 20*7c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 21*7c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 22*7c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 23*7c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 24*7c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 25*7c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 26*7c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 27*7c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 28*7c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 29*7c478bd9Sstevel@tonic-gate * or implied warranty. 30*7c478bd9Sstevel@tonic-gate * 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * Send packet to KDC for realm; wait for response, retransmitting 33*7c478bd9Sstevel@tonic-gate * as necessary. 34*7c478bd9Sstevel@tonic-gate */ 35*7c478bd9Sstevel@tonic-gate 36*7c478bd9Sstevel@tonic-gate #define NEED_SOCKETS 37*7c478bd9Sstevel@tonic-gate #define NEED_LOWLEVEL_IO 38*7c478bd9Sstevel@tonic-gate #include <fake-addrinfo.h> 39*7c478bd9Sstevel@tonic-gate #include <k5-int.h> 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H 42*7c478bd9Sstevel@tonic-gate #include <sys/time.h> 43*7c478bd9Sstevel@tonic-gate #else 44*7c478bd9Sstevel@tonic-gate #include <time.h> 45*7c478bd9Sstevel@tonic-gate #endif 46*7c478bd9Sstevel@tonic-gate #include "os-proto.h" 47*7c478bd9Sstevel@tonic-gate 48*7c478bd9Sstevel@tonic-gate #ifdef _AIX 49*7c478bd9Sstevel@tonic-gate #include <sys/select.h> 50*7c478bd9Sstevel@tonic-gate #endif 51*7c478bd9Sstevel@tonic-gate 52*7c478bd9Sstevel@tonic-gate /* For FIONBIO. */ 53*7c478bd9Sstevel@tonic-gate #include <sys/ioctl.h> 54*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H 55*7c478bd9Sstevel@tonic-gate #include <sys/filio.h> 56*7c478bd9Sstevel@tonic-gate #endif 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate #define MAX_PASS 3 59*7c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */ 60*7c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT 1465 */ 61*7c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */ 62*7c478bd9Sstevel@tonic-gate 63*7c478bd9Sstevel@tonic-gate extern krb5_error_code 64*7c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 65*7c478bd9Sstevel@tonic-gate const struct addrlist *addrs, krb5_data *reply, 66*7c478bd9Sstevel@tonic-gate struct sockaddr_storage *localaddr, socklen_t *localaddrlen); 67*7c478bd9Sstevel@tonic-gate 68*7c478bd9Sstevel@tonic-gate /* Solaris kerberos: leaving this here because other code depends on this. */ 69*7c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len) 70*7c478bd9Sstevel@tonic-gate { 71*7c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr); 72*7c478bd9Sstevel@tonic-gate /* stderr is unbuffered */ 73*7c478bd9Sstevel@tonic-gate } 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler; 76*7c478bd9Sstevel@tonic-gate 77*7c478bd9Sstevel@tonic-gate /* 78*7c478bd9Sstevel@tonic-gate * Solaris Kerberos: only including the debug stuff if DEBUG defined outside 79*7c478bd9Sstevel@tonic-gate * this file. 80*7c478bd9Sstevel@tonic-gate */ 81*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 82*7c478bd9Sstevel@tonic-gate 83*7c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024]; 84*7c478bd9Sstevel@tonic-gate 85*7c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */ 86*7c478bd9Sstevel@tonic-gate 87*7c478bd9Sstevel@tonic-gate static void putstr(const char *str) 88*7c478bd9Sstevel@tonic-gate { 89*7c478bd9Sstevel@tonic-gate /* Solaris kerberos: build the string which will be passed to syslog later */ 90*7c478bd9Sstevel@tonic-gate strlcat(global_err_str, str, sizeof (global_err_str)); 91*7c478bd9Sstevel@tonic-gate } 92*7c478bd9Sstevel@tonic-gate 93*7c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint 94*7c478bd9Sstevel@tonic-gate #define dperror dprint 95*7c478bd9Sstevel@tonic-gate 96*7c478bd9Sstevel@tonic-gate #include <com_err.h> 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate static void 99*7c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...) 100*7c478bd9Sstevel@tonic-gate { 101*7c478bd9Sstevel@tonic-gate va_list args; 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate /* Temporaries for variable arguments, etc. */ 104*7c478bd9Sstevel@tonic-gate krb5_error_code kerr; 105*7c478bd9Sstevel@tonic-gate int err; 106*7c478bd9Sstevel@tonic-gate fd_set *rfds, *wfds, *xfds; 107*7c478bd9Sstevel@tonic-gate int i; 108*7c478bd9Sstevel@tonic-gate int maxfd; 109*7c478bd9Sstevel@tonic-gate struct timeval *tv; 110*7c478bd9Sstevel@tonic-gate struct addrinfo *ai; 111*7c478bd9Sstevel@tonic-gate const krb5_data *d; 112*7c478bd9Sstevel@tonic-gate char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 113*7c478bd9Sstevel@tonic-gate const char *p; 114*7c478bd9Sstevel@tonic-gate char tmpbuf[NI_MAXHOST + NI_MAXSERV + 30]; 115*7c478bd9Sstevel@tonic-gate 116*7c478bd9Sstevel@tonic-gate /* 117*7c478bd9Sstevel@tonic-gate * Solaris kerberos: modified this function to create a string to pass to 118*7c478bd9Sstevel@tonic-gate * syslog() 119*7c478bd9Sstevel@tonic-gate */ 120*7c478bd9Sstevel@tonic-gate global_err_str[0] = NULL; 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate va_start(args, fmt); 123*7c478bd9Sstevel@tonic-gate 124*7c478bd9Sstevel@tonic-gate #define putf(FMT,X) (sprintf(tmpbuf,FMT,X),putstr(tmpbuf)) 125*7c478bd9Sstevel@tonic-gate 126*7c478bd9Sstevel@tonic-gate for (; *fmt; fmt++) { 127*7c478bd9Sstevel@tonic-gate if (*fmt != '%') { 128*7c478bd9Sstevel@tonic-gate /* Possible optimization: Look for % and print all chars 129*7c478bd9Sstevel@tonic-gate up to it in one call. */ 130*7c478bd9Sstevel@tonic-gate putf("%c", *fmt); 131*7c478bd9Sstevel@tonic-gate continue; 132*7c478bd9Sstevel@tonic-gate } 133*7c478bd9Sstevel@tonic-gate /* After this, always processing a '%' sequence. */ 134*7c478bd9Sstevel@tonic-gate fmt++; 135*7c478bd9Sstevel@tonic-gate switch (*fmt) { 136*7c478bd9Sstevel@tonic-gate case 0: 137*7c478bd9Sstevel@tonic-gate default: 138*7c478bd9Sstevel@tonic-gate abort(); 139*7c478bd9Sstevel@tonic-gate case 'E': 140*7c478bd9Sstevel@tonic-gate /* %E => krb5_error_code */ 141*7c478bd9Sstevel@tonic-gate kerr = va_arg(args, krb5_error_code); 142*7c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%lu/", (unsigned long) kerr); 143*7c478bd9Sstevel@tonic-gate putstr(tmpbuf); 144*7c478bd9Sstevel@tonic-gate p = error_message(kerr); 145*7c478bd9Sstevel@tonic-gate putstr(p); 146*7c478bd9Sstevel@tonic-gate break; 147*7c478bd9Sstevel@tonic-gate case 'm': 148*7c478bd9Sstevel@tonic-gate /* %m => errno value (int) */ 149*7c478bd9Sstevel@tonic-gate /* Like syslog's %m except the errno value is passed in 150*7c478bd9Sstevel@tonic-gate rather than the current value. */ 151*7c478bd9Sstevel@tonic-gate err = va_arg(args, int); 152*7c478bd9Sstevel@tonic-gate putf("%d/", err); 153*7c478bd9Sstevel@tonic-gate p = strerror(err); 154*7c478bd9Sstevel@tonic-gate putstr(p); 155*7c478bd9Sstevel@tonic-gate break; 156*7c478bd9Sstevel@tonic-gate case 'F': 157*7c478bd9Sstevel@tonic-gate /* %F => fd_set *, fd_set *, fd_set *, int */ 158*7c478bd9Sstevel@tonic-gate rfds = va_arg(args, fd_set *); 159*7c478bd9Sstevel@tonic-gate wfds = va_arg(args, fd_set *); 160*7c478bd9Sstevel@tonic-gate xfds = va_arg(args, fd_set *); 161*7c478bd9Sstevel@tonic-gate maxfd = va_arg(args, int); 162*7c478bd9Sstevel@tonic-gate 163*7c478bd9Sstevel@tonic-gate for (i = 0; i < maxfd; i++) { 164*7c478bd9Sstevel@tonic-gate int r = FD_ISSET(i, rfds); 165*7c478bd9Sstevel@tonic-gate int w = wfds && FD_ISSET(i, wfds); 166*7c478bd9Sstevel@tonic-gate int x = xfds && FD_ISSET(i, xfds); 167*7c478bd9Sstevel@tonic-gate if (r || w || x) { 168*7c478bd9Sstevel@tonic-gate putf(" %d", i); 169*7c478bd9Sstevel@tonic-gate if (r) 170*7c478bd9Sstevel@tonic-gate putstr("r"); 171*7c478bd9Sstevel@tonic-gate if (w) 172*7c478bd9Sstevel@tonic-gate putstr("w"); 173*7c478bd9Sstevel@tonic-gate if (x) 174*7c478bd9Sstevel@tonic-gate putstr("x"); 175*7c478bd9Sstevel@tonic-gate } 176*7c478bd9Sstevel@tonic-gate } 177*7c478bd9Sstevel@tonic-gate putstr(" "); 178*7c478bd9Sstevel@tonic-gate break; 179*7c478bd9Sstevel@tonic-gate case 's': 180*7c478bd9Sstevel@tonic-gate /* %s => char * */ 181*7c478bd9Sstevel@tonic-gate p = va_arg(args, const char *); 182*7c478bd9Sstevel@tonic-gate putstr(p); 183*7c478bd9Sstevel@tonic-gate break; 184*7c478bd9Sstevel@tonic-gate case 't': 185*7c478bd9Sstevel@tonic-gate /* %t => struct timeval * */ 186*7c478bd9Sstevel@tonic-gate tv = va_arg(args, struct timeval *); 187*7c478bd9Sstevel@tonic-gate if (tv) { 188*7c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%ld.%06ld", 189*7c478bd9Sstevel@tonic-gate (long) tv->tv_sec, (long) tv->tv_usec); 190*7c478bd9Sstevel@tonic-gate putstr(tmpbuf); 191*7c478bd9Sstevel@tonic-gate } else 192*7c478bd9Sstevel@tonic-gate putstr("never"); 193*7c478bd9Sstevel@tonic-gate break; 194*7c478bd9Sstevel@tonic-gate case 'd': 195*7c478bd9Sstevel@tonic-gate /* %d => int */ 196*7c478bd9Sstevel@tonic-gate putf("%d", va_arg(args, int)); 197*7c478bd9Sstevel@tonic-gate break; 198*7c478bd9Sstevel@tonic-gate case 'p': 199*7c478bd9Sstevel@tonic-gate /* %p => pointer */ 200*7c478bd9Sstevel@tonic-gate putf("%p", va_arg(args, void*)); 201*7c478bd9Sstevel@tonic-gate break; 202*7c478bd9Sstevel@tonic-gate case 'A': 203*7c478bd9Sstevel@tonic-gate /* %A => addrinfo */ 204*7c478bd9Sstevel@tonic-gate ai = va_arg(args, struct addrinfo *); 205*7c478bd9Sstevel@tonic-gate if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen, 206*7c478bd9Sstevel@tonic-gate addrbuf, sizeof (addrbuf), 207*7c478bd9Sstevel@tonic-gate portbuf, sizeof (portbuf), 208*7c478bd9Sstevel@tonic-gate NI_NUMERICHOST | NI_NUMERICSERV)) 209*7c478bd9Sstevel@tonic-gate strcpy (addrbuf, "??"), strcpy (portbuf, "??"); 210*7c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%s %s.%s", 211*7c478bd9Sstevel@tonic-gate (ai->ai_socktype == SOCK_DGRAM 212*7c478bd9Sstevel@tonic-gate ? "udp" 213*7c478bd9Sstevel@tonic-gate : ai->ai_socktype == SOCK_STREAM 214*7c478bd9Sstevel@tonic-gate ? "tcp" 215*7c478bd9Sstevel@tonic-gate : "???"), 216*7c478bd9Sstevel@tonic-gate addrbuf, portbuf); 217*7c478bd9Sstevel@tonic-gate putstr(tmpbuf); 218*7c478bd9Sstevel@tonic-gate break; 219*7c478bd9Sstevel@tonic-gate case 'D': 220*7c478bd9Sstevel@tonic-gate /* %D => krb5_data * */ 221*7c478bd9Sstevel@tonic-gate d = va_arg(args, krb5_data *); 222*7c478bd9Sstevel@tonic-gate p = d->data; 223*7c478bd9Sstevel@tonic-gate putstr("0x"); 224*7c478bd9Sstevel@tonic-gate for (i = 0; i < d->length; i++) { 225*7c478bd9Sstevel@tonic-gate putf("%.2x", *p++); 226*7c478bd9Sstevel@tonic-gate } 227*7c478bd9Sstevel@tonic-gate break; 228*7c478bd9Sstevel@tonic-gate } 229*7c478bd9Sstevel@tonic-gate } 230*7c478bd9Sstevel@tonic-gate va_end(args); 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate /* Solaris kerberos: use syslog() for debug output */ 233*7c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, global_err_str); 234*7c478bd9Sstevel@tonic-gate } 235*7c478bd9Sstevel@tonic-gate 236*7c478bd9Sstevel@tonic-gate #else 237*7c478bd9Sstevel@tonic-gate #define dprint (void) 238*7c478bd9Sstevel@tonic-gate #define dperror(MSG) ((void)(MSG)) 239*7c478bd9Sstevel@tonic-gate #endif 240*7c478bd9Sstevel@tonic-gate 241*7c478bd9Sstevel@tonic-gate static int 242*7c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src) 243*7c478bd9Sstevel@tonic-gate { 244*7c478bd9Sstevel@tonic-gate int err, i; 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 247*7c478bd9Sstevel@tonic-gate /*LINTED*/ 248*7c478bd9Sstevel@tonic-gate dprint("merging addrlists:\n\tlist1: "); 249*7c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 250*7c478bd9Sstevel@tonic-gate /*LINTED*/ 251*7c478bd9Sstevel@tonic-gate dprint(" %A", dest->addrs[i]); 252*7c478bd9Sstevel@tonic-gate /*LINTED*/ 253*7c478bd9Sstevel@tonic-gate dprint("\n\tlist2: "); 254*7c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) 255*7c478bd9Sstevel@tonic-gate /*LINTED*/ 256*7c478bd9Sstevel@tonic-gate dprint(" %A", src->addrs[i]); 257*7c478bd9Sstevel@tonic-gate /*LINTED*/ 258*7c478bd9Sstevel@tonic-gate dprint("\n"); 259*7c478bd9Sstevel@tonic-gate #endif 260*7c478bd9Sstevel@tonic-gate 261*7c478bd9Sstevel@tonic-gate err = krb5int_grow_addrlist (dest, src->naddrs); 262*7c478bd9Sstevel@tonic-gate if (err) 263*7c478bd9Sstevel@tonic-gate return err; 264*7c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) { 265*7c478bd9Sstevel@tonic-gate dest->addrs[dest->naddrs + i] = src->addrs[i]; 266*7c478bd9Sstevel@tonic-gate src->addrs[i] = 0; 267*7c478bd9Sstevel@tonic-gate } 268*7c478bd9Sstevel@tonic-gate dest->naddrs += i; 269*7c478bd9Sstevel@tonic-gate src->naddrs = 0; 270*7c478bd9Sstevel@tonic-gate 271*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 272*7c478bd9Sstevel@tonic-gate /*LINTED*/ 273*7c478bd9Sstevel@tonic-gate dprint("\tout: "); 274*7c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++) 275*7c478bd9Sstevel@tonic-gate /*LINTED*/ 276*7c478bd9Sstevel@tonic-gate dprint(" %A", dest->addrs[i]); 277*7c478bd9Sstevel@tonic-gate /*LINTED*/ 278*7c478bd9Sstevel@tonic-gate dprint("\n"); 279*7c478bd9Sstevel@tonic-gate #endif 280*7c478bd9Sstevel@tonic-gate 281*7c478bd9Sstevel@tonic-gate return 0; 282*7c478bd9Sstevel@tonic-gate } 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate /* 285*7c478bd9Sstevel@tonic-gate * send the formatted request 'message' to a KDC for realm 'realm' and 286*7c478bd9Sstevel@tonic-gate * return the response (if any) in 'reply'. 287*7c478bd9Sstevel@tonic-gate * 288*7c478bd9Sstevel@tonic-gate * If the message is sent and a response is received, 0 is returned, 289*7c478bd9Sstevel@tonic-gate * otherwise an error code is returned. 290*7c478bd9Sstevel@tonic-gate * 291*7c478bd9Sstevel@tonic-gate * The storage for 'reply' is allocated and should be freed by the caller 292*7c478bd9Sstevel@tonic-gate * when finished. 293*7c478bd9Sstevel@tonic-gate */ 294*7c478bd9Sstevel@tonic-gate 295*7c478bd9Sstevel@tonic-gate krb5_error_code 296*7c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message, 297*7c478bd9Sstevel@tonic-gate const krb5_data *realm, krb5_data *reply, 298*7c478bd9Sstevel@tonic-gate int use_master, int tcp_only) 299*7c478bd9Sstevel@tonic-gate { 300*7c478bd9Sstevel@tonic-gate krb5_error_code retval; 301*7c478bd9Sstevel@tonic-gate struct addrlist addrs; 302*7c478bd9Sstevel@tonic-gate int socktype1 = 0, socktype2 = 0; 303*7c478bd9Sstevel@tonic-gate 304*7c478bd9Sstevel@tonic-gate /* 305*7c478bd9Sstevel@tonic-gate * find KDC location(s) for realm 306*7c478bd9Sstevel@tonic-gate */ 307*7c478bd9Sstevel@tonic-gate 308*7c478bd9Sstevel@tonic-gate /* 309*7c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem, 310*7c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be 311*7c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or 312*7c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also, 313*7c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} 314*7c478bd9Sstevel@tonic-gate * should probably be returned as well. 315*7c478bd9Sstevel@tonic-gate */ 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate /*LINTED*/ 318*7c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", 319*7c478bd9Sstevel@tonic-gate /*LINTED*/ 320*7c478bd9Sstevel@tonic-gate message->length, message->data, realm, use_master, tcp_only); 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate /* 323*7c478bd9Sstevel@tonic-gate * Solaris Kerberos: keep it simple by not supporting a udp_preference_limit 324*7c478bd9Sstevel@tonic-gate */ 325*7c478bd9Sstevel@tonic-gate #if 0 /************** Begin IFDEF'ed OUT *******************************/ 326*7c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) { 327*7c478bd9Sstevel@tonic-gate int tmp; 328*7c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile, 329*7c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0, 330*7c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp); 331*7c478bd9Sstevel@tonic-gate if (retval) 332*7c478bd9Sstevel@tonic-gate return retval; 333*7c478bd9Sstevel@tonic-gate if (tmp < 0) 334*7c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT; 335*7c478bd9Sstevel@tonic-gate else if (tmp > HARD_UDP_LIMIT) { 336*7c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is 337*7c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can 338*7c478bd9Sstevel@tonic-gate support. */ 339*7c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT; 340*7c478bd9Sstevel@tonic-gate } 341*7c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp; 342*7c478bd9Sstevel@tonic-gate } 343*7c478bd9Sstevel@tonic-gate #endif /**************** END IFDEF'ed OUT *******************************/ 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate retval = (use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); 346*7c478bd9Sstevel@tonic-gate 347*7c478bd9Sstevel@tonic-gate if (tcp_only) 348*7c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0; 349*7c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit) 350*7c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; 351*7c478bd9Sstevel@tonic-gate else 352*7c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; 353*7c478bd9Sstevel@tonic-gate 354*7c478bd9Sstevel@tonic-gate retval = krb5_locate_kdc(context, realm, &addrs, use_master, socktype1, 0); 355*7c478bd9Sstevel@tonic-gate if (socktype2) { 356*7c478bd9Sstevel@tonic-gate struct addrlist addrs2; 357*7c478bd9Sstevel@tonic-gate 358*7c478bd9Sstevel@tonic-gate retval = krb5_locate_kdc(context, realm, &addrs2, use_master, 359*7c478bd9Sstevel@tonic-gate socktype2, 0); 360*7c478bd9Sstevel@tonic-gate if (retval == 0) { 361*7c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2); 362*7c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2); 363*7c478bd9Sstevel@tonic-gate } 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) { 366*7c478bd9Sstevel@tonic-gate retval = krb5int_sendto (context, message, &addrs, reply, 0, 0); 367*7c478bd9Sstevel@tonic-gate krb5int_free_addrlist (&addrs); 368*7c478bd9Sstevel@tonic-gate if (retval == 0) 369*7c478bd9Sstevel@tonic-gate return 0; 370*7c478bd9Sstevel@tonic-gate } 371*7c478bd9Sstevel@tonic-gate return retval; 372*7c478bd9Sstevel@tonic-gate } 373*7c478bd9Sstevel@tonic-gate 374*7c478bd9Sstevel@tonic-gate 375*7c478bd9Sstevel@tonic-gate /* 376*7c478bd9Sstevel@tonic-gate * Notes: 377*7c478bd9Sstevel@tonic-gate * 378*7c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes 379*7c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up 380*7c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags 381*7c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it 382*7c478bd9Sstevel@tonic-gate * system-specific. 383*7c478bd9Sstevel@tonic-gate * 384*7c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually 385*7c478bd9Sstevel@tonic-gate * fix the UDP code to do the same. 386*7c478bd9Sstevel@tonic-gate * 387*7c478bd9Sstevel@tonic-gate * To do: 388*7c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options? 389*7c478bd9Sstevel@tonic-gate * - error codes that don't suck 390*7c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status 391*7c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP 392*7c478bd9Sstevel@tonic-gate * connections already in progress 393*7c478bd9Sstevel@tonic-gate */ 394*7c478bd9Sstevel@tonic-gate 395*7c478bd9Sstevel@tonic-gate #include <cm.h> 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate static const char *const state_strings[] = { 398*7c478bd9Sstevel@tonic-gate "INITIALIZING", "CONNECTING", "WRITING", "READING", "FAILED" 399*7c478bd9Sstevel@tonic-gate }; 400*7c478bd9Sstevel@tonic-gate enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED }; 401*7c478bd9Sstevel@tonic-gate struct incoming_krb5_message { 402*7c478bd9Sstevel@tonic-gate size_t bufsizebytes_read; 403*7c478bd9Sstevel@tonic-gate size_t bufsize; 404*7c478bd9Sstevel@tonic-gate char *buf; 405*7c478bd9Sstevel@tonic-gate char *pos; 406*7c478bd9Sstevel@tonic-gate unsigned char bufsizebytes[4]; 407*7c478bd9Sstevel@tonic-gate size_t n_left; 408*7c478bd9Sstevel@tonic-gate }; 409*7c478bd9Sstevel@tonic-gate struct conn_state { 410*7c478bd9Sstevel@tonic-gate SOCKET fd; 411*7c478bd9Sstevel@tonic-gate krb5_error_code err; 412*7c478bd9Sstevel@tonic-gate enum conn_states state; 413*7c478bd9Sstevel@tonic-gate unsigned int is_udp : 1; 414*7c478bd9Sstevel@tonic-gate int (*service)(struct conn_state *, struct select_state *, int); 415*7c478bd9Sstevel@tonic-gate struct addrinfo *addr; 416*7c478bd9Sstevel@tonic-gate struct { 417*7c478bd9Sstevel@tonic-gate struct { 418*7c478bd9Sstevel@tonic-gate sg_buf sgbuf[2]; 419*7c478bd9Sstevel@tonic-gate sg_buf *sgp; 420*7c478bd9Sstevel@tonic-gate int sg_count; 421*7c478bd9Sstevel@tonic-gate } out; 422*7c478bd9Sstevel@tonic-gate struct incoming_krb5_message in; 423*7c478bd9Sstevel@tonic-gate } x; 424*7c478bd9Sstevel@tonic-gate }; 425*7c478bd9Sstevel@tonic-gate 426*7c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp) 427*7c478bd9Sstevel@tonic-gate { 428*7c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) { 429*7c478bd9Sstevel@tonic-gate dperror("gettimeofday"); 430*7c478bd9Sstevel@tonic-gate return errno; 431*7c478bd9Sstevel@tonic-gate } 432*7c478bd9Sstevel@tonic-gate return 0; 433*7c478bd9Sstevel@tonic-gate } 434*7c478bd9Sstevel@tonic-gate 435*7c478bd9Sstevel@tonic-gate /* 436*7c478bd9Sstevel@tonic-gate * Call select and return results. 437*7c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout 438*7c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets 439*7c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code. 440*7c478bd9Sstevel@tonic-gate */ 441*7c478bd9Sstevel@tonic-gate krb5_error_code 442*7c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in, 443*7c478bd9Sstevel@tonic-gate struct select_state *out, int *sret) 444*7c478bd9Sstevel@tonic-gate { 445*7c478bd9Sstevel@tonic-gate struct timeval now, *timo; 446*7c478bd9Sstevel@tonic-gate krb5_error_code e; 447*7c478bd9Sstevel@tonic-gate 448*7c478bd9Sstevel@tonic-gate *out = *in; 449*7c478bd9Sstevel@tonic-gate e = getcurtime(&now); 450*7c478bd9Sstevel@tonic-gate if (e) 451*7c478bd9Sstevel@tonic-gate return e; 452*7c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0) 453*7c478bd9Sstevel@tonic-gate timo = 0; 454*7c478bd9Sstevel@tonic-gate else { 455*7c478bd9Sstevel@tonic-gate timo = &out->end_time; 456*7c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec; 457*7c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec; 458*7c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) { 459*7c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000; 460*7c478bd9Sstevel@tonic-gate out->end_time.tv_sec--; 461*7c478bd9Sstevel@tonic-gate } 462*7c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) { 463*7c478bd9Sstevel@tonic-gate *sret = 0; 464*7c478bd9Sstevel@tonic-gate return 0; 465*7c478bd9Sstevel@tonic-gate } 466*7c478bd9Sstevel@tonic-gate } 467*7c478bd9Sstevel@tonic-gate /*LINTED*/ 468*7c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n", 469*7c478bd9Sstevel@tonic-gate /*LINTED*/ 470*7c478bd9Sstevel@tonic-gate out->max, &out->rfds, &out->wfds, &out->xfds, out->max, timo); 471*7c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo); 472*7c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 473*7c478bd9Sstevel@tonic-gate 474*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 475*7c478bd9Sstevel@tonic-gate /*LINTED*/ 476*7c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret); 477*7c478bd9Sstevel@tonic-gate if (*sret < 0) 478*7c478bd9Sstevel@tonic-gate /*LINTED*/ 479*7c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e); 480*7c478bd9Sstevel@tonic-gate else if (*sret == 0) 481*7c478bd9Sstevel@tonic-gate /*LINTED*/ 482*7c478bd9Sstevel@tonic-gate dprint(" (timeout)\n"); 483*7c478bd9Sstevel@tonic-gate else 484*7c478bd9Sstevel@tonic-gate /*LINTED*/ 485*7c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max); 486*7c478bd9Sstevel@tonic-gate #endif 487*7c478bd9Sstevel@tonic-gate 488*7c478bd9Sstevel@tonic-gate if (*sret < 0) 489*7c478bd9Sstevel@tonic-gate return e; 490*7c478bd9Sstevel@tonic-gate return 0; 491*7c478bd9Sstevel@tonic-gate } 492*7c478bd9Sstevel@tonic-gate 493*7c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn, 494*7c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 495*7c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn, 496*7c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags); 497*7c478bd9Sstevel@tonic-gate 498*7c478bd9Sstevel@tonic-gate 499*7c478bd9Sstevel@tonic-gate static int 500*7c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai, 501*7c478bd9Sstevel@tonic-gate const krb5_data *message, unsigned char *message_len_buf, 502*7c478bd9Sstevel@tonic-gate char **udpbufp) 503*7c478bd9Sstevel@tonic-gate { 504*7c478bd9Sstevel@tonic-gate state->state = INITIALIZING; 505*7c478bd9Sstevel@tonic-gate state->err = 0; 506*7c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf; 507*7c478bd9Sstevel@tonic-gate state->addr = ai; 508*7c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 509*7c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 510*7c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 511*7c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4); 512*7c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length); 513*7c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2; 514*7c478bd9Sstevel@tonic-gate state->is_udp = 0; 515*7c478bd9Sstevel@tonic-gate state->service = service_tcp_fd; 516*7c478bd9Sstevel@tonic-gate } else { 517*7c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length); 518*7c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0); 519*7c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1; 520*7c478bd9Sstevel@tonic-gate state->is_udp = 1; 521*7c478bd9Sstevel@tonic-gate state->service = service_udp_fd; 522*7c478bd9Sstevel@tonic-gate 523*7c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 524*7c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size); 525*7c478bd9Sstevel@tonic-gate if (*udpbufp == 0) { 526*7c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)"); 527*7c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 528*7c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 529*7c478bd9Sstevel@tonic-gate state->state = FAILED; 530*7c478bd9Sstevel@tonic-gate return 1; 531*7c478bd9Sstevel@tonic-gate } 532*7c478bd9Sstevel@tonic-gate } 533*7c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp; 534*7c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size; 535*7c478bd9Sstevel@tonic-gate } 536*7c478bd9Sstevel@tonic-gate return 0; 537*7c478bd9Sstevel@tonic-gate } 538*7c478bd9Sstevel@tonic-gate 539*7c478bd9Sstevel@tonic-gate static int 540*7c478bd9Sstevel@tonic-gate start_connection (struct conn_state *state, struct select_state *selstate) 541*7c478bd9Sstevel@tonic-gate { 542*7c478bd9Sstevel@tonic-gate int fd, e; 543*7c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr; 544*7c478bd9Sstevel@tonic-gate 545*7c478bd9Sstevel@tonic-gate /*LINTED*/ 546*7c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state, 547*7c478bd9Sstevel@tonic-gate /*LINTED*/ 548*7c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family); 549*7c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0); 550*7c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) { 551*7c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 552*7c478bd9Sstevel@tonic-gate /*LINTED*/ 553*7c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family); 554*7c478bd9Sstevel@tonic-gate return -1; /* try other hosts */ 555*7c478bd9Sstevel@tonic-gate } 556*7c478bd9Sstevel@tonic-gate /* Make it non-blocking. */ 557*7c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) { 558*7c478bd9Sstevel@tonic-gate static const int one = 1; 559*7c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 }; 560*7c478bd9Sstevel@tonic-gate 561*7c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one)) 562*7c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)"); 563*7c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt))) 564*7c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)"); 565*7c478bd9Sstevel@tonic-gate } 566*7c478bd9Sstevel@tonic-gate 567*7c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */ 568*7c478bd9Sstevel@tonic-gate /*LINTED*/ 569*7c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai); 570*7c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen); 571*7c478bd9Sstevel@tonic-gate if (e != 0) { 572*7c478bd9Sstevel@tonic-gate /* 573*7c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking 574*7c478bd9Sstevel@tonic-gate * connections. 575*7c478bd9Sstevel@tonic-gate */ 576*7c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) { 577*7c478bd9Sstevel@tonic-gate state->state = CONNECTING; 578*7c478bd9Sstevel@tonic-gate } else { 579*7c478bd9Sstevel@tonic-gate /*LINTED*/ 580*7c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO); 581*7c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO; 582*7c478bd9Sstevel@tonic-gate state->state = FAILED; 583*7c478bd9Sstevel@tonic-gate return -2; 584*7c478bd9Sstevel@tonic-gate } 585*7c478bd9Sstevel@tonic-gate } else { 586*7c478bd9Sstevel@tonic-gate /* 587*7c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it 588*7c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before 589*7c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network 590*7c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it. 591*7c478bd9Sstevel@tonic-gate */ 592*7c478bd9Sstevel@tonic-gate state->state = WRITING; 593*7c478bd9Sstevel@tonic-gate } 594*7c478bd9Sstevel@tonic-gate /*LINTED*/ 595*7c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]); 596*7c478bd9Sstevel@tonic-gate 597*7c478bd9Sstevel@tonic-gate state->fd = fd; 598*7c478bd9Sstevel@tonic-gate 599*7c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) { 600*7c478bd9Sstevel@tonic-gate /* Send it now. */ 601*7c478bd9Sstevel@tonic-gate int ret; 602*7c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0]; 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate /*LINTED*/ 605*7c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd); 606*7c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0); 607*7c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) { 608*7c478bd9Sstevel@tonic-gate dperror("sendto"); 609*7c478bd9Sstevel@tonic-gate (void) closesocket(state->fd); 610*7c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET; 611*7c478bd9Sstevel@tonic-gate state->state = FAILED; 612*7c478bd9Sstevel@tonic-gate return -3; 613*7c478bd9Sstevel@tonic-gate } else { 614*7c478bd9Sstevel@tonic-gate state->state = READING; 615*7c478bd9Sstevel@tonic-gate } 616*7c478bd9Sstevel@tonic-gate } 617*7c478bd9Sstevel@tonic-gate 618*7c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds); 619*7c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING) 620*7c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds); 621*7c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds); 622*7c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd) 623*7c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1; 624*7c478bd9Sstevel@tonic-gate selstate->nfds++; 625*7c478bd9Sstevel@tonic-gate 626*7c478bd9Sstevel@tonic-gate /*LINTED*/ 627*7c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n", 628*7c478bd9Sstevel@tonic-gate /*LINTED*/ 629*7c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max); 630*7c478bd9Sstevel@tonic-gate 631*7c478bd9Sstevel@tonic-gate return 0; 632*7c478bd9Sstevel@tonic-gate } 633*7c478bd9Sstevel@tonic-gate 634*7c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise. 635*7c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response. 636*7c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the 637*7c478bd9Sstevel@tonic-gate next connection. */ 638*7c478bd9Sstevel@tonic-gate static int 639*7c478bd9Sstevel@tonic-gate maybe_send (struct conn_state *conn, struct select_state *selstate) 640*7c478bd9Sstevel@tonic-gate { 641*7c478bd9Sstevel@tonic-gate sg_buf *sg; 642*7c478bd9Sstevel@tonic-gate 643*7c478bd9Sstevel@tonic-gate /*LINTED*/ 644*7c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn, 645*7c478bd9Sstevel@tonic-gate /*LINTED*/ 646*7c478bd9Sstevel@tonic-gate state_strings[conn->state], conn->is_udp ? "udp" : "tcp"); 647*7c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING) 648*7c478bd9Sstevel@tonic-gate return start_connection(conn, selstate); 649*7c478bd9Sstevel@tonic-gate 650*7c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */ 651*7c478bd9Sstevel@tonic-gate if (conn->state == FAILED) { 652*7c478bd9Sstevel@tonic-gate dprint("connection already closed\n"); 653*7c478bd9Sstevel@tonic-gate return -1; 654*7c478bd9Sstevel@tonic-gate } 655*7c478bd9Sstevel@tonic-gate 656*7c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) { 657*7c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n"); 658*7c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we 659*7c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */ 660*7c478bd9Sstevel@tonic-gate return -1; 661*7c478bd9Sstevel@tonic-gate } 662*7c478bd9Sstevel@tonic-gate 663*7c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a 664*7c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */ 665*7c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0]; 666*7c478bd9Sstevel@tonic-gate /*LINTED*/ 667*7c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd); 668*7c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) { 669*7c478bd9Sstevel@tonic-gate dperror("send"); 670*7c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass. 671*7c478bd9Sstevel@tonic-gate 672*7c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the 673*7c478bd9Sstevel@tonic-gate select callbacks? */ 674*7c478bd9Sstevel@tonic-gate return -1; 675*7c478bd9Sstevel@tonic-gate } 676*7c478bd9Sstevel@tonic-gate /* Yay, it worked. */ 677*7c478bd9Sstevel@tonic-gate return 0; 678*7c478bd9Sstevel@tonic-gate } 679*7c478bd9Sstevel@tonic-gate 680*7c478bd9Sstevel@tonic-gate static void 681*7c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err) 682*7c478bd9Sstevel@tonic-gate { 683*7c478bd9Sstevel@tonic-gate conn->state = FAILED; 684*7c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH); 685*7c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds); 686*7c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 687*7c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds); 688*7c478bd9Sstevel@tonic-gate conn->err = err; 689*7c478bd9Sstevel@tonic-gate /*LINTED*/ 690*7c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err); 691*7c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */ 692*7c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) { 693*7c478bd9Sstevel@tonic-gate while (selstate->max > 0 694*7c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds) 695*7c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds) 696*7c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds)) 697*7c478bd9Sstevel@tonic-gate selstate->max--; 698*7c478bd9Sstevel@tonic-gate /*LINTED*/ 699*7c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max); 700*7c478bd9Sstevel@tonic-gate } 701*7c478bd9Sstevel@tonic-gate selstate->nfds--; 702*7c478bd9Sstevel@tonic-gate } 703*7c478bd9Sstevel@tonic-gate 704*7c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit 705*7c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message, 706*7c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */ 707*7c478bd9Sstevel@tonic-gate 708*7c478bd9Sstevel@tonic-gate static int 709*7c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate, 710*7c478bd9Sstevel@tonic-gate int ssflags) 711*7c478bd9Sstevel@tonic-gate { 712*7c478bd9Sstevel@tonic-gate krb5_error_code e = 0; 713*7c478bd9Sstevel@tonic-gate int nwritten, nread; 714*7c478bd9Sstevel@tonic-gate 715*7c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION))) 716*7c478bd9Sstevel@tonic-gate abort(); 717*7c478bd9Sstevel@tonic-gate switch (conn->state) { 718*7c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp; 719*7c478bd9Sstevel@tonic-gate 720*7c478bd9Sstevel@tonic-gate case CONNECTING: 721*7c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 722*7c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */ 723*7c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */; 724*7c478bd9Sstevel@tonic-gate kill_conn: 725*7c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e); 726*7c478bd9Sstevel@tonic-gate if (e == EINVAL) { 727*7c478bd9Sstevel@tonic-gate closesocket(conn->fd); 728*7c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET; 729*7c478bd9Sstevel@tonic-gate } 730*7c478bd9Sstevel@tonic-gate return e == 0; 731*7c478bd9Sstevel@tonic-gate } 732*7c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 733*7c478bd9Sstevel@tonic-gate handle_exception: 734*7c478bd9Sstevel@tonic-gate e = 1; /* need only be non-zero */ 735*7c478bd9Sstevel@tonic-gate goto kill_conn; 736*7c478bd9Sstevel@tonic-gate } 737*7c478bd9Sstevel@tonic-gate 738*7c478bd9Sstevel@tonic-gate /* 739*7c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail? 740*7c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed. 741*7c478bd9Sstevel@tonic-gate * Try writing, I guess, and find out. 742*7c478bd9Sstevel@tonic-gate */ 743*7c478bd9Sstevel@tonic-gate conn->state = WRITING; 744*7c478bd9Sstevel@tonic-gate goto try_writing; 745*7c478bd9Sstevel@tonic-gate 746*7c478bd9Sstevel@tonic-gate case WRITING: 747*7c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) { 748*7c478bd9Sstevel@tonic-gate e = E2BIG; 749*7c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */ 750*7c478bd9Sstevel@tonic-gate goto kill_conn; 751*7c478bd9Sstevel@tonic-gate } 752*7c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) 753*7c478bd9Sstevel@tonic-gate goto handle_exception; 754*7c478bd9Sstevel@tonic-gate 755*7c478bd9Sstevel@tonic-gate try_writing: 756*7c478bd9Sstevel@tonic-gate /*LINTED*/ 757*7c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n", 758*7c478bd9Sstevel@tonic-gate /*LINTED*/ 759*7c478bd9Sstevel@tonic-gate conn->x.out.sg_count, 760*7c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0) 761*7c478bd9Sstevel@tonic-gate /*LINTED*/ 762*7c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])), 763*7c478bd9Sstevel@tonic-gate conn->fd); 764*7c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp, 765*7c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp); 766*7c478bd9Sstevel@tonic-gate if (nwritten < 0) { 767*7c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 768*7c478bd9Sstevel@tonic-gate /*LINTED*/ 769*7c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e); 770*7c478bd9Sstevel@tonic-gate goto kill_conn; 771*7c478bd9Sstevel@tonic-gate } 772*7c478bd9Sstevel@tonic-gate /*LINTED*/ 773*7c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten); 774*7c478bd9Sstevel@tonic-gate while (nwritten) { 775*7c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp; 776*7c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) { 777*7c478bd9Sstevel@tonic-gate /*LINTED*/ 778*7c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten); 779*7c478bd9Sstevel@tonic-gate nwritten = 0; 780*7c478bd9Sstevel@tonic-gate } else { 781*7c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp); 782*7c478bd9Sstevel@tonic-gate conn->x.out.sgp++; 783*7c478bd9Sstevel@tonic-gate conn->x.out.sg_count--; 784*7c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0) 785*7c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */ 786*7c478bd9Sstevel@tonic-gate abort(); 787*7c478bd9Sstevel@tonic-gate } 788*7c478bd9Sstevel@tonic-gate } 789*7c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) { 790*7c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */ 791*7c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because 792*7c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/ 793*7c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds); 794*7c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data 795*7c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode? 796*7c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */ 797*7c478bd9Sstevel@tonic-gate /*LINTED*/ 798*7c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd); 799*7c478bd9Sstevel@tonic-gate conn->state = READING; 800*7c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0; 801*7c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0; 802*7c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 803*7c478bd9Sstevel@tonic-gate conn->x.in.pos = 0; 804*7c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0; 805*7c478bd9Sstevel@tonic-gate } 806*7c478bd9Sstevel@tonic-gate return 0; 807*7c478bd9Sstevel@tonic-gate 808*7c478bd9Sstevel@tonic-gate case READING: 809*7c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) { 810*7c478bd9Sstevel@tonic-gate if (conn->x.in.buf) { 811*7c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 812*7c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 813*7c478bd9Sstevel@tonic-gate } 814*7c478bd9Sstevel@tonic-gate goto handle_exception; 815*7c478bd9Sstevel@tonic-gate } 816*7c478bd9Sstevel@tonic-gate 817*7c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 818*7c478bd9Sstevel@tonic-gate /* Reading data. */ 819*7c478bd9Sstevel@tonic-gate /*LINTED*/ 820*7c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n", 821*7c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd); 822*7c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left); 823*7c478bd9Sstevel@tonic-gate if (nread <= 0) { 824*7c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET; 825*7c478bd9Sstevel@tonic-gate free(conn->x.in.buf); 826*7c478bd9Sstevel@tonic-gate conn->x.in.buf = 0; 827*7c478bd9Sstevel@tonic-gate goto kill_conn; 828*7c478bd9Sstevel@tonic-gate } 829*7c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread; 830*7c478bd9Sstevel@tonic-gate conn->x.in.pos += nread; 831*7c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) { 832*7c478bd9Sstevel@tonic-gate /* We win! */ 833*7c478bd9Sstevel@tonic-gate return 1; 834*7c478bd9Sstevel@tonic-gate } 835*7c478bd9Sstevel@tonic-gate } else { 836*7c478bd9Sstevel@tonic-gate /* Reading length. */ 837*7c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, 838*7c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, 839*7c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read); 840*7c478bd9Sstevel@tonic-gate if (nread < 0) { 841*7c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO; 842*7c478bd9Sstevel@tonic-gate goto kill_conn; 843*7c478bd9Sstevel@tonic-gate } 844*7c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread; 845*7c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) { 846*7c478bd9Sstevel@tonic-gate unsigned long len; 847*7c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0]; 848*7c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1]; 849*7c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2]; 850*7c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3]; 851*7c478bd9Sstevel@tonic-gate /*LINTED*/ 852*7c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len); 853*7c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */ 854*7c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) { 855*7c478bd9Sstevel@tonic-gate e = E2BIG; 856*7c478bd9Sstevel@tonic-gate goto kill_conn; 857*7c478bd9Sstevel@tonic-gate } 858*7c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len; 859*7c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len); 860*7c478bd9Sstevel@tonic-gate /*LINTED*/ 861*7c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len, 862*7c478bd9Sstevel@tonic-gate conn->x.in.buf); 863*7c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) { 864*7c478bd9Sstevel@tonic-gate /* allocation failure */ 865*7c478bd9Sstevel@tonic-gate e = errno; 866*7c478bd9Sstevel@tonic-gate goto kill_conn; 867*7c478bd9Sstevel@tonic-gate } 868*7c478bd9Sstevel@tonic-gate } 869*7c478bd9Sstevel@tonic-gate } 870*7c478bd9Sstevel@tonic-gate break; 871*7c478bd9Sstevel@tonic-gate 872*7c478bd9Sstevel@tonic-gate default: 873*7c478bd9Sstevel@tonic-gate abort(); 874*7c478bd9Sstevel@tonic-gate } 875*7c478bd9Sstevel@tonic-gate return 0; 876*7c478bd9Sstevel@tonic-gate } 877*7c478bd9Sstevel@tonic-gate 878*7c478bd9Sstevel@tonic-gate static int 879*7c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate, 880*7c478bd9Sstevel@tonic-gate int ssflags) 881*7c478bd9Sstevel@tonic-gate { 882*7c478bd9Sstevel@tonic-gate int nread; 883*7c478bd9Sstevel@tonic-gate 884*7c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION))) 885*7c478bd9Sstevel@tonic-gate abort(); 886*7c478bd9Sstevel@tonic-gate if (conn->state != READING) 887*7c478bd9Sstevel@tonic-gate abort(); 888*7c478bd9Sstevel@tonic-gate 889*7c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0); 890*7c478bd9Sstevel@tonic-gate if (nread < 0) { 891*7c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO); 892*7c478bd9Sstevel@tonic-gate return 0; 893*7c478bd9Sstevel@tonic-gate } 894*7c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread; 895*7c478bd9Sstevel@tonic-gate return 1; 896*7c478bd9Sstevel@tonic-gate } 897*7c478bd9Sstevel@tonic-gate 898*7c478bd9Sstevel@tonic-gate static int 899*7c478bd9Sstevel@tonic-gate service_fds (struct select_state *selstate, 900*7c478bd9Sstevel@tonic-gate struct conn_state *conns, size_t n_conns, int *winning_conn) 901*7c478bd9Sstevel@tonic-gate { 902*7c478bd9Sstevel@tonic-gate int e, selret; 903*7c478bd9Sstevel@tonic-gate struct select_state sel_results; 904*7c478bd9Sstevel@tonic-gate 905*7c478bd9Sstevel@tonic-gate e = 0; 906*7c478bd9Sstevel@tonic-gate while (selstate->nfds > 0 907*7c478bd9Sstevel@tonic-gate && (e = krb5int_cm_call_select(selstate, &sel_results, &selret)) == 0) { 908*7c478bd9Sstevel@tonic-gate int i; 909*7c478bd9Sstevel@tonic-gate 910*7c478bd9Sstevel@tonic-gate /*LINTED*/ 911*7c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret); 912*7c478bd9Sstevel@tonic-gate 913*7c478bd9Sstevel@tonic-gate if (selret == 0) 914*7c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */ 915*7c478bd9Sstevel@tonic-gate return 0; 916*7c478bd9Sstevel@tonic-gate 917*7c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */ 918*7c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) { 919*7c478bd9Sstevel@tonic-gate int ssflags; 920*7c478bd9Sstevel@tonic-gate 921*7c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET) 922*7c478bd9Sstevel@tonic-gate continue; 923*7c478bd9Sstevel@tonic-gate ssflags = 0; 924*7c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.rfds)) 925*7c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--; 926*7c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.wfds)) 927*7c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--; 928*7c478bd9Sstevel@tonic-gate if (FD_ISSET(conns[i].fd, &sel_results.xfds)) 929*7c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--; 930*7c478bd9Sstevel@tonic-gate if (!ssflags) 931*7c478bd9Sstevel@tonic-gate continue; 932*7c478bd9Sstevel@tonic-gate 933*7c478bd9Sstevel@tonic-gate /*LINTED*/ 934*7c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n", 935*7c478bd9Sstevel@tonic-gate /*LINTED*/ 936*7c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "", 937*7c478bd9Sstevel@tonic-gate /*LINTED*/ 938*7c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "", 939*7c478bd9Sstevel@tonic-gate /*LINTED*/ 940*7c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "", 941*7c478bd9Sstevel@tonic-gate /*LINTED*/ 942*7c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr, 943*7c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]); 944*7c478bd9Sstevel@tonic-gate 945*7c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) { 946*7c478bd9Sstevel@tonic-gate dprint("fd service routine says we're done\n"); 947*7c478bd9Sstevel@tonic-gate *winning_conn = i; 948*7c478bd9Sstevel@tonic-gate return 1; 949*7c478bd9Sstevel@tonic-gate } 950*7c478bd9Sstevel@tonic-gate } 951*7c478bd9Sstevel@tonic-gate } 952*7c478bd9Sstevel@tonic-gate if (e != 0) { 953*7c478bd9Sstevel@tonic-gate /*LINTED*/ 954*7c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e); 955*7c478bd9Sstevel@tonic-gate *winning_conn = -1; 956*7c478bd9Sstevel@tonic-gate return 1; 957*7c478bd9Sstevel@tonic-gate } 958*7c478bd9Sstevel@tonic-gate return 0; 959*7c478bd9Sstevel@tonic-gate } 960*7c478bd9Sstevel@tonic-gate 961*7c478bd9Sstevel@tonic-gate /* 962*7c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior: 963*7c478bd9Sstevel@tonic-gate * 964*7c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end. 965*7c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s. 966*7c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s. 967*7c478bd9Sstevel@tonic-gate * Fourth => 16s, etc. 968*7c478bd9Sstevel@tonic-gate * 969*7c478bd9Sstevel@tonic-gate * Restated: 970*7c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass. 971*7c478bd9Sstevel@tonic-gate * Per TCP server, 1s. 972*7c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes. 973*7c478bd9Sstevel@tonic-gate * 974*7c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2. 975*7c478bd9Sstevel@tonic-gate * 976*7c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14. 977*7c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30. 978*7c478bd9Sstevel@tonic-gate * 979*7c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on 980*7c478bd9Sstevel@tonic-gate * one server, it counts as two. 981*7c478bd9Sstevel@tonic-gate */ 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate krb5_error_code 984*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 985*7c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message, 986*7c478bd9Sstevel@tonic-gate const struct addrlist *addrs, krb5_data *reply, 987*7c478bd9Sstevel@tonic-gate struct sockaddr_storage *localaddr, socklen_t *localaddrlen) 988*7c478bd9Sstevel@tonic-gate { 989*7c478bd9Sstevel@tonic-gate int i, pass; 990*7c478bd9Sstevel@tonic-gate int delay_this_pass = 2; 991*7c478bd9Sstevel@tonic-gate krb5_error_code retval; 992*7c478bd9Sstevel@tonic-gate struct conn_state *conns; 993*7c478bd9Sstevel@tonic-gate size_t n_conns, host; 994*7c478bd9Sstevel@tonic-gate struct select_state select_state; 995*7c478bd9Sstevel@tonic-gate struct timeval now; 996*7c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0; 997*7c478bd9Sstevel@tonic-gate unsigned char message_len_buf[4]; 998*7c478bd9Sstevel@tonic-gate char *udpbuf = 0; 999*7c478bd9Sstevel@tonic-gate 1000*7c478bd9Sstevel@tonic-gate /*LINTED*/ 1001*7c478bd9Sstevel@tonic-gate dprint("krb5int_sendto(message=%d@%p)\n", message->length, message->data); 1002*7c478bd9Sstevel@tonic-gate 1003*7c478bd9Sstevel@tonic-gate reply->data = 0; 1004*7c478bd9Sstevel@tonic-gate reply->length = 0; 1005*7c478bd9Sstevel@tonic-gate 1006*7c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs; 1007*7c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state)); 1008*7c478bd9Sstevel@tonic-gate if (conns == NULL) { 1009*7c478bd9Sstevel@tonic-gate return ENOMEM; 1010*7c478bd9Sstevel@tonic-gate } 1011*7c478bd9Sstevel@tonic-gate memset(conns, 0, n_conns * sizeof(conns[i])); 1012*7c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 1013*7c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET; 1014*7c478bd9Sstevel@tonic-gate } 1015*7c478bd9Sstevel@tonic-gate 1016*7c478bd9Sstevel@tonic-gate select_state.max = 0; 1017*7c478bd9Sstevel@tonic-gate select_state.nfds = 0; 1018*7c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.rfds); 1019*7c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.wfds); 1020*7c478bd9Sstevel@tonic-gate FD_ZERO(&select_state.xfds); 1021*7c478bd9Sstevel@tonic-gate 1022*7c478bd9Sstevel@tonic-gate message_len_buf[0] = (message->length >> 24) & 0xff; 1023*7c478bd9Sstevel@tonic-gate message_len_buf[1] = (message->length >> 16) & 0xff; 1024*7c478bd9Sstevel@tonic-gate message_len_buf[2] = (message->length >> 8) & 0xff; 1025*7c478bd9Sstevel@tonic-gate message_len_buf[3] = message->length & 0xff; 1026*7c478bd9Sstevel@tonic-gate 1027*7c478bd9Sstevel@tonic-gate /* Set up connections. */ 1028*7c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 1029*7c478bd9Sstevel@tonic-gate retval = setup_connection (&conns[host], addrs->addrs[host], 1030*7c478bd9Sstevel@tonic-gate message, message_len_buf, &udpbuf); 1031*7c478bd9Sstevel@tonic-gate if (retval) 1032*7c478bd9Sstevel@tonic-gate continue; 1033*7c478bd9Sstevel@tonic-gate } 1034*7c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) { 1035*7c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only. 1036*7c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */ 1037*7c478bd9Sstevel@tonic-gate /*LINTED*/ 1038*7c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass); 1039*7c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) { 1040*7c478bd9Sstevel@tonic-gate /*LINTED*/ 1041*7c478bd9Sstevel@tonic-gate dprint("host %d\n", host); 1042*7c478bd9Sstevel@tonic-gate 1043*7c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */ 1044*7c478bd9Sstevel@tonic-gate if (maybe_send(&conns[host], &select_state)) 1045*7c478bd9Sstevel@tonic-gate continue; 1046*7c478bd9Sstevel@tonic-gate 1047*7c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 1048*7c478bd9Sstevel@tonic-gate if (retval) 1049*7c478bd9Sstevel@tonic-gate goto egress; 1050*7c478bd9Sstevel@tonic-gate select_state.end_time = now; 1051*7c478bd9Sstevel@tonic-gate select_state.end_time.tv_sec += 1; 1052*7c478bd9Sstevel@tonic-gate e = service_fds(&select_state, conns, host+1, &winning_conn); 1053*7c478bd9Sstevel@tonic-gate if (e) 1054*7c478bd9Sstevel@tonic-gate break; 1055*7c478bd9Sstevel@tonic-gate if (pass > 0 && select_state.nfds == 0) 1056*7c478bd9Sstevel@tonic-gate /* 1057*7c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break 1058*7c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay, 1059*7c478bd9Sstevel@tonic-gate * we're probably about to open another connection. 1060*7c478bd9Sstevel@tonic-gate */ 1061*7c478bd9Sstevel@tonic-gate break; 1062*7c478bd9Sstevel@tonic-gate } 1063*7c478bd9Sstevel@tonic-gate if (e) 1064*7c478bd9Sstevel@tonic-gate break; 1065*7c478bd9Sstevel@tonic-gate retval = getcurtime(&now); 1066*7c478bd9Sstevel@tonic-gate if (retval) 1067*7c478bd9Sstevel@tonic-gate goto egress; 1068*7c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select 1069*7c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop 1070*7c478bd9Sstevel@tonic-gate actually calls select. */ 1071*7c478bd9Sstevel@tonic-gate select_state.end_time.tv_sec += delay_this_pass; 1072*7c478bd9Sstevel@tonic-gate e = service_fds(&select_state, conns, host+1, &winning_conn); 1073*7c478bd9Sstevel@tonic-gate if (e) 1074*7c478bd9Sstevel@tonic-gate break; 1075*7c478bd9Sstevel@tonic-gate if (select_state.nfds == 0) 1076*7c478bd9Sstevel@tonic-gate break; 1077*7c478bd9Sstevel@tonic-gate delay_this_pass *= 2; 1078*7c478bd9Sstevel@tonic-gate } 1079*7c478bd9Sstevel@tonic-gate 1080*7c478bd9Sstevel@tonic-gate if (select_state.nfds == 0) { 1081*7c478bd9Sstevel@tonic-gate /* No addresses? */ 1082*7c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 1083*7c478bd9Sstevel@tonic-gate goto egress; 1084*7c478bd9Sstevel@tonic-gate } 1085*7c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) { 1086*7c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH; 1087*7c478bd9Sstevel@tonic-gate goto egress; 1088*7c478bd9Sstevel@tonic-gate } 1089*7c478bd9Sstevel@tonic-gate /* Success! */ 1090*7c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf; 1091*7c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos 1092*7c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf); 1093*7c478bd9Sstevel@tonic-gate /*LINTED*/ 1094*7c478bd9Sstevel@tonic-gate dprint("returning %d bytes in buffer %p\n", 1095*7c478bd9Sstevel@tonic-gate (int) reply->length, reply->data); 1096*7c478bd9Sstevel@tonic-gate retval = 0; 1097*7c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0; 1098*7c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0) 1099*7c478bd9Sstevel@tonic-gate (void) getsockname(conns[winning_conn].fd, (struct sockaddr *)localaddr, 1100*7c478bd9Sstevel@tonic-gate localaddrlen); 1101*7c478bd9Sstevel@tonic-gate egress: 1102*7c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) { 1103*7c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET) 1104*7c478bd9Sstevel@tonic-gate close(conns[i].fd); 1105*7c478bd9Sstevel@tonic-gate if (conns[i].state == READING 1106*7c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0 1107*7c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf) 1108*7c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf); 1109*7c478bd9Sstevel@tonic-gate } 1110*7c478bd9Sstevel@tonic-gate free(conns); 1111*7c478bd9Sstevel@tonic-gate if (reply->data != udpbuf) 1112*7c478bd9Sstevel@tonic-gate free(udpbuf); 1113*7c478bd9Sstevel@tonic-gate return retval; 1114*7c478bd9Sstevel@tonic-gate } 1115