17c478bd9Sstevel@tonic-gate /*
256bbb0b2SPeter Shoults * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate */
47c478bd9Sstevel@tonic-gate /*
57c478bd9Sstevel@tonic-gate * lib/krb5/os/sendto_kdc.c
67c478bd9Sstevel@tonic-gate *
7159d09a2SMark Phalan * Copyright 1990,1991,2001,2002,2004,2005,2007 by the Massachusetts Institute of Technology.
87c478bd9Sstevel@tonic-gate * All Rights Reserved.
97c478bd9Sstevel@tonic-gate *
107c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may
117c478bd9Sstevel@tonic-gate * require a specific license from the United States Government.
127c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating
137c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting.
14*55fea89dSDan Cross *
157c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
167c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and
177c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright
187c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and
197c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that
207c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining
217c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior
227c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label
237c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a
247c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software.
257c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of
267c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express
277c478bd9Sstevel@tonic-gate * or implied warranty.
28*55fea89dSDan Cross *
297c478bd9Sstevel@tonic-gate *
307c478bd9Sstevel@tonic-gate * Send packet to KDC for realm; wait for response, retransmitting
317c478bd9Sstevel@tonic-gate * as necessary.
327c478bd9Sstevel@tonic-gate */
337c478bd9Sstevel@tonic-gate
34159d09a2SMark Phalan #include "fake-addrinfo.h"
35159d09a2SMark Phalan #include "k5-int.h"
36159d09a2SMark Phalan
37159d09a2SMark Phalan /* Solaris Kerberos */
38159d09a2SMark Phalan #include <syslog.h>
395e01956fSGlenn Barry #include <locale.h>
407c478bd9Sstevel@tonic-gate
417c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H
427c478bd9Sstevel@tonic-gate #include <sys/time.h>
437c478bd9Sstevel@tonic-gate #else
447c478bd9Sstevel@tonic-gate #include <time.h>
457c478bd9Sstevel@tonic-gate #endif
467c478bd9Sstevel@tonic-gate #include "os-proto.h"
47159d09a2SMark Phalan #ifdef _WIN32
48159d09a2SMark Phalan #include <sys/timeb.h>
49159d09a2SMark Phalan #endif
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate #ifdef _AIX
527c478bd9Sstevel@tonic-gate #include <sys/select.h>
537c478bd9Sstevel@tonic-gate #endif
547c478bd9Sstevel@tonic-gate
55159d09a2SMark Phalan #ifndef _WIN32
567c478bd9Sstevel@tonic-gate /* For FIONBIO. */
577c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
587c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_FILIO_H
597c478bd9Sstevel@tonic-gate #include <sys/filio.h>
607c478bd9Sstevel@tonic-gate #endif
61159d09a2SMark Phalan #endif
627c478bd9Sstevel@tonic-gate
637c478bd9Sstevel@tonic-gate #define MAX_PASS 3
647c478bd9Sstevel@tonic-gate /* Solaris Kerberos: moved to k5-int.h */
657c478bd9Sstevel@tonic-gate /* #define DEFAULT_UDP_PREF_LIMIT 1465 */
667c478bd9Sstevel@tonic-gate #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */
677c478bd9Sstevel@tonic-gate
68159d09a2SMark Phalan #undef DEBUG
69159d09a2SMark Phalan
70159d09a2SMark Phalan #ifdef DEBUG
71159d09a2SMark Phalan int krb5int_debug_sendto_kdc = 0;
72159d09a2SMark Phalan #define debug krb5int_debug_sendto_kdc
73159d09a2SMark Phalan
default_debug_handler(const void * data,size_t len)747c478bd9Sstevel@tonic-gate static void default_debug_handler (const void *data, size_t len)
757c478bd9Sstevel@tonic-gate {
76159d09a2SMark Phalan #if 0
77159d09a2SMark Phalan FILE *logfile;
78159d09a2SMark Phalan logfile = fopen("/tmp/sendto_kdc.log", "a");
79159d09a2SMark Phalan if (logfile == NULL)
80159d09a2SMark Phalan return;
81159d09a2SMark Phalan fwrite(data, 1, len, logfile);
82159d09a2SMark Phalan fclose(logfile);
83159d09a2SMark Phalan #else
847c478bd9Sstevel@tonic-gate fwrite(data, 1, len, stderr);
857c478bd9Sstevel@tonic-gate /* stderr is unbuffered */
86159d09a2SMark Phalan #endif
877c478bd9Sstevel@tonic-gate }
887c478bd9Sstevel@tonic-gate
897c478bd9Sstevel@tonic-gate void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler;
907c478bd9Sstevel@tonic-gate
91*55fea89dSDan Cross /*
927c478bd9Sstevel@tonic-gate * Solaris Kerberos: only including the debug stuff if DEBUG defined outside
937c478bd9Sstevel@tonic-gate * this file.
947c478bd9Sstevel@tonic-gate */
957c478bd9Sstevel@tonic-gate static char global_err_str[NI_MAXHOST + NI_MAXSERV + 1024];
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate /* Solaris kerberos: removed put() since it isn't needed. */
98159d09a2SMark Phalan #if 0
99159d09a2SMark Phalan static void put(const void *ptr, size_t len)
100159d09a2SMark Phalan {
101159d09a2SMark Phalan (*krb5int_sendtokdc_debug_handler)(ptr, len);
102159d09a2SMark Phalan }
103159d09a2SMark Phalan #endif
1047c478bd9Sstevel@tonic-gate
putstr(const char * str)1057c478bd9Sstevel@tonic-gate static void putstr(const char *str)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate /* Solaris kerberos: build the string which will be passed to syslog later */
1087c478bd9Sstevel@tonic-gate strlcat(global_err_str, str, sizeof (global_err_str));
1097c478bd9Sstevel@tonic-gate }
110159d09a2SMark Phalan #else
111159d09a2SMark Phalan void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0;
112159d09a2SMark Phalan #endif
1137c478bd9Sstevel@tonic-gate
1147c478bd9Sstevel@tonic-gate #define dprint krb5int_debug_fprint
115159d09a2SMark Phalan void
krb5int_debug_fprint(const char * fmt,...)1167c478bd9Sstevel@tonic-gate krb5int_debug_fprint (const char *fmt, ...)
1177c478bd9Sstevel@tonic-gate {
118159d09a2SMark Phalan #ifdef DEBUG
1197c478bd9Sstevel@tonic-gate va_list args;
1207c478bd9Sstevel@tonic-gate
1217c478bd9Sstevel@tonic-gate /* Temporaries for variable arguments, etc. */
1227c478bd9Sstevel@tonic-gate krb5_error_code kerr;
1237c478bd9Sstevel@tonic-gate int err;
1247c478bd9Sstevel@tonic-gate fd_set *rfds, *wfds, *xfds;
1257c478bd9Sstevel@tonic-gate int i;
1267c478bd9Sstevel@tonic-gate int maxfd;
1277c478bd9Sstevel@tonic-gate struct timeval *tv;
1287c478bd9Sstevel@tonic-gate struct addrinfo *ai;
1297c478bd9Sstevel@tonic-gate const krb5_data *d;
1307c478bd9Sstevel@tonic-gate char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
1317c478bd9Sstevel@tonic-gate const char *p;
132159d09a2SMark Phalan #ifndef max
133159d09a2SMark Phalan #define max(a,b) ((a) > (b) ? (a) : (b))
134159d09a2SMark Phalan #endif
135159d09a2SMark Phalan char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)];
1367c478bd9Sstevel@tonic-gate
137*55fea89dSDan Cross /*
1387c478bd9Sstevel@tonic-gate * Solaris kerberos: modified this function to create a string to pass to
1397c478bd9Sstevel@tonic-gate * syslog()
1407c478bd9Sstevel@tonic-gate */
1417c478bd9Sstevel@tonic-gate global_err_str[0] = NULL;
1427c478bd9Sstevel@tonic-gate
1437c478bd9Sstevel@tonic-gate va_start(args, fmt);
1447c478bd9Sstevel@tonic-gate
1457c478bd9Sstevel@tonic-gate #define putf(FMT,X) (sprintf(tmpbuf,FMT,X),putstr(tmpbuf))
1467c478bd9Sstevel@tonic-gate
1477c478bd9Sstevel@tonic-gate for (; *fmt; fmt++) {
1487c478bd9Sstevel@tonic-gate if (*fmt != '%') {
1497c478bd9Sstevel@tonic-gate /* Possible optimization: Look for % and print all chars
1507c478bd9Sstevel@tonic-gate up to it in one call. */
1517c478bd9Sstevel@tonic-gate putf("%c", *fmt);
1527c478bd9Sstevel@tonic-gate continue;
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate /* After this, always processing a '%' sequence. */
1557c478bd9Sstevel@tonic-gate fmt++;
1567c478bd9Sstevel@tonic-gate switch (*fmt) {
1577c478bd9Sstevel@tonic-gate case 0:
1587c478bd9Sstevel@tonic-gate default:
1597c478bd9Sstevel@tonic-gate abort();
1607c478bd9Sstevel@tonic-gate case 'E':
1617c478bd9Sstevel@tonic-gate /* %E => krb5_error_code */
1627c478bd9Sstevel@tonic-gate kerr = va_arg(args, krb5_error_code);
1637c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%lu/", (unsigned long) kerr);
1647c478bd9Sstevel@tonic-gate putstr(tmpbuf);
1657c478bd9Sstevel@tonic-gate p = error_message(kerr);
1667c478bd9Sstevel@tonic-gate putstr(p);
1677c478bd9Sstevel@tonic-gate break;
1687c478bd9Sstevel@tonic-gate case 'm':
1697c478bd9Sstevel@tonic-gate /* %m => errno value (int) */
1707c478bd9Sstevel@tonic-gate /* Like syslog's %m except the errno value is passed in
1717c478bd9Sstevel@tonic-gate rather than the current value. */
1727c478bd9Sstevel@tonic-gate err = va_arg(args, int);
1737c478bd9Sstevel@tonic-gate putf("%d/", err);
174159d09a2SMark Phalan p = NULL;
175159d09a2SMark Phalan #ifdef HAVE_STRERROR_R
176159d09a2SMark Phalan if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
177159d09a2SMark Phalan p = tmpbuf;
178159d09a2SMark Phalan #endif
179159d09a2SMark Phalan if (p == NULL)
180159d09a2SMark Phalan p = strerror(err);
1817c478bd9Sstevel@tonic-gate putstr(p);
1827c478bd9Sstevel@tonic-gate break;
1837c478bd9Sstevel@tonic-gate case 'F':
1847c478bd9Sstevel@tonic-gate /* %F => fd_set *, fd_set *, fd_set *, int */
1857c478bd9Sstevel@tonic-gate rfds = va_arg(args, fd_set *);
1867c478bd9Sstevel@tonic-gate wfds = va_arg(args, fd_set *);
1877c478bd9Sstevel@tonic-gate xfds = va_arg(args, fd_set *);
1887c478bd9Sstevel@tonic-gate maxfd = va_arg(args, int);
1897c478bd9Sstevel@tonic-gate
1907c478bd9Sstevel@tonic-gate for (i = 0; i < maxfd; i++) {
1917c478bd9Sstevel@tonic-gate int r = FD_ISSET(i, rfds);
1927c478bd9Sstevel@tonic-gate int w = wfds && FD_ISSET(i, wfds);
1937c478bd9Sstevel@tonic-gate int x = xfds && FD_ISSET(i, xfds);
1947c478bd9Sstevel@tonic-gate if (r || w || x) {
1957c478bd9Sstevel@tonic-gate putf(" %d", i);
1967c478bd9Sstevel@tonic-gate if (r)
1977c478bd9Sstevel@tonic-gate putstr("r");
1987c478bd9Sstevel@tonic-gate if (w)
1997c478bd9Sstevel@tonic-gate putstr("w");
2007c478bd9Sstevel@tonic-gate if (x)
2017c478bd9Sstevel@tonic-gate putstr("x");
2027c478bd9Sstevel@tonic-gate }
2037c478bd9Sstevel@tonic-gate }
2047c478bd9Sstevel@tonic-gate putstr(" ");
2057c478bd9Sstevel@tonic-gate break;
2067c478bd9Sstevel@tonic-gate case 's':
2077c478bd9Sstevel@tonic-gate /* %s => char * */
2087c478bd9Sstevel@tonic-gate p = va_arg(args, const char *);
2097c478bd9Sstevel@tonic-gate putstr(p);
2107c478bd9Sstevel@tonic-gate break;
2117c478bd9Sstevel@tonic-gate case 't':
2127c478bd9Sstevel@tonic-gate /* %t => struct timeval * */
2137c478bd9Sstevel@tonic-gate tv = va_arg(args, struct timeval *);
2147c478bd9Sstevel@tonic-gate if (tv) {
2157c478bd9Sstevel@tonic-gate sprintf(tmpbuf, "%ld.%06ld",
2167c478bd9Sstevel@tonic-gate (long) tv->tv_sec, (long) tv->tv_usec);
2177c478bd9Sstevel@tonic-gate putstr(tmpbuf);
2187c478bd9Sstevel@tonic-gate } else
2197c478bd9Sstevel@tonic-gate putstr("never");
2207c478bd9Sstevel@tonic-gate break;
2217c478bd9Sstevel@tonic-gate case 'd':
2227c478bd9Sstevel@tonic-gate /* %d => int */
2237c478bd9Sstevel@tonic-gate putf("%d", va_arg(args, int));
2247c478bd9Sstevel@tonic-gate break;
2257c478bd9Sstevel@tonic-gate case 'p':
2267c478bd9Sstevel@tonic-gate /* %p => pointer */
2277c478bd9Sstevel@tonic-gate putf("%p", va_arg(args, void*));
2287c478bd9Sstevel@tonic-gate break;
2297c478bd9Sstevel@tonic-gate case 'A':
2307c478bd9Sstevel@tonic-gate /* %A => addrinfo */
2317c478bd9Sstevel@tonic-gate ai = va_arg(args, struct addrinfo *);
232159d09a2SMark Phalan if (ai->ai_socktype == SOCK_DGRAM)
233159d09a2SMark Phalan strcpy(tmpbuf, "dgram");
234159d09a2SMark Phalan else if (ai->ai_socktype == SOCK_STREAM)
235159d09a2SMark Phalan strcpy(tmpbuf, "stream");
236159d09a2SMark Phalan else
237159d09a2SMark Phalan sprintf(tmpbuf, "socktype%d", ai->ai_socktype);
2387c478bd9Sstevel@tonic-gate if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen,
2397c478bd9Sstevel@tonic-gate addrbuf, sizeof (addrbuf),
2407c478bd9Sstevel@tonic-gate portbuf, sizeof (portbuf),
241159d09a2SMark Phalan NI_NUMERICHOST | NI_NUMERICSERV)) {
242159d09a2SMark Phalan if (ai->ai_addr->sa_family == AF_UNSPEC)
243159d09a2SMark Phalan strcpy(tmpbuf + strlen(tmpbuf), " AF_UNSPEC");
244159d09a2SMark Phalan else
245159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " af%d", ai->ai_addr->sa_family);
246159d09a2SMark Phalan } else
247159d09a2SMark Phalan sprintf(tmpbuf + strlen(tmpbuf), " %s.%s", addrbuf, portbuf);
2487c478bd9Sstevel@tonic-gate putstr(tmpbuf);
2497c478bd9Sstevel@tonic-gate break;
2507c478bd9Sstevel@tonic-gate case 'D':
2517c478bd9Sstevel@tonic-gate /* %D => krb5_data * */
2527c478bd9Sstevel@tonic-gate d = va_arg(args, krb5_data *);
253159d09a2SMark Phalan /* Solaris Kerberos */
2547c478bd9Sstevel@tonic-gate p = d->data;
2557c478bd9Sstevel@tonic-gate putstr("0x");
2567c478bd9Sstevel@tonic-gate for (i = 0; i < d->length; i++) {
2577c478bd9Sstevel@tonic-gate putf("%.2x", *p++);
2587c478bd9Sstevel@tonic-gate }
2597c478bd9Sstevel@tonic-gate break;
2607c478bd9Sstevel@tonic-gate }
2617c478bd9Sstevel@tonic-gate }
2627c478bd9Sstevel@tonic-gate va_end(args);
2637c478bd9Sstevel@tonic-gate
2647c478bd9Sstevel@tonic-gate /* Solaris kerberos: use syslog() for debug output */
2657c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, global_err_str);
266159d09a2SMark Phalan #endif
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate
269159d09a2SMark Phalan #define print_addrlist krb5int_print_addrlist
270159d09a2SMark Phalan static void
print_addrlist(const struct addrlist * a)271159d09a2SMark Phalan print_addrlist (const struct addrlist *a)
272159d09a2SMark Phalan {
273159d09a2SMark Phalan int i;
274159d09a2SMark Phalan dprint("%d{", a->naddrs);
275159d09a2SMark Phalan for (i = 0; i < a->naddrs; i++)
276159d09a2SMark Phalan dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai);
277159d09a2SMark Phalan dprint("}");
278159d09a2SMark Phalan }
2797c478bd9Sstevel@tonic-gate
2807c478bd9Sstevel@tonic-gate static int
merge_addrlists(struct addrlist * dest,struct addrlist * src)2817c478bd9Sstevel@tonic-gate merge_addrlists (struct addrlist *dest, struct addrlist *src)
2827c478bd9Sstevel@tonic-gate {
283159d09a2SMark Phalan /* Wouldn't it be nice if we could filter out duplicates? The
284159d09a2SMark Phalan alloc/free handling makes that pretty difficult though. */
2857c478bd9Sstevel@tonic-gate int err, i;
2867c478bd9Sstevel@tonic-gate
287159d09a2SMark Phalan /* Solaris Kerberos */
2887c478bd9Sstevel@tonic-gate #ifdef DEBUG
2897c478bd9Sstevel@tonic-gate /*LINTED*/
2907c478bd9Sstevel@tonic-gate dprint("merging addrlists:\n\tlist1: ");
2917c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++)
2927c478bd9Sstevel@tonic-gate /*LINTED*/
293159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai);
2947c478bd9Sstevel@tonic-gate /*LINTED*/
2957c478bd9Sstevel@tonic-gate dprint("\n\tlist2: ");
2967c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++)
2977c478bd9Sstevel@tonic-gate /*LINTED*/
298159d09a2SMark Phalan dprint(" %A", src->addrs[i].ai);
2997c478bd9Sstevel@tonic-gate /*LINTED*/
3007c478bd9Sstevel@tonic-gate dprint("\n");
3017c478bd9Sstevel@tonic-gate #endif
3027c478bd9Sstevel@tonic-gate
3037c478bd9Sstevel@tonic-gate err = krb5int_grow_addrlist (dest, src->naddrs);
3047c478bd9Sstevel@tonic-gate if (err)
3057c478bd9Sstevel@tonic-gate return err;
3067c478bd9Sstevel@tonic-gate for (i = 0; i < src->naddrs; i++) {
3077c478bd9Sstevel@tonic-gate dest->addrs[dest->naddrs + i] = src->addrs[i];
308159d09a2SMark Phalan src->addrs[i].ai = 0;
309159d09a2SMark Phalan src->addrs[i].freefn = 0;
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate dest->naddrs += i;
3127c478bd9Sstevel@tonic-gate src->naddrs = 0;
3137c478bd9Sstevel@tonic-gate
314159d09a2SMark Phalan /* Solaris Kerberos */
3157c478bd9Sstevel@tonic-gate #ifdef DEBUG
3167c478bd9Sstevel@tonic-gate /*LINTED*/
3177c478bd9Sstevel@tonic-gate dprint("\tout: ");
3187c478bd9Sstevel@tonic-gate for (i = 0; i < dest->naddrs; i++)
3197c478bd9Sstevel@tonic-gate /*LINTED*/
320159d09a2SMark Phalan dprint(" %A", dest->addrs[i].ai);
3217c478bd9Sstevel@tonic-gate /*LINTED*/
3227c478bd9Sstevel@tonic-gate dprint("\n");
3237c478bd9Sstevel@tonic-gate #endif
3247c478bd9Sstevel@tonic-gate
3257c478bd9Sstevel@tonic-gate return 0;
3267c478bd9Sstevel@tonic-gate }
3277c478bd9Sstevel@tonic-gate
328159d09a2SMark Phalan static int
in_addrlist(struct addrinfo * thisaddr,struct addrlist * list)329159d09a2SMark Phalan in_addrlist (struct addrinfo *thisaddr, struct addrlist *list)
330159d09a2SMark Phalan {
331159d09a2SMark Phalan int i;
332159d09a2SMark Phalan for (i = 0; i < list->naddrs; i++) {
333159d09a2SMark Phalan if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen
334159d09a2SMark Phalan && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr,
335159d09a2SMark Phalan thisaddr->ai_addrlen))
336159d09a2SMark Phalan return 1;
337159d09a2SMark Phalan }
338159d09a2SMark Phalan return 0;
339159d09a2SMark Phalan }
340159d09a2SMark Phalan
341159d09a2SMark Phalan static int
check_for_svc_unavailable(krb5_context context,const krb5_data * reply,void * msg_handler_data)342159d09a2SMark Phalan check_for_svc_unavailable (krb5_context context,
343159d09a2SMark Phalan const krb5_data *reply,
344159d09a2SMark Phalan void *msg_handler_data)
345159d09a2SMark Phalan {
346159d09a2SMark Phalan krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
347159d09a2SMark Phalan
348159d09a2SMark Phalan *retval = 0;
349159d09a2SMark Phalan
350159d09a2SMark Phalan if (krb5_is_krb_error(reply)) {
351159d09a2SMark Phalan krb5_error *err_reply;
352159d09a2SMark Phalan
353159d09a2SMark Phalan if (decode_krb5_error(reply, &err_reply) == 0) {
354159d09a2SMark Phalan *retval = err_reply->error;
355159d09a2SMark Phalan krb5_free_error(context, err_reply);
356159d09a2SMark Phalan
357159d09a2SMark Phalan /* Returning 0 means continue to next KDC */
358159d09a2SMark Phalan return (*retval != KDC_ERR_SVC_UNAVAILABLE);
359159d09a2SMark Phalan }
360159d09a2SMark Phalan }
361159d09a2SMark Phalan
362159d09a2SMark Phalan return 1;
363159d09a2SMark Phalan }
364159d09a2SMark Phalan
3657c478bd9Sstevel@tonic-gate /*
3667c478bd9Sstevel@tonic-gate * send the formatted request 'message' to a KDC for realm 'realm' and
3677c478bd9Sstevel@tonic-gate * return the response (if any) in 'reply'.
3687c478bd9Sstevel@tonic-gate *
3697c478bd9Sstevel@tonic-gate * If the message is sent and a response is received, 0 is returned,
3707c478bd9Sstevel@tonic-gate * otherwise an error code is returned.
3717c478bd9Sstevel@tonic-gate *
3727c478bd9Sstevel@tonic-gate * The storage for 'reply' is allocated and should be freed by the caller
3737c478bd9Sstevel@tonic-gate * when finished.
3747c478bd9Sstevel@tonic-gate */
3757c478bd9Sstevel@tonic-gate
3767c478bd9Sstevel@tonic-gate krb5_error_code
krb5_sendto_kdc(krb5_context context,const krb5_data * message,const krb5_data * realm,krb5_data * reply,int * use_master,int tcp_only)3777c478bd9Sstevel@tonic-gate krb5_sendto_kdc (krb5_context context, const krb5_data *message,
3787c478bd9Sstevel@tonic-gate const krb5_data *realm, krb5_data *reply,
379505d05c7Sgtb int *use_master, int tcp_only)
3805e01956fSGlenn Barry {
3815e01956fSGlenn Barry return (krb5_sendto_kdc2(context, message, realm, reply, use_master,
3825e01956fSGlenn Barry tcp_only, NULL));
3835e01956fSGlenn Barry }
3845e01956fSGlenn Barry
3855e01956fSGlenn Barry /*
3865e01956fSGlenn Barry * Solaris Kerberos
3875e01956fSGlenn Barry * Same as krb5_sendto_kdc plus an extra arg to return the FQDN
3885e01956fSGlenn Barry * of the KDC sent the request.
3895e01956fSGlenn Barry * Caller (at top of stack) needs to free hostname_used.
3905e01956fSGlenn Barry */
3915e01956fSGlenn Barry krb5_error_code
krb5_sendto_kdc2(krb5_context context,const krb5_data * message,const krb5_data * realm,krb5_data * reply,int * use_master,int tcp_only,char ** hostname_used)3925e01956fSGlenn Barry krb5_sendto_kdc2 (krb5_context context, const krb5_data *message,
3935e01956fSGlenn Barry const krb5_data *realm, krb5_data *reply,
3945e01956fSGlenn Barry int *use_master, int tcp_only, char **hostname_used)
3957c478bd9Sstevel@tonic-gate {
396159d09a2SMark Phalan krb5_error_code retval, retval2;
39756bbb0b2SPeter Shoults struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */
398505d05c7Sgtb int socktype1 = 0, socktype2 = 0, addr_used;
3997c478bd9Sstevel@tonic-gate
4007c478bd9Sstevel@tonic-gate /*
4017c478bd9Sstevel@tonic-gate * find KDC location(s) for realm
4027c478bd9Sstevel@tonic-gate */
4037c478bd9Sstevel@tonic-gate
4047c478bd9Sstevel@tonic-gate /*
4057c478bd9Sstevel@tonic-gate * BUG: This code won't return "interesting" errors (e.g., out of mem,
4067c478bd9Sstevel@tonic-gate * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be
4077c478bd9Sstevel@tonic-gate * ignored from one query of two, but if only one query is done, or
4087c478bd9Sstevel@tonic-gate * both return that error, it should be returned to the caller. Also,
4097c478bd9Sstevel@tonic-gate * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
4107c478bd9Sstevel@tonic-gate * should probably be returned as well.
4117c478bd9Sstevel@tonic-gate */
4127c478bd9Sstevel@tonic-gate
4137c478bd9Sstevel@tonic-gate /*LINTED*/
4147c478bd9Sstevel@tonic-gate dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
4157c478bd9Sstevel@tonic-gate /*LINTED*/
416505d05c7Sgtb message->length, message->data, realm, *use_master, tcp_only);
4177c478bd9Sstevel@tonic-gate
4187c478bd9Sstevel@tonic-gate if (!tcp_only && context->udp_pref_limit < 0) {
4197c478bd9Sstevel@tonic-gate int tmp;
4207c478bd9Sstevel@tonic-gate retval = profile_get_integer(context->profile,
4217c478bd9Sstevel@tonic-gate "libdefaults", "udp_preference_limit", 0,
4227c478bd9Sstevel@tonic-gate DEFAULT_UDP_PREF_LIMIT, &tmp);
4237c478bd9Sstevel@tonic-gate if (retval)
4247c478bd9Sstevel@tonic-gate return retval;
4257c478bd9Sstevel@tonic-gate if (tmp < 0)
4267c478bd9Sstevel@tonic-gate tmp = DEFAULT_UDP_PREF_LIMIT;
42756a424ccSmp else if (tmp > HARD_UDP_LIMIT)
4287c478bd9Sstevel@tonic-gate /* In the unlikely case that a *really* big value is
4297c478bd9Sstevel@tonic-gate given, let 'em use as big as we think we can
4307c478bd9Sstevel@tonic-gate support. */
4317c478bd9Sstevel@tonic-gate tmp = HARD_UDP_LIMIT;
4327c478bd9Sstevel@tonic-gate context->udp_pref_limit = tmp;
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate
435505d05c7Sgtb retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
4367c478bd9Sstevel@tonic-gate
4377c478bd9Sstevel@tonic-gate if (tcp_only)
4387c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = 0;
4397c478bd9Sstevel@tonic-gate else if (message->length <= context->udp_pref_limit)
4407c478bd9Sstevel@tonic-gate socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
4417c478bd9Sstevel@tonic-gate else
4427c478bd9Sstevel@tonic-gate socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
4437c478bd9Sstevel@tonic-gate
444505d05c7Sgtb retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
4457c478bd9Sstevel@tonic-gate if (socktype2) {
4467c478bd9Sstevel@tonic-gate struct addrlist addrs2;
4477c478bd9Sstevel@tonic-gate
448159d09a2SMark Phalan retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
449159d09a2SMark Phalan socktype2, 0);
450159d09a2SMark Phalan #if 0
451159d09a2SMark Phalan if (retval2 == 0) {
452159d09a2SMark Phalan (void) merge_addrlists(&addrs, &addrs2);
453159d09a2SMark Phalan krb5int_free_addrlist(&addrs2);
454159d09a2SMark Phalan retval = 0;
455159d09a2SMark Phalan } else if (retval == KRB5_REALM_CANT_RESOLVE) {
456159d09a2SMark Phalan retval = retval2;
457159d09a2SMark Phalan }
458159d09a2SMark Phalan #else
459159d09a2SMark Phalan retval = retval2;
4607c478bd9Sstevel@tonic-gate if (retval == 0) {
4617c478bd9Sstevel@tonic-gate (void) merge_addrlists(&addrs, &addrs2);
4627c478bd9Sstevel@tonic-gate krb5int_free_addrlist(&addrs2);
4637c478bd9Sstevel@tonic-gate }
464159d09a2SMark Phalan #endif
4657c478bd9Sstevel@tonic-gate }
466159d09a2SMark Phalan
4677c478bd9Sstevel@tonic-gate if (addrs.naddrs > 0) {
468159d09a2SMark Phalan krb5_error_code err = 0;
469159d09a2SMark Phalan
470159d09a2SMark Phalan retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
471159d09a2SMark Phalan 0, 0, &addr_used, check_for_svc_unavailable, &err);
472159d09a2SMark Phalan switch (retval) {
473159d09a2SMark Phalan case 0:
474505d05c7Sgtb /*
475159d09a2SMark Phalan * Set use_master to 1 if we ended up talking to a master when
476159d09a2SMark Phalan * we didn't explicitly request to
477159d09a2SMark Phalan */
478159d09a2SMark Phalan if (*use_master == 0) {
479159d09a2SMark Phalan struct addrlist addrs3;
480*55fea89dSDan Cross retval = krb5_locate_kdc(context, realm, &addrs3, 1,
481159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_socktype,
482159d09a2SMark Phalan addrs.addrs[addr_used].ai->ai_family);
483159d09a2SMark Phalan if (retval == 0) {
484159d09a2SMark Phalan if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
485159d09a2SMark Phalan *use_master = 1;
486159d09a2SMark Phalan krb5int_free_addrlist (&addrs3);
487159d09a2SMark Phalan }
488159d09a2SMark Phalan }
4895e01956fSGlenn Barry
4905e01956fSGlenn Barry if (hostname_used) {
4915e01956fSGlenn Barry struct sockaddr *sa;
4925e01956fSGlenn Barry char buf[NI_MAXHOST];
4935e01956fSGlenn Barry int err;
4945e01956fSGlenn Barry
4955e01956fSGlenn Barry *hostname_used = NULL;
4965e01956fSGlenn Barry sa = addrs.addrs[addr_used].ai->ai_addr;
4975e01956fSGlenn Barry err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0,
4985e01956fSGlenn Barry AI_CANONNAME);
4995e01956fSGlenn Barry if (err)
5005e01956fSGlenn Barry err = getnameinfo (sa, socklen (sa), buf,
5015e01956fSGlenn Barry sizeof (buf), 0, 0,
5025e01956fSGlenn Barry NI_NUMERICHOST);
5035e01956fSGlenn Barry if (!err)
5045e01956fSGlenn Barry *hostname_used = strdup(buf);
5055e01956fSGlenn Barry /* don't sweat strdup fail */
5065e01956fSGlenn Barry }
507159d09a2SMark Phalan krb5int_free_addrlist (&addrs);
508159d09a2SMark Phalan return 0;
509159d09a2SMark Phalan default:
510159d09a2SMark Phalan break;
511159d09a2SMark Phalan /* Cases here are for constructing useful error messages. */
512159d09a2SMark Phalan case KRB5_KDC_UNREACH:
513159d09a2SMark Phalan if (err == KDC_ERR_SVC_UNAVAILABLE) {
514159d09a2SMark Phalan retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
515159d09a2SMark Phalan } else {
516159d09a2SMark Phalan krb5_set_error_message(context, retval,
5175e01956fSGlenn Barry dgettext(TEXT_DOMAIN,
5185e01956fSGlenn Barry "Cannot contact any KDC for realm '%.*s'"),
5195e01956fSGlenn Barry realm->length, realm->data);
520505d05c7Sgtb }
521159d09a2SMark Phalan break;
522505d05c7Sgtb }
523159d09a2SMark Phalan krb5int_free_addrlist (&addrs);
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate return retval;
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate
528159d09a2SMark Phalan #ifdef DEBUG
529159d09a2SMark Phalan
530159d09a2SMark Phalan #ifdef _WIN32
531159d09a2SMark Phalan #define dperror(MSG) \
532159d09a2SMark Phalan dprint("%s: an error occurred ... " \
533159d09a2SMark Phalan "\tline=%d errno=%m socketerrno=%m\n", \
534159d09a2SMark Phalan (MSG), __LINE__, errno, SOCKET_ERRNO)
535159d09a2SMark Phalan #else
536159d09a2SMark Phalan #define dperror(MSG) dprint("%s: %m\n", MSG, errno)
537159d09a2SMark Phalan #endif
538159d09a2SMark Phalan #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0)
539159d09a2SMark Phalan
540159d09a2SMark Phalan #else /* ! DEBUG */
541159d09a2SMark Phalan
542159d09a2SMark Phalan #define dperror(MSG) ((void)(MSG))
543159d09a2SMark Phalan #define dfprintf(ARGLIST) ((void)0)
544159d09a2SMark Phalan
545159d09a2SMark Phalan #endif
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate /*
5487c478bd9Sstevel@tonic-gate * Notes:
5497c478bd9Sstevel@tonic-gate *
5507c478bd9Sstevel@tonic-gate * Getting "connection refused" on a connected UDP socket causes
5517c478bd9Sstevel@tonic-gate * select to indicate write capability on UNIX, but only shows up
5527c478bd9Sstevel@tonic-gate * as an exception on Windows. (I don't think any UNIX system flags
5537c478bd9Sstevel@tonic-gate * the error as an exception.) So we check for both, or make it
5547c478bd9Sstevel@tonic-gate * system-specific.
5557c478bd9Sstevel@tonic-gate *
5567c478bd9Sstevel@tonic-gate * Always watch for responses from *any* of the servers. Eventually
5577c478bd9Sstevel@tonic-gate * fix the UDP code to do the same.
5587c478bd9Sstevel@tonic-gate *
5597c478bd9Sstevel@tonic-gate * To do:
5607c478bd9Sstevel@tonic-gate * - TCP NOPUSH/CORK socket options?
5617c478bd9Sstevel@tonic-gate * - error codes that don't suck
5627c478bd9Sstevel@tonic-gate * - getsockopt(SO_ERROR) to check connect status
5637c478bd9Sstevel@tonic-gate * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
5647c478bd9Sstevel@tonic-gate * connections already in progress
5657c478bd9Sstevel@tonic-gate */
5667c478bd9Sstevel@tonic-gate
567159d09a2SMark Phalan #include "cm.h"
5687c478bd9Sstevel@tonic-gate
getcurtime(struct timeval * tvp)5697c478bd9Sstevel@tonic-gate static int getcurtime (struct timeval *tvp)
5707c478bd9Sstevel@tonic-gate {
571159d09a2SMark Phalan #ifdef _WIN32
572159d09a2SMark Phalan struct _timeb tb;
573159d09a2SMark Phalan _ftime(&tb);
574159d09a2SMark Phalan tvp->tv_sec = tb.time;
575159d09a2SMark Phalan tvp->tv_usec = tb.millitm * 1000;
576159d09a2SMark Phalan /* Can _ftime fail? */
577159d09a2SMark Phalan return 0;
578159d09a2SMark Phalan #else
5797c478bd9Sstevel@tonic-gate if (gettimeofday(tvp, 0)) {
5807c478bd9Sstevel@tonic-gate dperror("gettimeofday");
5817c478bd9Sstevel@tonic-gate return errno;
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate return 0;
584159d09a2SMark Phalan #endif
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate * Call select and return results.
5897c478bd9Sstevel@tonic-gate * Input: interesting file descriptors and absolute timeout
5907c478bd9Sstevel@tonic-gate * Output: select return value (-1 or num fds ready) and fd_sets
5917c478bd9Sstevel@tonic-gate * Return: 0 (for i/o available or timeout) or error code.
5927c478bd9Sstevel@tonic-gate */
5937c478bd9Sstevel@tonic-gate krb5_error_code
krb5int_cm_call_select(const struct select_state * in,struct select_state * out,int * sret)5947c478bd9Sstevel@tonic-gate krb5int_cm_call_select (const struct select_state *in,
5957c478bd9Sstevel@tonic-gate struct select_state *out, int *sret)
5967c478bd9Sstevel@tonic-gate {
5977c478bd9Sstevel@tonic-gate struct timeval now, *timo;
5987c478bd9Sstevel@tonic-gate krb5_error_code e;
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate *out = *in;
6017c478bd9Sstevel@tonic-gate e = getcurtime(&now);
6027c478bd9Sstevel@tonic-gate if (e)
6037c478bd9Sstevel@tonic-gate return e;
6047c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec == 0)
6057c478bd9Sstevel@tonic-gate timo = 0;
6067c478bd9Sstevel@tonic-gate else {
6077c478bd9Sstevel@tonic-gate timo = &out->end_time;
6087c478bd9Sstevel@tonic-gate out->end_time.tv_sec -= now.tv_sec;
6097c478bd9Sstevel@tonic-gate out->end_time.tv_usec -= now.tv_usec;
6107c478bd9Sstevel@tonic-gate if (out->end_time.tv_usec < 0) {
6117c478bd9Sstevel@tonic-gate out->end_time.tv_usec += 1000000;
6127c478bd9Sstevel@tonic-gate out->end_time.tv_sec--;
6137c478bd9Sstevel@tonic-gate }
6147c478bd9Sstevel@tonic-gate if (out->end_time.tv_sec < 0) {
6157c478bd9Sstevel@tonic-gate *sret = 0;
6167c478bd9Sstevel@tonic-gate return 0;
6177c478bd9Sstevel@tonic-gate }
6187c478bd9Sstevel@tonic-gate }
6197c478bd9Sstevel@tonic-gate /*LINTED*/
6207c478bd9Sstevel@tonic-gate dprint("selecting on max=%d sockets [%F] timeout %t\n",
6217c478bd9Sstevel@tonic-gate /*LINTED*/
622159d09a2SMark Phalan out->max,
623159d09a2SMark Phalan &out->rfds, &out->wfds, &out->xfds, out->max,
624159d09a2SMark Phalan timo);
6257c478bd9Sstevel@tonic-gate *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
6267c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO;
6277c478bd9Sstevel@tonic-gate
628159d09a2SMark Phalan /* Solaris Kerberos */
6297c478bd9Sstevel@tonic-gate #ifdef DEBUG
6307c478bd9Sstevel@tonic-gate /*LINTED*/
6317c478bd9Sstevel@tonic-gate dprint("select returns %d", *sret);
6327c478bd9Sstevel@tonic-gate if (*sret < 0)
6337c478bd9Sstevel@tonic-gate /*LINTED*/
6347c478bd9Sstevel@tonic-gate dprint(", error = %E\n", e);
6357c478bd9Sstevel@tonic-gate else if (*sret == 0)
6367c478bd9Sstevel@tonic-gate /*LINTED*/
6377c478bd9Sstevel@tonic-gate dprint(" (timeout)\n");
6387c478bd9Sstevel@tonic-gate else
6397c478bd9Sstevel@tonic-gate /*LINTED*/
6407c478bd9Sstevel@tonic-gate dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
6417c478bd9Sstevel@tonic-gate #endif
6427c478bd9Sstevel@tonic-gate
6437c478bd9Sstevel@tonic-gate if (*sret < 0)
6447c478bd9Sstevel@tonic-gate return e;
6457c478bd9Sstevel@tonic-gate return 0;
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate static int service_tcp_fd (struct conn_state *conn,
6497c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags);
6507c478bd9Sstevel@tonic-gate static int service_udp_fd (struct conn_state *conn,
6517c478bd9Sstevel@tonic-gate struct select_state *selstate, int ssflags);
6527c478bd9Sstevel@tonic-gate
653159d09a2SMark Phalan static void
set_conn_state_msg_length(struct conn_state * state,const krb5_data * message)654159d09a2SMark Phalan set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
655159d09a2SMark Phalan {
656*55fea89dSDan Cross if (!message || message->length == 0)
657159d09a2SMark Phalan return;
658159d09a2SMark Phalan
659159d09a2SMark Phalan if (!state->is_udp) {
660159d09a2SMark Phalan
661159d09a2SMark Phalan state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff;
662159d09a2SMark Phalan state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff;
663159d09a2SMark Phalan state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff;
664159d09a2SMark Phalan state->x.out.msg_len_buf[3] = message->length & 0xff;
665159d09a2SMark Phalan
666159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
667159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
668159d09a2SMark Phalan state->x.out.sg_count = 2;
669159d09a2SMark Phalan
670159d09a2SMark Phalan } else {
671159d09a2SMark Phalan
672159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
673159d09a2SMark Phalan SG_SET(&state->x.out.sgbuf[1], 0, 0);
674159d09a2SMark Phalan state->x.out.sg_count = 1;
675159d09a2SMark Phalan
676159d09a2SMark Phalan }
677159d09a2SMark Phalan }
678159d09a2SMark Phalan
679159d09a2SMark Phalan
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate static int
setup_connection(struct conn_state * state,struct addrinfo * ai,const krb5_data * message,char ** udpbufp)6827c478bd9Sstevel@tonic-gate setup_connection (struct conn_state *state, struct addrinfo *ai,
683159d09a2SMark Phalan const krb5_data *message, char **udpbufp)
6847c478bd9Sstevel@tonic-gate {
6857c478bd9Sstevel@tonic-gate state->state = INITIALIZING;
6867c478bd9Sstevel@tonic-gate state->err = 0;
6877c478bd9Sstevel@tonic-gate state->x.out.sgp = state->x.out.sgbuf;
6887c478bd9Sstevel@tonic-gate state->addr = ai;
6897c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET;
6907c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0);
6917c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) {
692159d09a2SMark Phalan /*
6937c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
6947c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
6957c478bd9Sstevel@tonic-gate state->x.out.sg_count = 2;
696159d09a2SMark Phalan */
697*55fea89dSDan Cross
6987c478bd9Sstevel@tonic-gate state->is_udp = 0;
6997c478bd9Sstevel@tonic-gate state->service = service_tcp_fd;
700159d09a2SMark Phalan set_conn_state_msg_length (state, message);
7017c478bd9Sstevel@tonic-gate } else {
702159d09a2SMark Phalan /*
7037c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
7047c478bd9Sstevel@tonic-gate SG_SET(&state->x.out.sgbuf[1], 0, 0);
7057c478bd9Sstevel@tonic-gate state->x.out.sg_count = 1;
706159d09a2SMark Phalan */
707159d09a2SMark Phalan
7087c478bd9Sstevel@tonic-gate state->is_udp = 1;
7097c478bd9Sstevel@tonic-gate state->service = service_udp_fd;
710159d09a2SMark Phalan set_conn_state_msg_length (state, message);
7117c478bd9Sstevel@tonic-gate
7127c478bd9Sstevel@tonic-gate if (*udpbufp == 0) {
7137c478bd9Sstevel@tonic-gate *udpbufp = malloc(krb5_max_dgram_size);
7147c478bd9Sstevel@tonic-gate if (*udpbufp == 0) {
7157c478bd9Sstevel@tonic-gate dperror("malloc(krb5_max_dgram_size)");
7167c478bd9Sstevel@tonic-gate (void) closesocket(state->fd);
7177c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET;
7187c478bd9Sstevel@tonic-gate state->state = FAILED;
7197c478bd9Sstevel@tonic-gate return 1;
7207c478bd9Sstevel@tonic-gate }
7217c478bd9Sstevel@tonic-gate }
7227c478bd9Sstevel@tonic-gate state->x.in.buf = *udpbufp;
7237c478bd9Sstevel@tonic-gate state->x.in.bufsize = krb5_max_dgram_size;
7247c478bd9Sstevel@tonic-gate }
7257c478bd9Sstevel@tonic-gate return 0;
7267c478bd9Sstevel@tonic-gate }
7277c478bd9Sstevel@tonic-gate
7287c478bd9Sstevel@tonic-gate static int
start_connection(struct conn_state * state,struct select_state * selstate,struct sendto_callback_info * callback_info,krb5_data * callback_buffer)729*55fea89dSDan Cross start_connection (struct conn_state *state,
730*55fea89dSDan Cross struct select_state *selstate,
731159d09a2SMark Phalan struct sendto_callback_info* callback_info,
732159d09a2SMark Phalan krb5_data* callback_buffer)
7337c478bd9Sstevel@tonic-gate {
7347c478bd9Sstevel@tonic-gate int fd, e;
7357c478bd9Sstevel@tonic-gate struct addrinfo *ai = state->addr;
7367c478bd9Sstevel@tonic-gate
7377c478bd9Sstevel@tonic-gate /*LINTED*/
7387c478bd9Sstevel@tonic-gate dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
7397c478bd9Sstevel@tonic-gate /*LINTED*/
7407c478bd9Sstevel@tonic-gate ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
7417c478bd9Sstevel@tonic-gate fd = socket(ai->ai_family, ai->ai_socktype, 0);
7427c478bd9Sstevel@tonic-gate if (fd == INVALID_SOCKET) {
7437c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO;
7447c478bd9Sstevel@tonic-gate /*LINTED*/
7457c478bd9Sstevel@tonic-gate dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
7467c478bd9Sstevel@tonic-gate return -1; /* try other hosts */
7477c478bd9Sstevel@tonic-gate }
7487c478bd9Sstevel@tonic-gate /* Make it non-blocking. */
7497c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_STREAM) {
7507c478bd9Sstevel@tonic-gate static const int one = 1;
7517c478bd9Sstevel@tonic-gate static const struct linger lopt = { 0, 0 };
7527c478bd9Sstevel@tonic-gate
7537c478bd9Sstevel@tonic-gate if (ioctlsocket(fd, FIONBIO, (const void *) &one))
7547c478bd9Sstevel@tonic-gate dperror("sendto_kdc: ioctl(FIONBIO)");
7557c478bd9Sstevel@tonic-gate if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
7567c478bd9Sstevel@tonic-gate dperror("sendto_kdc: setsockopt(SO_LINGER)");
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate
7597c478bd9Sstevel@tonic-gate /* Start connecting to KDC. */
7607c478bd9Sstevel@tonic-gate /*LINTED*/
7617c478bd9Sstevel@tonic-gate dprint(" fd %d; connecting to %A...\n", fd, ai);
7627c478bd9Sstevel@tonic-gate e = connect(fd, ai->ai_addr, ai->ai_addrlen);
7637c478bd9Sstevel@tonic-gate if (e != 0) {
7647c478bd9Sstevel@tonic-gate /*
7657c478bd9Sstevel@tonic-gate * This is the path that should be followed for non-blocking
7667c478bd9Sstevel@tonic-gate * connections.
7677c478bd9Sstevel@tonic-gate */
7687c478bd9Sstevel@tonic-gate if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
7697c478bd9Sstevel@tonic-gate state->state = CONNECTING;
770159d09a2SMark Phalan state->fd = fd;
7717c478bd9Sstevel@tonic-gate } else {
7727c478bd9Sstevel@tonic-gate /*LINTED*/
7737c478bd9Sstevel@tonic-gate dprint("connect failed: %m\n", SOCKET_ERRNO);
774159d09a2SMark Phalan (void) closesocket(fd);
7757c478bd9Sstevel@tonic-gate state->err = SOCKET_ERRNO;
7767c478bd9Sstevel@tonic-gate state->state = FAILED;
7777c478bd9Sstevel@tonic-gate return -2;
7787c478bd9Sstevel@tonic-gate }
7797c478bd9Sstevel@tonic-gate } else {
7807c478bd9Sstevel@tonic-gate /*
7817c478bd9Sstevel@tonic-gate * Connect returned zero even though we tried to make it
7827c478bd9Sstevel@tonic-gate * non-blocking, which should have caused it to return before
7837c478bd9Sstevel@tonic-gate * finishing the connection. Oh well. Someone's network
7847c478bd9Sstevel@tonic-gate * stack is broken, but if they gave us a connection, use it.
7857c478bd9Sstevel@tonic-gate */
7867c478bd9Sstevel@tonic-gate state->state = WRITING;
787159d09a2SMark Phalan state->fd = fd;
7887c478bd9Sstevel@tonic-gate }
7897c478bd9Sstevel@tonic-gate /*LINTED*/
7907c478bd9Sstevel@tonic-gate dprint("new state = %s\n", state_strings[state->state]);
7917c478bd9Sstevel@tonic-gate
792159d09a2SMark Phalan
793159d09a2SMark Phalan /*
794159d09a2SMark Phalan * Here's where KPASSWD callback gets the socket information it needs for
795159d09a2SMark Phalan * a kpasswd request
796159d09a2SMark Phalan */
797159d09a2SMark Phalan if (callback_info) {
798159d09a2SMark Phalan
799*55fea89dSDan Cross e = callback_info->pfn_callback(state,
800*55fea89dSDan Cross callback_info->context,
801159d09a2SMark Phalan callback_buffer);
802159d09a2SMark Phalan if (e != 0) {
803159d09a2SMark Phalan dprint("callback failed: %m\n", e);
804159d09a2SMark Phalan (void) closesocket(fd);
805159d09a2SMark Phalan state->err = e;
806159d09a2SMark Phalan state->fd = INVALID_SOCKET;
807159d09a2SMark Phalan state->state = FAILED;
808159d09a2SMark Phalan return -3;
809159d09a2SMark Phalan }
810159d09a2SMark Phalan
811*55fea89dSDan Cross dprint("callback %p (message=%d@%p)\n",
812159d09a2SMark Phalan state,
813*55fea89dSDan Cross callback_buffer->length,
814159d09a2SMark Phalan callback_buffer->data);
815159d09a2SMark Phalan
816159d09a2SMark Phalan set_conn_state_msg_length( state, callback_buffer );
817159d09a2SMark Phalan }
8187c478bd9Sstevel@tonic-gate
8197c478bd9Sstevel@tonic-gate if (ai->ai_socktype == SOCK_DGRAM) {
8207c478bd9Sstevel@tonic-gate /* Send it now. */
8217c478bd9Sstevel@tonic-gate int ret;
8227c478bd9Sstevel@tonic-gate sg_buf *sg = &state->x.out.sgbuf[0];
8237c478bd9Sstevel@tonic-gate
8247c478bd9Sstevel@tonic-gate /*LINTED*/
8257c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
8267c478bd9Sstevel@tonic-gate ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
8277c478bd9Sstevel@tonic-gate if (ret != SG_LEN(sg)) {
8287c478bd9Sstevel@tonic-gate dperror("sendto");
8297c478bd9Sstevel@tonic-gate (void) closesocket(state->fd);
8307c478bd9Sstevel@tonic-gate state->fd = INVALID_SOCKET;
8317c478bd9Sstevel@tonic-gate state->state = FAILED;
832159d09a2SMark Phalan return -4;
8337c478bd9Sstevel@tonic-gate } else {
8347c478bd9Sstevel@tonic-gate state->state = READING;
8357c478bd9Sstevel@tonic-gate }
8367c478bd9Sstevel@tonic-gate }
837159d09a2SMark Phalan #ifdef DEBUG
838159d09a2SMark Phalan if (debug) {
839159d09a2SMark Phalan struct sockaddr_storage ss;
840159d09a2SMark Phalan socklen_t sslen = sizeof(ss);
841159d09a2SMark Phalan if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
842159d09a2SMark Phalan struct addrinfo hack_ai;
843159d09a2SMark Phalan memset(&hack_ai, 0, sizeof(hack_ai));
844159d09a2SMark Phalan hack_ai.ai_addr = (struct sockaddr *) &ss;
845159d09a2SMark Phalan hack_ai.ai_addrlen = sslen;
846159d09a2SMark Phalan hack_ai.ai_socktype = SOCK_DGRAM;
847159d09a2SMark Phalan hack_ai.ai_family = ai->ai_family;
848159d09a2SMark Phalan dprint("local socket address is %A\n", &hack_ai);
849159d09a2SMark Phalan }
850159d09a2SMark Phalan }
851159d09a2SMark Phalan #endif
8527c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->rfds);
8537c478bd9Sstevel@tonic-gate if (state->state == CONNECTING || state->state == WRITING)
8547c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->wfds);
8557c478bd9Sstevel@tonic-gate FD_SET(state->fd, &selstate->xfds);
8567c478bd9Sstevel@tonic-gate if (selstate->max <= state->fd)
8577c478bd9Sstevel@tonic-gate selstate->max = state->fd + 1;
8587c478bd9Sstevel@tonic-gate selstate->nfds++;
8597c478bd9Sstevel@tonic-gate
8607c478bd9Sstevel@tonic-gate /*LINTED*/
8617c478bd9Sstevel@tonic-gate dprint("new select vectors: %F\n",
8627c478bd9Sstevel@tonic-gate /*LINTED*/
8637c478bd9Sstevel@tonic-gate &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
8647c478bd9Sstevel@tonic-gate
8657c478bd9Sstevel@tonic-gate return 0;
8667c478bd9Sstevel@tonic-gate }
8677c478bd9Sstevel@tonic-gate
8687c478bd9Sstevel@tonic-gate /* Return 0 if we sent something, non-0 otherwise.
8697c478bd9Sstevel@tonic-gate If 0 is returned, the caller should delay waiting for a response.
8707c478bd9Sstevel@tonic-gate Otherwise, the caller should immediately move on to process the
8717c478bd9Sstevel@tonic-gate next connection. */
8727c478bd9Sstevel@tonic-gate static int
maybe_send(struct conn_state * conn,struct select_state * selstate,struct sendto_callback_info * callback_info,krb5_data * callback_buffer)873*55fea89dSDan Cross maybe_send (struct conn_state *conn,
874*55fea89dSDan Cross struct select_state *selstate,
875159d09a2SMark Phalan struct sendto_callback_info* callback_info,
876159d09a2SMark Phalan krb5_data* callback_buffer)
8777c478bd9Sstevel@tonic-gate {
8787c478bd9Sstevel@tonic-gate sg_buf *sg;
8797c478bd9Sstevel@tonic-gate
8807c478bd9Sstevel@tonic-gate /*LINTED*/
8817c478bd9Sstevel@tonic-gate dprint("maybe_send(@%p) state=%s type=%s\n", conn,
8827c478bd9Sstevel@tonic-gate /*LINTED*/
883159d09a2SMark Phalan state_strings[conn->state],
884159d09a2SMark Phalan conn->is_udp ? "udp" : "tcp");
8857c478bd9Sstevel@tonic-gate if (conn->state == INITIALIZING)
886159d09a2SMark Phalan return start_connection(conn, selstate, callback_info, callback_buffer);
8877c478bd9Sstevel@tonic-gate
8887c478bd9Sstevel@tonic-gate /* Did we already shut down this channel? */
8897c478bd9Sstevel@tonic-gate if (conn->state == FAILED) {
8907c478bd9Sstevel@tonic-gate dprint("connection already closed\n");
8917c478bd9Sstevel@tonic-gate return -1;
8927c478bd9Sstevel@tonic-gate }
8937c478bd9Sstevel@tonic-gate
8947c478bd9Sstevel@tonic-gate if (conn->addr->ai_socktype == SOCK_STREAM) {
8957c478bd9Sstevel@tonic-gate dprint("skipping stream socket\n");
8967c478bd9Sstevel@tonic-gate /* The select callback will handle flushing any data we
8977c478bd9Sstevel@tonic-gate haven't written yet, and we only write it once. */
8987c478bd9Sstevel@tonic-gate return -1;
8997c478bd9Sstevel@tonic-gate }
9007c478bd9Sstevel@tonic-gate
9017c478bd9Sstevel@tonic-gate /* UDP - Send message, possibly for the first time, possibly a
9027c478bd9Sstevel@tonic-gate retransmit if a previous attempt timed out. */
9037c478bd9Sstevel@tonic-gate sg = &conn->x.out.sgbuf[0];
9047c478bd9Sstevel@tonic-gate /*LINTED*/
9057c478bd9Sstevel@tonic-gate dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
9067c478bd9Sstevel@tonic-gate if (send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0) != SG_LEN(sg)) {
9077c478bd9Sstevel@tonic-gate dperror("send");
9087c478bd9Sstevel@tonic-gate /* Keep connection alive, we'll try again next pass.
9097c478bd9Sstevel@tonic-gate
9107c478bd9Sstevel@tonic-gate Is this likely to catch any errors we didn't get from the
9117c478bd9Sstevel@tonic-gate select callbacks? */
9127c478bd9Sstevel@tonic-gate return -1;
9137c478bd9Sstevel@tonic-gate }
9147c478bd9Sstevel@tonic-gate /* Yay, it worked. */
9157c478bd9Sstevel@tonic-gate return 0;
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate
9187c478bd9Sstevel@tonic-gate static void
kill_conn(struct conn_state * conn,struct select_state * selstate,int err)9197c478bd9Sstevel@tonic-gate kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
9207c478bd9Sstevel@tonic-gate {
9217c478bd9Sstevel@tonic-gate conn->state = FAILED;
9227c478bd9Sstevel@tonic-gate shutdown(conn->fd, SHUTDOWN_BOTH);
9237c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->rfds);
9247c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds);
9257c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->xfds);
9267c478bd9Sstevel@tonic-gate conn->err = err;
9277c478bd9Sstevel@tonic-gate /*LINTED*/
9287c478bd9Sstevel@tonic-gate dprint("abandoning connection %d: %m\n", conn->fd, err);
9297c478bd9Sstevel@tonic-gate /* Fix up max fd for next select call. */
9307c478bd9Sstevel@tonic-gate if (selstate->max == 1 + conn->fd) {
9317c478bd9Sstevel@tonic-gate while (selstate->max > 0
9327c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->rfds)
9337c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->wfds)
9347c478bd9Sstevel@tonic-gate && ! FD_ISSET(selstate->max-1, &selstate->xfds))
9357c478bd9Sstevel@tonic-gate selstate->max--;
9367c478bd9Sstevel@tonic-gate /*LINTED*/
9377c478bd9Sstevel@tonic-gate dprint("new max_fd + 1 is %d\n", selstate->max);
9387c478bd9Sstevel@tonic-gate }
9397c478bd9Sstevel@tonic-gate selstate->nfds--;
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate
942159d09a2SMark Phalan /* Check socket for error. */
943159d09a2SMark Phalan static int
get_so_error(int fd)944159d09a2SMark Phalan get_so_error(int fd)
945159d09a2SMark Phalan {
946159d09a2SMark Phalan int e, sockerr;
947159d09a2SMark Phalan socklen_t sockerrlen;
948159d09a2SMark Phalan
949159d09a2SMark Phalan sockerr = 0;
950159d09a2SMark Phalan sockerrlen = sizeof(sockerr);
951159d09a2SMark Phalan e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
952159d09a2SMark Phalan if (e != 0) {
953159d09a2SMark Phalan /* What to do now? */
954159d09a2SMark Phalan e = SOCKET_ERRNO;
955159d09a2SMark Phalan dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e);
956159d09a2SMark Phalan return e;
957159d09a2SMark Phalan }
958159d09a2SMark Phalan return sockerr;
959159d09a2SMark Phalan }
960159d09a2SMark Phalan
9617c478bd9Sstevel@tonic-gate /* Return nonzero only if we're finished and the caller should exit
9627c478bd9Sstevel@tonic-gate its loop. This happens in two cases: We have a complete message,
9637c478bd9Sstevel@tonic-gate or the socket has closed and no others are open. */
9647c478bd9Sstevel@tonic-gate
9657c478bd9Sstevel@tonic-gate static int
service_tcp_fd(struct conn_state * conn,struct select_state * selstate,int ssflags)9667c478bd9Sstevel@tonic-gate service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
9677c478bd9Sstevel@tonic-gate int ssflags)
9687c478bd9Sstevel@tonic-gate {
9697c478bd9Sstevel@tonic-gate krb5_error_code e = 0;
9707c478bd9Sstevel@tonic-gate int nwritten, nread;
9717c478bd9Sstevel@tonic-gate
9727c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
9737c478bd9Sstevel@tonic-gate abort();
9747c478bd9Sstevel@tonic-gate switch (conn->state) {
9757c478bd9Sstevel@tonic-gate SOCKET_WRITEV_TEMP tmp;
9767c478bd9Sstevel@tonic-gate
9777c478bd9Sstevel@tonic-gate case CONNECTING:
9787c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) {
9797c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending to us first. */
9807c478bd9Sstevel@tonic-gate e = EINVAL /* ?? */;
9817c478bd9Sstevel@tonic-gate kill_conn:
9827c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, e);
9837c478bd9Sstevel@tonic-gate if (e == EINVAL) {
9847c478bd9Sstevel@tonic-gate closesocket(conn->fd);
9857c478bd9Sstevel@tonic-gate conn->fd = INVALID_SOCKET;
9867c478bd9Sstevel@tonic-gate }
9877c478bd9Sstevel@tonic-gate return e == 0;
9887c478bd9Sstevel@tonic-gate }
9897c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) {
9907c478bd9Sstevel@tonic-gate handle_exception:
991159d09a2SMark Phalan e = get_so_error(conn->fd);
992159d09a2SMark Phalan if (e)
993159d09a2SMark Phalan dprint("socket error on exception fd: %m", e);
994159d09a2SMark Phalan else
995159d09a2SMark Phalan dprint("no socket error info available on exception fd");
9967c478bd9Sstevel@tonic-gate goto kill_conn;
9977c478bd9Sstevel@tonic-gate }
9987c478bd9Sstevel@tonic-gate
9997c478bd9Sstevel@tonic-gate /*
10007c478bd9Sstevel@tonic-gate * Connect finished -- but did it succeed or fail?
10017c478bd9Sstevel@tonic-gate * UNIX sets can_write if failed.
1002159d09a2SMark Phalan * Call getsockopt to see if error pending.
1003159d09a2SMark Phalan *
1004159d09a2SMark Phalan * (For most UNIX systems it works to just try writing the
1005159d09a2SMark Phalan * first time and detect an error. But Bill Dodd at IBM
1006159d09a2SMark Phalan * reports that some version of AIX, SIGPIPE can result.)
10077c478bd9Sstevel@tonic-gate */
1008159d09a2SMark Phalan e = get_so_error(conn->fd);
1009159d09a2SMark Phalan if (e) {
1010159d09a2SMark Phalan dprint("socket error on write fd: %m", e);
1011159d09a2SMark Phalan goto kill_conn;
1012159d09a2SMark Phalan }
10137c478bd9Sstevel@tonic-gate conn->state = WRITING;
10147c478bd9Sstevel@tonic-gate goto try_writing;
10157c478bd9Sstevel@tonic-gate
10167c478bd9Sstevel@tonic-gate case WRITING:
10177c478bd9Sstevel@tonic-gate if (ssflags & SSF_READ) {
10187c478bd9Sstevel@tonic-gate e = E2BIG;
10197c478bd9Sstevel@tonic-gate /* Bad -- the KDC shouldn't be sending anything yet. */
10207c478bd9Sstevel@tonic-gate goto kill_conn;
10217c478bd9Sstevel@tonic-gate }
10227c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION)
10237c478bd9Sstevel@tonic-gate goto handle_exception;
10247c478bd9Sstevel@tonic-gate
10257c478bd9Sstevel@tonic-gate try_writing:
10267c478bd9Sstevel@tonic-gate /*LINTED*/
10277c478bd9Sstevel@tonic-gate dprint("trying to writev %d (%d bytes) to fd %d\n",
10287c478bd9Sstevel@tonic-gate /*LINTED*/
10297c478bd9Sstevel@tonic-gate conn->x.out.sg_count,
10307c478bd9Sstevel@tonic-gate ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
10317c478bd9Sstevel@tonic-gate /*LINTED*/
10327c478bd9Sstevel@tonic-gate + SG_LEN(&conn->x.out.sgp[0])),
10337c478bd9Sstevel@tonic-gate conn->fd);
10347c478bd9Sstevel@tonic-gate nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
10357c478bd9Sstevel@tonic-gate conn->x.out.sg_count, tmp);
10367c478bd9Sstevel@tonic-gate if (nwritten < 0) {
10377c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO;
10387c478bd9Sstevel@tonic-gate /*LINTED*/
10397c478bd9Sstevel@tonic-gate dprint("failed: %m\n", e);
10407c478bd9Sstevel@tonic-gate goto kill_conn;
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate /*LINTED*/
10437c478bd9Sstevel@tonic-gate dprint("wrote %d bytes\n", nwritten);
10447c478bd9Sstevel@tonic-gate while (nwritten) {
10457c478bd9Sstevel@tonic-gate sg_buf *sgp = conn->x.out.sgp;
10467c478bd9Sstevel@tonic-gate if (nwritten < SG_LEN(sgp)) {
10477c478bd9Sstevel@tonic-gate /*LINTED*/
10487c478bd9Sstevel@tonic-gate SG_ADVANCE(sgp, nwritten);
10497c478bd9Sstevel@tonic-gate nwritten = 0;
10507c478bd9Sstevel@tonic-gate } else {
10517c478bd9Sstevel@tonic-gate nwritten -= SG_LEN(conn->x.out.sgp);
10527c478bd9Sstevel@tonic-gate conn->x.out.sgp++;
10537c478bd9Sstevel@tonic-gate conn->x.out.sg_count--;
10547c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0 && nwritten != 0)
10557c478bd9Sstevel@tonic-gate /* Wrote more than we wanted to? */
10567c478bd9Sstevel@tonic-gate abort();
10577c478bd9Sstevel@tonic-gate }
10587c478bd9Sstevel@tonic-gate }
10597c478bd9Sstevel@tonic-gate if (conn->x.out.sg_count == 0) {
10607c478bd9Sstevel@tonic-gate /* Done writing, switch to reading. */
10617c478bd9Sstevel@tonic-gate /* Don't call shutdown at this point because
10627c478bd9Sstevel@tonic-gate * some implementations cannot deal with half-closed connections.*/
10637c478bd9Sstevel@tonic-gate FD_CLR(conn->fd, &selstate->wfds);
10647c478bd9Sstevel@tonic-gate /* Q: How do we detect failures to send the remaining data
10657c478bd9Sstevel@tonic-gate to the remote side, since we're in non-blocking mode?
10667c478bd9Sstevel@tonic-gate Will we always get errors on the reading side? */
10677c478bd9Sstevel@tonic-gate /*LINTED*/
10687c478bd9Sstevel@tonic-gate dprint("switching fd %d to READING\n", conn->fd);
10697c478bd9Sstevel@tonic-gate conn->state = READING;
10707c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read = 0;
10717c478bd9Sstevel@tonic-gate conn->x.in.bufsize = 0;
10727c478bd9Sstevel@tonic-gate conn->x.in.buf = 0;
10737c478bd9Sstevel@tonic-gate conn->x.in.pos = 0;
10747c478bd9Sstevel@tonic-gate conn->x.in.n_left = 0;
10757c478bd9Sstevel@tonic-gate }
10767c478bd9Sstevel@tonic-gate return 0;
10777c478bd9Sstevel@tonic-gate
10787c478bd9Sstevel@tonic-gate case READING:
10797c478bd9Sstevel@tonic-gate if (ssflags & SSF_EXCEPTION) {
10807c478bd9Sstevel@tonic-gate if (conn->x.in.buf) {
10817c478bd9Sstevel@tonic-gate free(conn->x.in.buf);
10827c478bd9Sstevel@tonic-gate conn->x.in.buf = 0;
10837c478bd9Sstevel@tonic-gate }
10847c478bd9Sstevel@tonic-gate goto handle_exception;
10857c478bd9Sstevel@tonic-gate }
10867c478bd9Sstevel@tonic-gate
10877c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) {
10887c478bd9Sstevel@tonic-gate /* Reading data. */
10897c478bd9Sstevel@tonic-gate /*LINTED*/
10907c478bd9Sstevel@tonic-gate dprint("reading %d bytes of data from fd %d\n",
10917c478bd9Sstevel@tonic-gate (int) conn->x.in.n_left, conn->fd);
10927c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
10937c478bd9Sstevel@tonic-gate if (nread <= 0) {
10947c478bd9Sstevel@tonic-gate e = nread ? SOCKET_ERRNO : ECONNRESET;
10957c478bd9Sstevel@tonic-gate free(conn->x.in.buf);
10967c478bd9Sstevel@tonic-gate conn->x.in.buf = 0;
10977c478bd9Sstevel@tonic-gate goto kill_conn;
10987c478bd9Sstevel@tonic-gate }
10997c478bd9Sstevel@tonic-gate conn->x.in.n_left -= nread;
11007c478bd9Sstevel@tonic-gate conn->x.in.pos += nread;
1101159d09a2SMark Phalan /* Solaris Kerberos */
11027c478bd9Sstevel@tonic-gate if ((long)conn->x.in.n_left <= 0) {
11037c478bd9Sstevel@tonic-gate /* We win! */
11047c478bd9Sstevel@tonic-gate return 1;
11057c478bd9Sstevel@tonic-gate }
11067c478bd9Sstevel@tonic-gate } else {
11077c478bd9Sstevel@tonic-gate /* Reading length. */
11087c478bd9Sstevel@tonic-gate nread = SOCKET_READ(conn->fd,
11097c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
11107c478bd9Sstevel@tonic-gate 4 - conn->x.in.bufsizebytes_read);
11117c478bd9Sstevel@tonic-gate if (nread < 0) {
11127c478bd9Sstevel@tonic-gate e = SOCKET_ERRNO;
11137c478bd9Sstevel@tonic-gate goto kill_conn;
11147c478bd9Sstevel@tonic-gate }
11157c478bd9Sstevel@tonic-gate conn->x.in.bufsizebytes_read += nread;
11167c478bd9Sstevel@tonic-gate if (conn->x.in.bufsizebytes_read == 4) {
11177c478bd9Sstevel@tonic-gate unsigned long len;
11187c478bd9Sstevel@tonic-gate len = conn->x.in.bufsizebytes[0];
11197c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[1];
11207c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[2];
11217c478bd9Sstevel@tonic-gate len = (len << 8) + conn->x.in.bufsizebytes[3];
11227c478bd9Sstevel@tonic-gate /*LINTED*/
11237c478bd9Sstevel@tonic-gate dprint("received length on fd %d is %d\n", conn->fd, (int)len);
11247c478bd9Sstevel@tonic-gate /* Arbitrary 1M cap. */
11257c478bd9Sstevel@tonic-gate if (len > 1 * 1024 * 1024) {
11267c478bd9Sstevel@tonic-gate e = E2BIG;
11277c478bd9Sstevel@tonic-gate goto kill_conn;
11287c478bd9Sstevel@tonic-gate }
11297c478bd9Sstevel@tonic-gate conn->x.in.bufsize = conn->x.in.n_left = len;
11307c478bd9Sstevel@tonic-gate conn->x.in.buf = conn->x.in.pos = malloc(len);
11317c478bd9Sstevel@tonic-gate /*LINTED*/
11327c478bd9Sstevel@tonic-gate dprint("allocated %d byte buffer at %p\n", (int) len,
11337c478bd9Sstevel@tonic-gate conn->x.in.buf);
11347c478bd9Sstevel@tonic-gate if (conn->x.in.buf == 0) {
11357c478bd9Sstevel@tonic-gate /* allocation failure */
11367c478bd9Sstevel@tonic-gate e = errno;
11377c478bd9Sstevel@tonic-gate goto kill_conn;
11387c478bd9Sstevel@tonic-gate }
11397c478bd9Sstevel@tonic-gate }
11407c478bd9Sstevel@tonic-gate }
11417c478bd9Sstevel@tonic-gate break;
11427c478bd9Sstevel@tonic-gate
11437c478bd9Sstevel@tonic-gate default:
11447c478bd9Sstevel@tonic-gate abort();
11457c478bd9Sstevel@tonic-gate }
11467c478bd9Sstevel@tonic-gate return 0;
11477c478bd9Sstevel@tonic-gate }
11487c478bd9Sstevel@tonic-gate
11497c478bd9Sstevel@tonic-gate static int
service_udp_fd(struct conn_state * conn,struct select_state * selstate,int ssflags)11507c478bd9Sstevel@tonic-gate service_udp_fd(struct conn_state *conn, struct select_state *selstate,
11517c478bd9Sstevel@tonic-gate int ssflags)
11527c478bd9Sstevel@tonic-gate {
11537c478bd9Sstevel@tonic-gate int nread;
11547c478bd9Sstevel@tonic-gate
11557c478bd9Sstevel@tonic-gate if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
11567c478bd9Sstevel@tonic-gate abort();
11577c478bd9Sstevel@tonic-gate if (conn->state != READING)
11587c478bd9Sstevel@tonic-gate abort();
11597c478bd9Sstevel@tonic-gate
11607c478bd9Sstevel@tonic-gate nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
11617c478bd9Sstevel@tonic-gate if (nread < 0) {
11627c478bd9Sstevel@tonic-gate kill_conn(conn, selstate, SOCKET_ERRNO);
11637c478bd9Sstevel@tonic-gate return 0;
11647c478bd9Sstevel@tonic-gate }
11657c478bd9Sstevel@tonic-gate conn->x.in.pos = conn->x.in.buf + nread;
11667c478bd9Sstevel@tonic-gate return 1;
11677c478bd9Sstevel@tonic-gate }
11687c478bd9Sstevel@tonic-gate
11697c478bd9Sstevel@tonic-gate static int
service_fds(krb5_context context,struct select_state * selstate,struct conn_state * conns,size_t n_conns,int * winning_conn,struct select_state * seltemp,int (* msg_handler)(krb5_context,const krb5_data *,void *),void * msg_handler_data)1170159d09a2SMark Phalan service_fds (krb5_context context,
1171159d09a2SMark Phalan struct select_state *selstate,
1172159d09a2SMark Phalan struct conn_state *conns, size_t n_conns, int *winning_conn,
1173159d09a2SMark Phalan struct select_state *seltemp,
1174159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *),
1175159d09a2SMark Phalan void *msg_handler_data)
11767c478bd9Sstevel@tonic-gate {
11777c478bd9Sstevel@tonic-gate int e, selret;
11787c478bd9Sstevel@tonic-gate
11797c478bd9Sstevel@tonic-gate e = 0;
11807c478bd9Sstevel@tonic-gate while (selstate->nfds > 0
1181159d09a2SMark Phalan && (e = krb5int_cm_call_select(selstate, seltemp, &selret)) == 0) {
11827c478bd9Sstevel@tonic-gate int i;
11837c478bd9Sstevel@tonic-gate
11847c478bd9Sstevel@tonic-gate /*LINTED*/
11857c478bd9Sstevel@tonic-gate dprint("service_fds examining results, selret=%d\n", selret);
11867c478bd9Sstevel@tonic-gate
11877c478bd9Sstevel@tonic-gate if (selret == 0)
11887c478bd9Sstevel@tonic-gate /* Timeout, return to caller. */
11897c478bd9Sstevel@tonic-gate return 0;
11907c478bd9Sstevel@tonic-gate
11917c478bd9Sstevel@tonic-gate /* Got something on a socket, process it. */
11927c478bd9Sstevel@tonic-gate for (i = 0; i <= selstate->max && selret > 0 && i < n_conns; i++) {
11937c478bd9Sstevel@tonic-gate int ssflags;
11947c478bd9Sstevel@tonic-gate
11957c478bd9Sstevel@tonic-gate if (conns[i].fd == INVALID_SOCKET)
11967c478bd9Sstevel@tonic-gate continue;
11977c478bd9Sstevel@tonic-gate ssflags = 0;
1198159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->rfds))
11997c478bd9Sstevel@tonic-gate ssflags |= SSF_READ, selret--;
1200159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->wfds))
12017c478bd9Sstevel@tonic-gate ssflags |= SSF_WRITE, selret--;
1202159d09a2SMark Phalan if (FD_ISSET(conns[i].fd, &seltemp->xfds))
12037c478bd9Sstevel@tonic-gate ssflags |= SSF_EXCEPTION, selret--;
12047c478bd9Sstevel@tonic-gate if (!ssflags)
12057c478bd9Sstevel@tonic-gate continue;
12067c478bd9Sstevel@tonic-gate
12077c478bd9Sstevel@tonic-gate /*LINTED*/
12087c478bd9Sstevel@tonic-gate dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
12097c478bd9Sstevel@tonic-gate /*LINTED*/
12107c478bd9Sstevel@tonic-gate (ssflags & SSF_READ) ? "r" : "",
12117c478bd9Sstevel@tonic-gate /*LINTED*/
12127c478bd9Sstevel@tonic-gate (ssflags & SSF_WRITE) ? "w" : "",
12137c478bd9Sstevel@tonic-gate /*LINTED*/
12147c478bd9Sstevel@tonic-gate (ssflags & SSF_EXCEPTION) ? "x" : "",
12157c478bd9Sstevel@tonic-gate /*LINTED*/
12167c478bd9Sstevel@tonic-gate conns[i].fd, conns[i].addr,
12177c478bd9Sstevel@tonic-gate state_strings[(int) conns[i].state]);
12187c478bd9Sstevel@tonic-gate
12197c478bd9Sstevel@tonic-gate if (conns[i].service (&conns[i], selstate, ssflags)) {
1220159d09a2SMark Phalan int stop = 1;
1221159d09a2SMark Phalan
1222159d09a2SMark Phalan if (msg_handler != NULL) {
1223159d09a2SMark Phalan krb5_data reply;
1224159d09a2SMark Phalan
1225159d09a2SMark Phalan reply.data = conns[i].x.in.buf;
1226159d09a2SMark Phalan reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
1227159d09a2SMark Phalan
1228159d09a2SMark Phalan stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1229159d09a2SMark Phalan }
1230159d09a2SMark Phalan
1231159d09a2SMark Phalan if (stop) {
1232159d09a2SMark Phalan dprint("fd service routine says we're done\n");
1233159d09a2SMark Phalan *winning_conn = i;
1234159d09a2SMark Phalan return 1;
1235159d09a2SMark Phalan }
12367c478bd9Sstevel@tonic-gate }
12377c478bd9Sstevel@tonic-gate }
12387c478bd9Sstevel@tonic-gate }
12397c478bd9Sstevel@tonic-gate if (e != 0) {
12407c478bd9Sstevel@tonic-gate /*LINTED*/
12417c478bd9Sstevel@tonic-gate dprint("select returned %m\n", e);
12427c478bd9Sstevel@tonic-gate *winning_conn = -1;
12437c478bd9Sstevel@tonic-gate return 1;
12447c478bd9Sstevel@tonic-gate }
12457c478bd9Sstevel@tonic-gate return 0;
12467c478bd9Sstevel@tonic-gate }
12477c478bd9Sstevel@tonic-gate
12487c478bd9Sstevel@tonic-gate /*
12497c478bd9Sstevel@tonic-gate * Current worst-case timeout behavior:
12507c478bd9Sstevel@tonic-gate *
12517c478bd9Sstevel@tonic-gate * First pass, 1s per udp or tcp server, plus 2s at end.
12527c478bd9Sstevel@tonic-gate * Second pass, 1s per udp server, plus 4s.
12537c478bd9Sstevel@tonic-gate * Third pass, 1s per udp server, plus 8s.
12547c478bd9Sstevel@tonic-gate * Fourth => 16s, etc.
12557c478bd9Sstevel@tonic-gate *
12567c478bd9Sstevel@tonic-gate * Restated:
12577c478bd9Sstevel@tonic-gate * Per UDP server, 1s per pass.
12587c478bd9Sstevel@tonic-gate * Per TCP server, 1s.
12597c478bd9Sstevel@tonic-gate * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
12607c478bd9Sstevel@tonic-gate *
12617c478bd9Sstevel@tonic-gate * Total = 2**(P+1) + U*P + T - 2.
12627c478bd9Sstevel@tonic-gate *
12637c478bd9Sstevel@tonic-gate * If P=3, Total = 3*U + T + 14.
12647c478bd9Sstevel@tonic-gate * If P=4, Total = 4*U + T + 30.
12657c478bd9Sstevel@tonic-gate *
12667c478bd9Sstevel@tonic-gate * Note that if you try to reach two ports (e.g., both 88 and 750) on
12677c478bd9Sstevel@tonic-gate * one server, it counts as two.
12687c478bd9Sstevel@tonic-gate */
12697c478bd9Sstevel@tonic-gate
12707c478bd9Sstevel@tonic-gate krb5_error_code
12717c478bd9Sstevel@tonic-gate /*ARGSUSED*/
krb5int_sendto(krb5_context context,const krb5_data * message,const struct addrlist * addrs,struct sendto_callback_info * callback_info,krb5_data * reply,struct sockaddr * localaddr,socklen_t * localaddrlen,struct sockaddr * remoteaddr,socklen_t * remoteaddrlen,int * addr_used,int (* msg_handler)(krb5_context,const krb5_data *,void *),void * msg_handler_data)12727c478bd9Sstevel@tonic-gate krb5int_sendto (krb5_context context, const krb5_data *message,
1273159d09a2SMark Phalan const struct addrlist *addrs,
1274159d09a2SMark Phalan struct sendto_callback_info* callback_info, krb5_data *reply,
1275159d09a2SMark Phalan struct sockaddr *localaddr, socklen_t *localaddrlen,
1276159d09a2SMark Phalan struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1277159d09a2SMark Phalan int *addr_used,
1278159d09a2SMark Phalan /* return 0 -> keep going, 1 -> quit */
1279159d09a2SMark Phalan int (*msg_handler)(krb5_context, const krb5_data *, void *),
1280159d09a2SMark Phalan void *msg_handler_data)
12817c478bd9Sstevel@tonic-gate {
12827c478bd9Sstevel@tonic-gate int i, pass;
12837c478bd9Sstevel@tonic-gate int delay_this_pass = 2;
12847c478bd9Sstevel@tonic-gate krb5_error_code retval;
12857c478bd9Sstevel@tonic-gate struct conn_state *conns;
1286159d09a2SMark Phalan krb5_data *callback_data = 0;
12877c478bd9Sstevel@tonic-gate size_t n_conns, host;
1288159d09a2SMark Phalan struct select_state *sel_state;
12897c478bd9Sstevel@tonic-gate struct timeval now;
12907c478bd9Sstevel@tonic-gate int winning_conn = -1, e = 0;
12917c478bd9Sstevel@tonic-gate char *udpbuf = 0;
12927c478bd9Sstevel@tonic-gate
1293159d09a2SMark Phalan if (message)
1294159d09a2SMark Phalan dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
1295159d09a2SMark Phalan else
1296159d09a2SMark Phalan dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
1297159d09a2SMark Phalan print_addrlist(addrs);
1298159d09a2SMark Phalan dprint(")\n");
12997c478bd9Sstevel@tonic-gate
13007c478bd9Sstevel@tonic-gate reply->data = 0;
13017c478bd9Sstevel@tonic-gate reply->length = 0;
13027c478bd9Sstevel@tonic-gate
13037c478bd9Sstevel@tonic-gate n_conns = addrs->naddrs;
13047c478bd9Sstevel@tonic-gate conns = malloc(n_conns * sizeof(struct conn_state));
13057c478bd9Sstevel@tonic-gate if (conns == NULL) {
13067c478bd9Sstevel@tonic-gate return ENOMEM;
13077c478bd9Sstevel@tonic-gate }
1308159d09a2SMark Phalan
1309159d09a2SMark Phalan memset(conns, 0, n_conns * sizeof(struct conn_state));
1310159d09a2SMark Phalan
1311159d09a2SMark Phalan if (callback_info) {
1312159d09a2SMark Phalan callback_data = malloc(n_conns * sizeof(krb5_data));
1313159d09a2SMark Phalan if (callback_data == NULL) {
1314159d09a2SMark Phalan return ENOMEM;
1315159d09a2SMark Phalan }
1316159d09a2SMark Phalan
1317159d09a2SMark Phalan memset(callback_data, 0, n_conns * sizeof(krb5_data));
1318159d09a2SMark Phalan }
1319159d09a2SMark Phalan
13207c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) {
13217c478bd9Sstevel@tonic-gate conns[i].fd = INVALID_SOCKET;
13227c478bd9Sstevel@tonic-gate }
13237c478bd9Sstevel@tonic-gate
1324159d09a2SMark Phalan /* One for use here, listing all our fds in use, and one for
1325159d09a2SMark Phalan temporary use in service_fds, for the fds of interest. */
1326159d09a2SMark Phalan sel_state = malloc(2 * sizeof(*sel_state));
1327159d09a2SMark Phalan if (sel_state == NULL) {
1328159d09a2SMark Phalan free(conns);
1329159d09a2SMark Phalan return ENOMEM;
1330159d09a2SMark Phalan }
1331159d09a2SMark Phalan sel_state->max = 0;
1332159d09a2SMark Phalan sel_state->nfds = 0;
1333159d09a2SMark Phalan sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
1334159d09a2SMark Phalan FD_ZERO(&sel_state->rfds);
1335159d09a2SMark Phalan FD_ZERO(&sel_state->wfds);
1336159d09a2SMark Phalan FD_ZERO(&sel_state->xfds);
13377c478bd9Sstevel@tonic-gate
13387c478bd9Sstevel@tonic-gate
13397c478bd9Sstevel@tonic-gate /* Set up connections. */
13407c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) {
1341*55fea89dSDan Cross retval = setup_connection(&conns[host],
1342159d09a2SMark Phalan addrs->addrs[host].ai,
1343*55fea89dSDan Cross message,
1344159d09a2SMark Phalan &udpbuf);
13457c478bd9Sstevel@tonic-gate if (retval)
13467c478bd9Sstevel@tonic-gate continue;
13477c478bd9Sstevel@tonic-gate }
13487c478bd9Sstevel@tonic-gate for (pass = 0; pass < MAX_PASS; pass++) {
13497c478bd9Sstevel@tonic-gate /* Possible optimization: Make only one pass if TCP only.
13507c478bd9Sstevel@tonic-gate Stop making passes if all UDP ports are closed down. */
13517c478bd9Sstevel@tonic-gate /*LINTED*/
13527c478bd9Sstevel@tonic-gate dprint("pass %d delay=%d\n", pass, delay_this_pass);
13537c478bd9Sstevel@tonic-gate for (host = 0; host < n_conns; host++) {
13547c478bd9Sstevel@tonic-gate /*LINTED*/
13557c478bd9Sstevel@tonic-gate dprint("host %d\n", host);
13567c478bd9Sstevel@tonic-gate
13577c478bd9Sstevel@tonic-gate /* Send to the host, wait for a response, then move on. */
1358*55fea89dSDan Cross if (maybe_send(&conns[host],
1359159d09a2SMark Phalan sel_state,
1360159d09a2SMark Phalan callback_info,
1361159d09a2SMark Phalan (callback_info ? &callback_data[host] : NULL)))
13627c478bd9Sstevel@tonic-gate continue;
13637c478bd9Sstevel@tonic-gate
13647c478bd9Sstevel@tonic-gate retval = getcurtime(&now);
13657c478bd9Sstevel@tonic-gate if (retval)
13667c478bd9Sstevel@tonic-gate goto egress;
1367159d09a2SMark Phalan sel_state->end_time = now;
1368159d09a2SMark Phalan sel_state->end_time.tv_sec += 1;
1369159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1370159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data);
13717c478bd9Sstevel@tonic-gate if (e)
13727c478bd9Sstevel@tonic-gate break;
1373159d09a2SMark Phalan if (pass > 0 && sel_state->nfds == 0)
13747c478bd9Sstevel@tonic-gate /*
13757c478bd9Sstevel@tonic-gate * After the first pass, if we close all fds, break
13767c478bd9Sstevel@tonic-gate * out right away. During the first pass, it's okay,
13777c478bd9Sstevel@tonic-gate * we're probably about to open another connection.
13787c478bd9Sstevel@tonic-gate */
13797c478bd9Sstevel@tonic-gate break;
13807c478bd9Sstevel@tonic-gate }
13817c478bd9Sstevel@tonic-gate if (e)
13827c478bd9Sstevel@tonic-gate break;
13837c478bd9Sstevel@tonic-gate retval = getcurtime(&now);
13847c478bd9Sstevel@tonic-gate if (retval)
13857c478bd9Sstevel@tonic-gate goto egress;
13867c478bd9Sstevel@tonic-gate /* Possible optimization: Find a way to integrate this select
13877c478bd9Sstevel@tonic-gate call with the last one from the above loop, if the loop
13887c478bd9Sstevel@tonic-gate actually calls select. */
1389159d09a2SMark Phalan sel_state->end_time.tv_sec += delay_this_pass;
1390159d09a2SMark Phalan e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1391159d09a2SMark Phalan sel_state+1, msg_handler, msg_handler_data);
13927c478bd9Sstevel@tonic-gate if (e)
13937c478bd9Sstevel@tonic-gate break;
1394159d09a2SMark Phalan if (sel_state->nfds == 0)
13957c478bd9Sstevel@tonic-gate break;
13967c478bd9Sstevel@tonic-gate delay_this_pass *= 2;
13977c478bd9Sstevel@tonic-gate }
13987c478bd9Sstevel@tonic-gate
1399159d09a2SMark Phalan if (sel_state->nfds == 0) {
14007c478bd9Sstevel@tonic-gate /* No addresses? */
14017c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH;
14027c478bd9Sstevel@tonic-gate goto egress;
14037c478bd9Sstevel@tonic-gate }
14047c478bd9Sstevel@tonic-gate if (e == 0 || winning_conn < 0) {
14057c478bd9Sstevel@tonic-gate retval = KRB5_KDC_UNREACH;
14067c478bd9Sstevel@tonic-gate goto egress;
14077c478bd9Sstevel@tonic-gate }
14087c478bd9Sstevel@tonic-gate /* Success! */
14097c478bd9Sstevel@tonic-gate reply->data = conns[winning_conn].x.in.buf;
14107c478bd9Sstevel@tonic-gate reply->length = (conns[winning_conn].x.in.pos
14117c478bd9Sstevel@tonic-gate - conns[winning_conn].x.in.buf);
14127c478bd9Sstevel@tonic-gate /*LINTED*/
1413159d09a2SMark Phalan dprint("returning %d bytes in buffer %p\n",
1414159d09a2SMark Phalan (int) reply->length, reply->data);
14157c478bd9Sstevel@tonic-gate retval = 0;
14167c478bd9Sstevel@tonic-gate conns[winning_conn].x.in.buf = 0;
1417505d05c7Sgtb if (addr_used)
1418159d09a2SMark Phalan *addr_used = winning_conn;
14197c478bd9Sstevel@tonic-gate if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
1420159d09a2SMark Phalan (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
1421159d09a2SMark Phalan
142249bf4c2bSToomas Soome if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
1423159d09a2SMark Phalan (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
1424159d09a2SMark Phalan
14257c478bd9Sstevel@tonic-gate egress:
14267c478bd9Sstevel@tonic-gate for (i = 0; i < n_conns; i++) {
14277c478bd9Sstevel@tonic-gate if (conns[i].fd != INVALID_SOCKET)
1428159d09a2SMark Phalan closesocket(conns[i].fd);
14297c478bd9Sstevel@tonic-gate if (conns[i].state == READING
14307c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != 0
14317c478bd9Sstevel@tonic-gate && conns[i].x.in.buf != udpbuf)
14327c478bd9Sstevel@tonic-gate free(conns[i].x.in.buf);
1433159d09a2SMark Phalan if (callback_info) {
1434159d09a2SMark Phalan callback_info->pfn_cleanup( callback_info->context, &callback_data[i]);
1435159d09a2SMark Phalan }
14367c478bd9Sstevel@tonic-gate }
1437159d09a2SMark Phalan
1438*55fea89dSDan Cross if (callback_data)
1439159d09a2SMark Phalan free(callback_data);
1440159d09a2SMark Phalan
14417c478bd9Sstevel@tonic-gate free(conns);
14427c478bd9Sstevel@tonic-gate if (reply->data != udpbuf)
14437c478bd9Sstevel@tonic-gate free(udpbuf);
1444159d09a2SMark Phalan free(sel_state);
14457c478bd9Sstevel@tonic-gate return retval;
14467c478bd9Sstevel@tonic-gate }
1447