1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * lib/krb5/os/sendto_kdc.c
5 * Copyright 1990,1991,2001,2002,2004,2005,2007,2008 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
28 * Send packet to KDC for realm; wait for response, retransmitting
32 #include "fake-addrinfo.h"
35 #ifdef HAVE_SYS_TIME_H
42 #include <sys/timeb.h>
46 #include <sys/select.h>
51 #include <sys/ioctl.h>
52 #ifdef HAVE_SYS_FILIO_H
53 #include <sys/filio.h>
58 #define DEFAULT_UDP_PREF_LIMIT 1465
59 #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */
64 int krb5int_debug_sendto_kdc = 0;
65 #define debug krb5int_debug_sendto_kdc
68 default_debug_handler (const void *data, size_t len)
72 if (logfile == NULL) {
73 logfile = fopen("/tmp/sendto_kdc.log", "a");
76 setbuf(logfile, NULL);
78 fwrite(data, 1, len, logfile);
80 fwrite(data, 1, len, stderr);
81 /* stderr is unbuffered */
85 void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = default_debug_handler;
88 put(const void *ptr, size_t len)
90 (*krb5int_sendtokdc_debug_handler)(ptr, len);
93 putstr(const char *str)
95 put(str, strlen(str));
98 void (*krb5int_sendtokdc_debug_handler) (const void *, size_t) = 0;
101 #define dprint krb5int_debug_fprint
103 krb5int_debug_fprint (const char *fmt, ...)
108 /* Temporaries for variable arguments, etc. */
109 krb5_error_code kerr;
111 fd_set *rfds, *wfds, *xfds;
117 char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
120 #define max(a,b) ((a) > (b) ? (a) : (b))
122 char tmpbuf[max(NI_MAXHOST + NI_MAXSERV + 30, 200)];
125 if (!krb5int_debug_sendto_kdc)
130 #define putf(FMT,X) (snprintf(tmpbuf,sizeof(tmpbuf),FMT,X),putstr(tmpbuf))
132 for (; *fmt; fmt++) {
136 for (fmt2 = fmt+1; *fmt2; fmt2++)
141 fmt += len - 1; /* then fmt++ in loop header */
144 /* After this, always processing a '%' sequence. */
151 /* %E => krb5_error_code */
152 kerr = va_arg(args, krb5_error_code);
153 snprintf(tmpbuf, sizeof(tmpbuf), "%lu/", (unsigned long) kerr);
155 p = error_message(kerr);
159 /* %m => errno value (int) */
160 /* Like syslog's %m except the errno value is passed in
161 rather than the current value. */
162 err = va_arg(args, int);
165 #ifdef HAVE_STRERROR_R
166 if (strerror_r(err, tmpbuf, sizeof(tmpbuf)) == 0)
174 /* %F => fd_set *, fd_set *, fd_set *, int */
175 rfds = va_arg(args, fd_set *);
176 wfds = va_arg(args, fd_set *);
177 xfds = va_arg(args, fd_set *);
178 maxfd = va_arg(args, int);
180 for (i = 0; i < maxfd; i++) {
181 int r = FD_ISSET(i, rfds);
182 int w = wfds && FD_ISSET(i, wfds);
183 int x = xfds && FD_ISSET(i, xfds);
198 p = va_arg(args, const char *);
202 /* %t => struct timeval * */
203 tv = va_arg(args, struct timeval *);
205 snprintf(tmpbuf, sizeof(tmpbuf), "%ld.%06ld",
206 (long) tv->tv_sec, (long) tv->tv_usec);
213 putf("%d", va_arg(args, int));
217 putf("%p", va_arg(args, void*));
221 ai = va_arg(args, struct addrinfo *);
222 krb5int_buf_init_dynamic(&buf);
223 if (ai->ai_socktype == SOCK_DGRAM)
224 krb5int_buf_add(&buf, "dgram");
225 else if (ai->ai_socktype == SOCK_STREAM)
226 krb5int_buf_add(&buf, "stream");
228 krb5int_buf_add_fmt(&buf, "socktype%d", ai->ai_socktype);
230 if (0 != getnameinfo (ai->ai_addr, ai->ai_addrlen,
231 addrbuf, sizeof (addrbuf),
232 portbuf, sizeof (portbuf),
233 NI_NUMERICHOST | NI_NUMERICSERV)) {
234 if (ai->ai_addr->sa_family == AF_UNSPEC)
235 krb5int_buf_add(&buf, " AF_UNSPEC");
237 krb5int_buf_add_fmt(&buf, " af%d", ai->ai_addr->sa_family);
239 krb5int_buf_add_fmt(&buf, " %s.%s", addrbuf, portbuf);
240 if (krb5int_buf_data(&buf))
241 putstr(krb5int_buf_data(&buf));
242 krb5int_free_buf(&buf);
245 /* %D => krb5_data * */
246 d = va_arg(args, krb5_data *);
247 /* may not be nul-terminated */
248 put(d->data, d->length);
256 #define print_addrlist krb5int_print_addrlist
258 print_addrlist (const struct addrlist *a)
261 dprint("%d{", a->naddrs);
262 for (i = 0; i < a->naddrs; i++)
263 dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai);
268 merge_addrlists (struct addrlist *dest, struct addrlist *src)
270 /* Wouldn't it be nice if we could filter out duplicates? The
271 alloc/free handling makes that pretty difficult though. */
275 dprint("merging addrlists:\n\tlist1: ");
276 for (i = 0; i < dest->naddrs; i++)
277 dprint(" %A", dest->addrs[i].ai);
278 dprint("\n\tlist2: ");
279 for (i = 0; i < src->naddrs; i++)
280 dprint(" %A", src->addrs[i].ai);
283 err = krb5int_grow_addrlist (dest, src->naddrs);
286 for (i = 0; i < src->naddrs; i++) {
287 dest->addrs[dest->naddrs + i] = src->addrs[i];
288 src->addrs[i].ai = 0;
289 src->addrs[i].freefn = 0;
295 for (i = 0; i < dest->naddrs; i++)
296 dprint(" %A", dest->addrs[i].ai);
303 in_addrlist (struct addrinfo *thisaddr, struct addrlist *list)
306 for (i = 0; i < list->naddrs; i++) {
307 if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen
308 && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr,
309 thisaddr->ai_addrlen))
316 check_for_svc_unavailable (krb5_context context,
317 const krb5_data *reply,
318 void *msg_handler_data)
320 krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
324 if (krb5_is_krb_error(reply)) {
325 krb5_error *err_reply;
327 if (decode_krb5_error(reply, &err_reply) == 0) {
328 *retval = err_reply->error;
329 krb5_free_error(context, err_reply);
331 /* Returning 0 means continue to next KDC */
332 return (*retval != KDC_ERR_SVC_UNAVAILABLE);
340 * send the formatted request 'message' to a KDC for realm 'realm' and
341 * return the response (if any) in 'reply'.
343 * If the message is sent and a response is received, 0 is returned,
344 * otherwise an error code is returned.
346 * The storage for 'reply' is allocated and should be freed by the caller
351 krb5_sendto_kdc (krb5_context context, const krb5_data *message,
352 const krb5_data *realm, krb5_data *reply,
353 int *use_master, int tcp_only)
355 krb5_error_code retval, retval2;
356 struct addrlist addrs;
357 int socktype1 = 0, socktype2 = 0, addr_used;
360 * find KDC location(s) for realm
364 * BUG: This code won't return "interesting" errors (e.g., out of mem,
365 * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be
366 * ignored from one query of two, but if only one query is done, or
367 * both return that error, it should be returned to the caller. Also,
368 * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
369 * should probably be returned as well.
372 dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n",
373 message->length, message->data, realm, *use_master, tcp_only);
375 if (!tcp_only && context->udp_pref_limit < 0) {
377 retval = profile_get_integer(context->profile,
378 KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
379 DEFAULT_UDP_PREF_LIMIT, &tmp);
383 tmp = DEFAULT_UDP_PREF_LIMIT;
384 else if (tmp > HARD_UDP_LIMIT)
385 /* In the unlikely case that a *really* big value is
386 given, let 'em use as big as we think we can
388 tmp = HARD_UDP_LIMIT;
389 context->udp_pref_limit = tmp;
392 retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
395 socktype1 = SOCK_STREAM, socktype2 = 0;
396 else if (message->length <= (unsigned int) context->udp_pref_limit)
397 socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM;
399 socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
401 retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
403 struct addrlist addrs2;
405 retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
409 (void) merge_addrlists(&addrs, &addrs2);
410 krb5int_free_addrlist(&addrs2);
412 } else if (retval == KRB5_REALM_CANT_RESOLVE) {
418 (void) merge_addrlists(&addrs, &addrs2);
419 krb5int_free_addrlist(&addrs2);
424 if (addrs.naddrs > 0) {
425 krb5_error_code err = 0;
427 retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
428 0, 0, &addr_used, check_for_svc_unavailable, &err);
432 * Set use_master to 1 if we ended up talking to a master when
433 * we didn't explicitly request to
435 if (*use_master == 0) {
436 struct addrlist addrs3;
437 retval = krb5_locate_kdc(context, realm, &addrs3, 1,
438 addrs.addrs[addr_used].ai->ai_socktype,
439 addrs.addrs[addr_used].ai->ai_family);
441 if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
443 krb5int_free_addrlist (&addrs3);
446 krb5int_free_addrlist (&addrs);
450 /* Cases here are for constructing useful error messages. */
451 case KRB5_KDC_UNREACH:
452 if (err == KDC_ERR_SVC_UNAVAILABLE) {
453 retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
455 krb5_set_error_message(context, retval,
456 "Cannot contact any KDC for realm '%.*s'",
457 realm->length, realm->data);
461 krb5int_free_addrlist (&addrs);
469 #define dperror(MSG) \
470 dprint("%s: an error occurred ... " \
471 "\tline=%d errno=%m socketerrno=%m\n", \
472 (MSG), __LINE__, errno, SOCKET_ERRNO)
474 #define dperror(MSG) dprint("%s: %m\n", MSG, errno)
476 #define dfprintf(ARGLIST) (debug ? fprintf ARGLIST : 0)
480 #define dperror(MSG) ((void)(MSG))
481 #define dfprintf(ARGLIST) ((void)0)
488 * Getting "connection refused" on a connected UDP socket causes
489 * select to indicate write capability on UNIX, but only shows up
490 * as an exception on Windows. (I don't think any UNIX system flags
491 * the error as an exception.) So we check for both, or make it
494 * Always watch for responses from *any* of the servers. Eventually
495 * fix the UDP code to do the same.
498 * - TCP NOPUSH/CORK socket options?
499 * - error codes that don't suck
500 * - getsockopt(SO_ERROR) to check connect status
501 * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
502 * connections already in progress
508 getcurtime (struct timeval *tvp)
513 tvp->tv_sec = tb.time;
514 tvp->tv_usec = tb.millitm * 1000;
515 /* Can _ftime fail? */
518 if (gettimeofday(tvp, 0)) {
519 dperror("gettimeofday");
527 * Call select and return results.
528 * Input: interesting file descriptors and absolute timeout
529 * Output: select return value (-1 or num fds ready) and fd_sets
530 * Return: 0 (for i/o available or timeout) or error code.
533 krb5int_cm_call_select (const struct select_state *in,
534 struct select_state *out, int *sret)
536 struct timeval now, *timo;
540 e = getcurtime(&now);
543 if (out->end_time.tv_sec == 0)
546 timo = &out->end_time;
547 out->end_time.tv_sec -= now.tv_sec;
548 out->end_time.tv_usec -= now.tv_usec;
549 if (out->end_time.tv_usec < 0) {
550 out->end_time.tv_usec += 1000000;
551 out->end_time.tv_sec--;
553 if (out->end_time.tv_sec < 0) {
558 dprint("selecting on max=%d sockets [%F] timeout %t\n",
560 &out->rfds, &out->wfds, &out->xfds, out->max,
562 *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, timo);
565 dprint("select returns %d", *sret);
567 dprint(", error = %E\n", e);
569 dprint(" (timeout)\n");
571 dprint(":%F\n", &out->rfds, &out->wfds, &out->xfds, out->max);
578 static int service_tcp_fd (struct conn_state *conn,
579 struct select_state *selstate, int ssflags);
580 static int service_udp_fd (struct conn_state *conn,
581 struct select_state *selstate, int ssflags);
584 set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
586 if (!message || message->length == 0)
589 if (!state->is_udp) {
591 store_32_be(message->length, state->x.out.msg_len_buf);
592 SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
593 SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
594 state->x.out.sg_count = 2;
598 SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
599 SG_SET(&state->x.out.sgbuf[1], 0, 0);
600 state->x.out.sg_count = 1;
608 setup_connection (struct conn_state *state, struct addrinfo *ai,
609 const krb5_data *message, char **udpbufp)
611 state->state = INITIALIZING;
613 state->x.out.sgp = state->x.out.sgbuf;
615 state->fd = INVALID_SOCKET;
616 SG_SET(&state->x.out.sgbuf[1], 0, 0);
617 if (ai->ai_socktype == SOCK_STREAM) {
619 SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
620 SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
621 state->x.out.sg_count = 2;
625 state->service = service_tcp_fd;
626 set_conn_state_msg_length (state, message);
629 SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
630 SG_SET(&state->x.out.sgbuf[1], 0, 0);
631 state->x.out.sg_count = 1;
635 state->service = service_udp_fd;
636 set_conn_state_msg_length (state, message);
639 *udpbufp = malloc(krb5_max_dgram_size);
641 dperror("malloc(krb5_max_dgram_size)");
642 state->state = FAILED;
646 state->x.in.buf = *udpbufp;
647 state->x.in.bufsize = krb5_max_dgram_size;
652 start_connection (struct conn_state *state,
653 struct select_state *selstate,
654 struct sendto_callback_info* callback_info,
655 krb5_data* callback_buffer)
658 struct addrinfo *ai = state->addr;
660 dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
661 ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
662 fd = socket(ai->ai_family, ai->ai_socktype, 0);
663 if (fd == INVALID_SOCKET) {
664 state->err = SOCKET_ERRNO;
665 dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
666 return -1; /* try other hosts */
668 #ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value. */
669 if (fd >= FD_SETSIZE) {
672 dprint("socket: fd %d too high\n", fd);
677 /* Make it non-blocking. */
678 if (ai->ai_socktype == SOCK_STREAM) {
679 static const int one = 1;
680 static const struct linger lopt = { 0, 0 };
682 if (ioctlsocket(fd, FIONBIO, (const void *) &one))
683 dperror("sendto_kdc: ioctl(FIONBIO)");
684 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
685 dperror("sendto_kdc: setsockopt(SO_LINGER)");
688 /* Start connecting to KDC. */
689 dprint(" fd %d; connecting to %A...\n", fd, ai);
690 e = connect(fd, ai->ai_addr, ai->ai_addrlen);
693 * This is the path that should be followed for non-blocking
696 if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
697 state->state = CONNECTING;
700 dprint("connect failed: %m\n", SOCKET_ERRNO);
701 (void) closesocket(fd);
702 state->err = SOCKET_ERRNO;
703 state->state = FAILED;
708 * Connect returned zero even though we tried to make it
709 * non-blocking, which should have caused it to return before
710 * finishing the connection. Oh well. Someone's network
711 * stack is broken, but if they gave us a connection, use it.
713 state->state = WRITING;
716 dprint("new state = %s\n", state_strings[state->state]);
720 * Here's where KPASSWD callback gets the socket information it needs for
725 e = callback_info->pfn_callback(state,
726 callback_info->context,
729 dprint("callback failed: %m\n", e);
730 (void) closesocket(fd);
732 state->fd = INVALID_SOCKET;
733 state->state = FAILED;
737 dprint("callback %p (message=%d@%p)\n",
739 callback_buffer->length,
740 callback_buffer->data);
742 set_conn_state_msg_length( state, callback_buffer );
745 if (ai->ai_socktype == SOCK_DGRAM) {
748 sg_buf *sg = &state->x.out.sgbuf[0];
750 dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
751 ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
752 if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
754 (void) closesocket(state->fd);
755 state->fd = INVALID_SOCKET;
756 state->state = FAILED;
759 state->state = READING;
764 struct sockaddr_storage ss;
765 socklen_t sslen = sizeof(ss);
766 if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
767 struct addrinfo hack_ai;
768 memset(&hack_ai, 0, sizeof(hack_ai));
769 hack_ai.ai_addr = (struct sockaddr *) &ss;
770 hack_ai.ai_addrlen = sslen;
771 hack_ai.ai_socktype = SOCK_DGRAM;
772 hack_ai.ai_family = ai->ai_family;
773 dprint("local socket address is %A\n", &hack_ai);
777 FD_SET(state->fd, &selstate->rfds);
778 if (state->state == CONNECTING || state->state == WRITING)
779 FD_SET(state->fd, &selstate->wfds);
780 FD_SET(state->fd, &selstate->xfds);
781 if (selstate->max <= state->fd)
782 selstate->max = state->fd + 1;
785 dprint("new select vectors: %F\n",
786 &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);
791 /* Return 0 if we sent something, non-0 otherwise.
792 If 0 is returned, the caller should delay waiting for a response.
793 Otherwise, the caller should immediately move on to process the
796 maybe_send (struct conn_state *conn,
797 struct select_state *selstate,
798 struct sendto_callback_info* callback_info,
799 krb5_data* callback_buffer)
804 dprint("maybe_send(@%p) state=%s type=%s\n", conn,
805 state_strings[conn->state],
806 conn->is_udp ? "udp" : "tcp");
807 if (conn->state == INITIALIZING)
808 return start_connection(conn, selstate, callback_info, callback_buffer);
810 /* Did we already shut down this channel? */
811 if (conn->state == FAILED) {
812 dprint("connection already closed\n");
816 if (conn->addr->ai_socktype == SOCK_STREAM) {
817 dprint("skipping stream socket\n");
818 /* The select callback will handle flushing any data we
819 haven't written yet, and we only write it once. */
823 /* UDP - Send message, possibly for the first time, possibly a
824 retransmit if a previous attempt timed out. */
825 sg = &conn->x.out.sgbuf[0];
826 dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
827 ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
828 if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
830 /* Keep connection alive, we'll try again next pass.
832 Is this likely to catch any errors we didn't get from the
836 /* Yay, it worked. */
841 kill_conn(struct conn_state *conn, struct select_state *selstate, int err)
843 conn->state = FAILED;
844 shutdown(conn->fd, SHUTDOWN_BOTH);
845 FD_CLR(conn->fd, &selstate->rfds);
846 FD_CLR(conn->fd, &selstate->wfds);
847 FD_CLR(conn->fd, &selstate->xfds);
849 dprint("abandoning connection %d: %m\n", conn->fd, err);
850 /* Fix up max fd for next select call. */
851 if (selstate->max == 1 + conn->fd) {
852 while (selstate->max > 0
853 && ! FD_ISSET(selstate->max-1, &selstate->rfds)
854 && ! FD_ISSET(selstate->max-1, &selstate->wfds)
855 && ! FD_ISSET(selstate->max-1, &selstate->xfds))
857 dprint("new max_fd + 1 is %d\n", selstate->max);
862 /* Check socket for error. */
867 socklen_t sockerrlen;
870 sockerrlen = sizeof(sockerr);
871 e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
873 /* What to do now? */
875 dprint("getsockopt(SO_ERROR) on fd failed: %m\n", e);
881 /* Return nonzero only if we're finished and the caller should exit
882 its loop. This happens in two cases: We have a complete message,
883 or the socket has closed and no others are open. */
886 service_tcp_fd (struct conn_state *conn, struct select_state *selstate,
889 krb5_error_code e = 0;
890 ssize_t nwritten, nread;
892 if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
894 switch (conn->state) {
895 SOCKET_WRITEV_TEMP tmp;
898 if (ssflags & SSF_READ) {
899 /* Bad -- the KDC shouldn't be sending to us first. */
902 kill_conn(conn, selstate, e);
904 closesocket(conn->fd);
905 conn->fd = INVALID_SOCKET;
909 if (ssflags & SSF_EXCEPTION) {
911 e = get_so_error(conn->fd);
913 dprint("socket error on exception fd: %m", e);
915 dprint("no socket error info available on exception fd");
920 * Connect finished -- but did it succeed or fail?
921 * UNIX sets can_write if failed.
922 * Call getsockopt to see if error pending.
924 * (For most UNIX systems it works to just try writing the
925 * first time and detect an error. But Bill Dodd at IBM
926 * reports that some version of AIX, SIGPIPE can result.)
928 e = get_so_error(conn->fd);
930 dprint("socket error on write fd: %m", e);
933 conn->state = WRITING;
937 if (ssflags & SSF_READ) {
939 /* Bad -- the KDC shouldn't be sending anything yet. */
942 if (ssflags & SSF_EXCEPTION)
943 goto handle_exception;
946 dprint("trying to writev %d (%d bytes) to fd %d\n",
947 conn->x.out.sg_count,
948 ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
949 + SG_LEN(&conn->x.out.sgp[0])),
951 nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
952 conn->x.out.sg_count, tmp);
955 dprint("failed: %m\n", e);
958 dprint("wrote %d bytes\n", nwritten);
960 sg_buf *sgp = conn->x.out.sgp;
961 if ((size_t) nwritten < SG_LEN(sgp)) {
962 SG_ADVANCE(sgp, (size_t) nwritten);
965 nwritten -= SG_LEN(sgp);
967 conn->x.out.sg_count--;
968 if (conn->x.out.sg_count == 0 && nwritten != 0)
969 /* Wrote more than we wanted to? */
973 if (conn->x.out.sg_count == 0) {
974 /* Done writing, switch to reading. */
975 /* Don't call shutdown at this point because
976 * some implementations cannot deal with half-closed connections.*/
977 FD_CLR(conn->fd, &selstate->wfds);
978 /* Q: How do we detect failures to send the remaining data
979 to the remote side, since we're in non-blocking mode?
980 Will we always get errors on the reading side? */
981 dprint("switching fd %d to READING\n", conn->fd);
982 conn->state = READING;
983 conn->x.in.bufsizebytes_read = 0;
984 conn->x.in.bufsize = 0;
987 conn->x.in.n_left = 0;
992 if (ssflags & SSF_EXCEPTION) {
993 if (conn->x.in.buf) {
994 free(conn->x.in.buf);
997 goto handle_exception;
1000 if (conn->x.in.bufsizebytes_read == 4) {
1002 dprint("reading %d bytes of data from fd %d\n",
1003 (int) conn->x.in.n_left, conn->fd);
1004 nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
1006 e = nread ? SOCKET_ERRNO : ECONNRESET;
1007 free(conn->x.in.buf);
1011 conn->x.in.n_left -= nread;
1012 conn->x.in.pos += nread;
1013 if (conn->x.in.n_left <= 0) {
1018 /* Reading length. */
1019 nread = SOCKET_READ(conn->fd,
1020 conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
1021 4 - conn->x.in.bufsizebytes_read);
1026 conn->x.in.bufsizebytes_read += nread;
1027 if (conn->x.in.bufsizebytes_read == 4) {
1028 unsigned long len = load_32_be (conn->x.in.bufsizebytes);
1029 dprint("received length on fd %d is %d\n", conn->fd, (int)len);
1030 /* Arbitrary 1M cap. */
1031 if (len > 1 * 1024 * 1024) {
1035 conn->x.in.bufsize = conn->x.in.n_left = len;
1036 conn->x.in.buf = conn->x.in.pos = malloc(len);
1037 dprint("allocated %d byte buffer at %p\n", (int) len,
1039 if (conn->x.in.buf == 0) {
1040 /* allocation failure */
1055 service_udp_fd(struct conn_state *conn, struct select_state *selstate,
1060 if (!(ssflags & (SSF_READ|SSF_EXCEPTION)))
1062 if (conn->state != READING)
1065 nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
1067 kill_conn(conn, selstate, SOCKET_ERRNO);
1070 conn->x.in.pos = conn->x.in.buf + nread;
1075 service_fds (krb5_context context,
1076 struct select_state *selstate,
1077 struct conn_state *conns, size_t n_conns, int *winning_conn,
1078 struct select_state *seltemp,
1079 int (*msg_handler)(krb5_context, const krb5_data *, void *),
1080 void *msg_handler_data)
1085 while (selstate->nfds > 0) {
1088 e = krb5int_cm_call_select(selstate, seltemp, &selret);
1094 dprint("service_fds examining results, selret=%d\n", selret);
1097 /* Timeout, return to caller. */
1100 /* Got something on a socket, process it. */
1101 for (i = 0; i <= (unsigned int)selstate->max && selret > 0 && i < n_conns; i++) {
1104 if (conns[i].fd == INVALID_SOCKET)
1107 if (FD_ISSET(conns[i].fd, &seltemp->rfds))
1108 ssflags |= SSF_READ, selret--;
1109 if (FD_ISSET(conns[i].fd, &seltemp->wfds))
1110 ssflags |= SSF_WRITE, selret--;
1111 if (FD_ISSET(conns[i].fd, &seltemp->xfds))
1112 ssflags |= SSF_EXCEPTION, selret--;
1116 dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
1117 (ssflags & SSF_READ) ? "r" : "",
1118 (ssflags & SSF_WRITE) ? "w" : "",
1119 (ssflags & SSF_EXCEPTION) ? "x" : "",
1120 conns[i].fd, conns[i].addr,
1121 state_strings[(int) conns[i].state]);
1123 if (conns[i].service (&conns[i], selstate, ssflags)) {
1126 if (msg_handler != NULL) {
1129 reply.data = conns[i].x.in.buf;
1130 reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
1132 stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1136 dprint("fd service routine says we're done\n");
1144 dprint("select returned %m\n", e);
1152 * Current worst-case timeout behavior:
1154 * First pass, 1s per udp or tcp server, plus 2s at end.
1155 * Second pass, 1s per udp server, plus 4s.
1156 * Third pass, 1s per udp server, plus 8s.
1157 * Fourth => 16s, etc.
1160 * Per UDP server, 1s per pass.
1161 * Per TCP server, 1s.
1162 * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
1164 * Total = 2**(P+1) + U*P + T - 2.
1166 * If P=3, Total = 3*U + T + 14.
1167 * If P=4, Total = 4*U + T + 30.
1169 * Note that if you try to reach two ports (e.g., both 88 and 750) on
1170 * one server, it counts as two.
1174 krb5int_sendto (krb5_context context, const krb5_data *message,
1175 const struct addrlist *addrs,
1176 struct sendto_callback_info* callback_info, krb5_data *reply,
1177 struct sockaddr *localaddr, socklen_t *localaddrlen,
1178 struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1180 /* return 0 -> keep going, 1 -> quit */
1181 int (*msg_handler)(krb5_context, const krb5_data *, void *),
1182 void *msg_handler_data)
1185 int delay_this_pass = 2;
1186 krb5_error_code retval;
1187 struct conn_state *conns = NULL;
1188 krb5_data *callback_data = NULL;
1189 size_t i, n_conns = 0, host;
1190 struct select_state *sel_state = NULL;
1192 int winning_conn = -1, e = 0;
1193 char *udpbuf = NULL;
1196 dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
1198 dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
1199 print_addrlist(addrs);
1205 conns = calloc(addrs->naddrs, sizeof(struct conn_state));
1209 if (callback_info) {
1210 callback_data = calloc(addrs->naddrs, sizeof(krb5_data));
1211 if (callback_data == NULL) {
1217 for (i = 0; i < addrs->naddrs; i++)
1218 conns[i].fd = INVALID_SOCKET;
1220 /* One for use here, listing all our fds in use, and one for
1221 temporary use in service_fds, for the fds of interest. */
1222 sel_state = malloc(2 * sizeof(*sel_state));
1223 if (sel_state == NULL) {
1228 sel_state->nfds = 0;
1229 sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
1230 FD_ZERO(&sel_state->rfds);
1231 FD_ZERO(&sel_state->wfds);
1232 FD_ZERO(&sel_state->xfds);
1235 /* Set up connections. */
1236 for (host = 0; host < addrs->naddrs; host++) {
1237 setup_connection(&conns[host], addrs->addrs[host].ai, message,
1240 n_conns = addrs->naddrs;
1241 for (pass = 0; pass < MAX_PASS; pass++) {
1242 /* Possible optimization: Make only one pass if TCP only.
1243 Stop making passes if all UDP ports are closed down. */
1244 dprint("pass %d delay=%d\n", pass, delay_this_pass);
1245 for (host = 0; host < n_conns; host++) {
1246 dprint("host %d\n", host);
1248 /* Send to the host, wait for a response, then move on. */
1249 if (maybe_send(&conns[host],
1252 (callback_info ? &callback_data[host] : NULL)))
1255 retval = getcurtime(&now);
1258 sel_state->end_time = now;
1259 sel_state->end_time.tv_sec += 1;
1260 e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1261 sel_state+1, msg_handler, msg_handler_data);
1264 if (pass > 0 && sel_state->nfds == 0)
1266 * After the first pass, if we close all fds, break
1267 * out right away. During the first pass, it's okay,
1268 * we're probably about to open another connection.
1274 retval = getcurtime(&now);
1277 /* Possible optimization: Find a way to integrate this select
1278 call with the last one from the above loop, if the loop
1279 actually calls select. */
1280 sel_state->end_time.tv_sec += delay_this_pass;
1281 e = service_fds(context, sel_state, conns, host+1, &winning_conn,
1282 sel_state+1, msg_handler, msg_handler_data);
1285 if (sel_state->nfds == 0)
1287 delay_this_pass *= 2;
1290 if (sel_state->nfds == 0) {
1292 retval = KRB5_KDC_UNREACH;
1295 if (e == 0 || winning_conn < 0) {
1296 retval = KRB5_KDC_UNREACH;
1300 reply->data = conns[winning_conn].x.in.buf;
1301 reply->length = (conns[winning_conn].x.in.pos
1302 - conns[winning_conn].x.in.buf);
1303 dprint("returning %d bytes in buffer %p\n",
1304 (int) reply->length, reply->data);
1306 conns[winning_conn].x.in.buf = 0;
1308 *addr_used = winning_conn;
1309 if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
1310 (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
1312 if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
1313 (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
1316 for (i = 0; i < n_conns; i++) {
1317 if (conns[i].fd != INVALID_SOCKET)
1318 closesocket(conns[i].fd);
1319 if (conns[i].state == READING && conns[i].x.in.buf != udpbuf)
1320 free(conns[i].x.in.buf);
1321 if (callback_info) {
1322 callback_info->pfn_cleanup(callback_info->context,
1327 free(callback_data);
1329 if (reply->data != udpbuf)