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