Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / os / sendto_kdc.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/sendto_kdc.c */
3 /*
4  * Copyright 1990,1991,2001,2002,2004,2005,2007,2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * MS-KKDCP implementation Copyright 2013,2014 Red Hat, Inc.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions are met:
31  *
32  *    1. Redistributions of source code must retain the above copyright
33  *       notice, this list of conditions and the following disclaimer.
34  *
35  *    2. Redistributions in binary form must reproduce the above copyright
36  *       notice, this list of conditions and the following disclaimer in
37  *       the documentation and/or other materials provided with the
38  *       distribution.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
41  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
44  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
46  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
47  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
48  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52
53 /* Send packet to KDC for realm; wait for response, retransmitting
54  * as necessary. */
55
56 #include "k5-int.h"
57 #include "k5-tls.h"
58 #include "fake-addrinfo.h"
59
60 #include "os-proto.h"
61
62 #if defined(HAVE_POLL_H)
63 #include <poll.h>
64 #define USE_POLL
65 #define MAX_POLLFDS 1024
66 #elif defined(HAVE_SYS_SELECT_H)
67 #include <sys/select.h>
68 #endif
69
70 #ifndef _WIN32
71 /* For FIONBIO.  */
72 #include <sys/ioctl.h>
73 #ifdef HAVE_SYS_FILIO_H
74 #include <sys/filio.h>
75 #endif
76 #endif
77
78 #define MAX_PASS                    3
79 #define DEFAULT_UDP_PREF_LIMIT   1465
80 #define HARD_UDP_LIMIT          32700 /* could probably do 64K-epsilon ? */
81 #define PORT_LENGTH                 6 /* decimal repr of UINT16_MAX */
82
83 /* Select state flags.  */
84 #define SSF_READ 0x01
85 #define SSF_WRITE 0x02
86 #define SSF_EXCEPTION 0x04
87
88 typedef int64_t time_ms;
89
90 /* This can be pretty large, so should not be stack-allocated. */
91 struct select_state {
92 #ifdef USE_POLL
93     struct pollfd fds[MAX_POLLFDS];
94 #else
95     int max;
96     fd_set rfds, wfds, xfds;
97 #endif
98     int nfds;
99 };
100
101 /* connection states */
102 enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
103 struct incoming_message {
104     size_t bufsizebytes_read;
105     size_t bufsize;
106     size_t pos;
107     char *buf;
108     unsigned char bufsizebytes[4];
109     size_t n_left;
110 };
111
112 struct outgoing_message {
113     sg_buf sgbuf[2];
114     sg_buf *sgp;
115     int sg_count;
116     unsigned char msg_len_buf[4];
117 };
118
119 struct conn_state;
120 typedef krb5_boolean fd_handler_fn(krb5_context context,
121                                    const krb5_data *realm,
122                                    struct conn_state *conn,
123                                    struct select_state *selstate);
124
125 struct conn_state {
126     SOCKET fd;
127     enum conn_states state;
128     fd_handler_fn *service_connect;
129     fd_handler_fn *service_write;
130     fd_handler_fn *service_read;
131     struct remote_address addr;
132     struct incoming_message in;
133     struct outgoing_message out;
134     krb5_data callback_buffer;
135     size_t server_index;
136     struct conn_state *next;
137     time_ms endtime;
138     krb5_boolean defer;
139     struct {
140         const char *uri_path;
141         const char *servername;
142         char port[PORT_LENGTH];
143         char *https_request;
144         k5_tls_handle tls;
145     } http;
146 };
147
148 /* Set up context->tls.  On allocation failure, return ENOMEM.  On plugin load
149  * failure, set context->tls to point to a nulled vtable and return 0. */
150 static krb5_error_code
151 init_tls_vtable(krb5_context context)
152 {
153     krb5_plugin_initvt_fn initfn;
154
155     if (context->tls != NULL)
156         return 0;
157
158     context->tls = calloc(1, sizeof(*context->tls));
159     if (context->tls == NULL)
160         return ENOMEM;
161
162     /* Attempt to load the module; just let it stay nulled out on failure. */
163     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_TLS, "k5tls", "tls");
164     if (k5_plugin_load(context, PLUGIN_INTERFACE_TLS, "k5tls", &initfn) == 0)
165         (*initfn)(context, 0, 0, (krb5_plugin_vtable)context->tls);
166
167     return 0;
168 }
169
170 /* Get current time in milliseconds. */
171 static krb5_error_code
172 get_curtime_ms(time_ms *time_out)
173 {
174     struct timeval tv;
175
176     *time_out = 0;
177
178     if (gettimeofday(&tv, 0))
179         return errno;
180     *time_out = (time_ms)tv.tv_sec * 1000 + tv.tv_usec / 1000;
181     return 0;
182 }
183
184 static void
185 free_http_tls_data(krb5_context context, struct conn_state *state)
186 {
187     if (state->http.tls != NULL)
188         context->tls->free_handle(context, state->http.tls);
189     state->http.tls = NULL;
190     free(state->http.https_request);
191     state->http.https_request = NULL;
192 }
193
194 #ifdef USE_POLL
195
196 /* Find a pollfd in selstate by fd, or abort if we can't find it. */
197 static inline struct pollfd *
198 find_pollfd(struct select_state *selstate, int fd)
199 {
200     int i;
201
202     for (i = 0; i < selstate->nfds; i++) {
203         if (selstate->fds[i].fd == fd)
204             return &selstate->fds[i];
205     }
206     abort();
207 }
208
209 static void
210 cm_init_selstate(struct select_state *selstate)
211 {
212     selstate->nfds = 0;
213 }
214
215 static krb5_boolean
216 cm_add_fd(struct select_state *selstate, int fd)
217 {
218     if (selstate->nfds >= MAX_POLLFDS)
219         return FALSE;
220     selstate->fds[selstate->nfds].fd = fd;
221     selstate->fds[selstate->nfds].events = 0;
222     selstate->nfds++;
223     return TRUE;
224 }
225
226 static void
227 cm_remove_fd(struct select_state *selstate, int fd)
228 {
229     struct pollfd *pfd = find_pollfd(selstate, fd);
230
231     *pfd = selstate->fds[selstate->nfds - 1];
232     selstate->nfds--;
233 }
234
235 /* Poll for reading (and not writing) on fd the next time we poll. */
236 static void
237 cm_read(struct select_state *selstate, int fd)
238 {
239     find_pollfd(selstate, fd)->events = POLLIN;
240 }
241
242 /* Poll for writing (and not reading) on fd the next time we poll. */
243 static void
244 cm_write(struct select_state *selstate, int fd)
245 {
246     find_pollfd(selstate, fd)->events = POLLOUT;
247 }
248
249 /* Get the output events for fd in the form of ssflags. */
250 static unsigned int
251 cm_get_ssflags(struct select_state *selstate, int fd)
252 {
253     struct pollfd *pfd = find_pollfd(selstate, fd);
254
255     /*
256      * OS X sets POLLHUP without POLLOUT on connection error.  Catch this as
257      * well as other error events such as POLLNVAL, but only if POLLIN and
258      * POLLOUT aren't set, as we can get POLLHUP along with POLLIN with TCP
259      * data still to be read.
260      */
261     if (pfd->revents != 0 && !(pfd->revents & (POLLIN | POLLOUT)))
262         return SSF_EXCEPTION;
263
264     return ((pfd->revents & POLLIN) ? SSF_READ : 0) |
265         ((pfd->revents & POLLOUT) ? SSF_WRITE : 0) |
266         ((pfd->revents & POLLERR) ? SSF_EXCEPTION : 0);
267 }
268
269 #else /* not USE_POLL */
270
271 static void
272 cm_init_selstate(struct select_state *selstate)
273 {
274     selstate->nfds = 0;
275     selstate->max = 0;
276     FD_ZERO(&selstate->rfds);
277     FD_ZERO(&selstate->wfds);
278     FD_ZERO(&selstate->xfds);
279 }
280
281 static krb5_boolean
282 cm_add_fd(struct select_state *selstate, int fd)
283 {
284 #ifndef _WIN32  /* On Windows FD_SETSIZE is a count, not a max value. */
285     if (fd >= FD_SETSIZE)
286         return FALSE;
287 #endif
288     FD_SET(fd, &selstate->xfds);
289     if (selstate->max <= fd)
290         selstate->max = fd + 1;
291     selstate->nfds++;
292     return TRUE;
293 }
294
295 static void
296 cm_remove_fd(struct select_state *selstate, int fd)
297 {
298     FD_CLR(fd, &selstate->rfds);
299     FD_CLR(fd, &selstate->wfds);
300     FD_CLR(fd, &selstate->xfds);
301     if (selstate->max == fd + 1) {
302         while (selstate->max > 0 &&
303                !FD_ISSET(selstate->max - 1, &selstate->rfds) &&
304                !FD_ISSET(selstate->max - 1, &selstate->wfds) &&
305                !FD_ISSET(selstate->max - 1, &selstate->xfds))
306             selstate->max--;
307     }
308     selstate->nfds--;
309 }
310
311 /* Select for reading (and not writing) on fd the next time we select. */
312 static void
313 cm_read(struct select_state *selstate, int fd)
314 {
315     FD_SET(fd, &selstate->rfds);
316     FD_CLR(fd, &selstate->wfds);
317 }
318
319 /* Select for writing (and not reading) on fd the next time we select. */
320 static void
321 cm_write(struct select_state *selstate, int fd)
322 {
323     FD_CLR(fd, &selstate->rfds);
324     FD_SET(fd, &selstate->wfds);
325 }
326
327 /* Get the events for fd from selstate after a select. */
328 static unsigned int
329 cm_get_ssflags(struct select_state *selstate, int fd)
330 {
331     return (FD_ISSET(fd, &selstate->rfds) ? SSF_READ : 0) |
332         (FD_ISSET(fd, &selstate->wfds) ? SSF_WRITE : 0) |
333         (FD_ISSET(fd, &selstate->xfds) ? SSF_EXCEPTION : 0);
334 }
335
336 #endif /* not USE_POLL */
337
338 static krb5_error_code
339 cm_select_or_poll(const struct select_state *in, time_ms endtime,
340                   struct select_state *out, int *sret)
341 {
342 #ifndef USE_POLL
343     struct timeval tv;
344 #endif
345     krb5_error_code retval;
346     time_ms curtime, interval;
347
348     retval = get_curtime_ms(&curtime);
349     if (retval != 0)
350         return retval;
351     interval = (curtime < endtime) ? endtime - curtime : 0;
352
353     /* We don't need a separate copy of the selstate for poll, but use one for
354      * consistency with how we use select. */
355     *out = *in;
356
357 #ifdef USE_POLL
358     *sret = poll(out->fds, out->nfds, interval);
359 #else
360     tv.tv_sec = interval / 1000;
361     tv.tv_usec = interval % 1000 * 1000;
362     *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
363 #endif
364
365     return (*sret < 0) ? SOCKET_ERRNO : 0;
366 }
367
368 static int
369 socktype_for_transport(k5_transport transport)
370 {
371     switch (transport) {
372     case UDP:
373         return SOCK_DGRAM;
374     case TCP:
375     case HTTPS:
376         return SOCK_STREAM;
377     default:
378         return 0;
379     }
380 }
381
382 static int
383 check_for_svc_unavailable (krb5_context context,
384                            const krb5_data *reply,
385                            void *msg_handler_data)
386 {
387     krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
388
389     *retval = 0;
390
391     if (krb5_is_krb_error(reply)) {
392         krb5_error *err_reply;
393
394         if (decode_krb5_error(reply, &err_reply) == 0) {
395             *retval = err_reply->error;
396             krb5_free_error(context, err_reply);
397
398             /* Returning 0 means continue to next KDC */
399             return (*retval != KDC_ERR_SVC_UNAVAILABLE);
400         }
401     }
402
403     return 1;
404 }
405
406 void KRB5_CALLCONV
407 krb5_set_kdc_send_hook(krb5_context context, krb5_pre_send_fn send_hook,
408                        void *data)
409 {
410     context->kdc_send_hook = send_hook;
411     context->kdc_send_hook_data = data;
412 }
413
414 void KRB5_CALLCONV
415 krb5_set_kdc_recv_hook(krb5_context context, krb5_post_recv_fn recv_hook,
416                        void *data)
417 {
418     context->kdc_recv_hook = recv_hook;
419     context->kdc_recv_hook_data = data;
420 }
421
422 /*
423  * send the formatted request 'message' to a KDC for realm 'realm' and
424  * return the response (if any) in 'reply'.
425  *
426  * If the message is sent and a response is received, 0 is returned,
427  * otherwise an error code is returned.
428  *
429  * The storage for 'reply' is allocated and should be freed by the caller
430  * when finished.
431  */
432
433 krb5_error_code
434 krb5_sendto_kdc(krb5_context context, const krb5_data *message,
435                 const krb5_data *realm, krb5_data *reply_out, int *use_master,
436                 int no_udp)
437 {
438     krb5_error_code retval, oldret, err;
439     struct serverlist servers;
440     int server_used;
441     k5_transport_strategy strategy;
442     krb5_data reply = empty_data(), *hook_message = NULL, *hook_reply = NULL;
443
444     *reply_out = empty_data();
445
446     /*
447      * find KDC location(s) for realm
448      */
449
450     /*
451      * BUG: This code won't return "interesting" errors (e.g., out of mem,
452      * bad config file) from locate_kdc.  KRB5_REALM_CANT_RESOLVE can be
453      * ignored from one query of two, but if only one query is done, or
454      * both return that error, it should be returned to the caller.  Also,
455      * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp}
456      * should probably be returned as well.
457      */
458
459     TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp);
460
461     if (!no_udp && context->udp_pref_limit < 0) {
462         int tmp;
463         retval = profile_get_integer(context->profile,
464                                      KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
465                                      DEFAULT_UDP_PREF_LIMIT, &tmp);
466         if (retval)
467             return retval;
468         if (tmp < 0)
469             tmp = DEFAULT_UDP_PREF_LIMIT;
470         else if (tmp > HARD_UDP_LIMIT)
471             /* In the unlikely case that a *really* big value is
472                given, let 'em use as big as we think we can
473                support.  */
474             tmp = HARD_UDP_LIMIT;
475         context->udp_pref_limit = tmp;
476     }
477
478     if (no_udp)
479         strategy = NO_UDP;
480     else if (message->length <= (unsigned int) context->udp_pref_limit)
481         strategy = UDP_FIRST;
482     else
483         strategy = UDP_LAST;
484
485     retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp);
486     if (retval)
487         return retval;
488
489     if (context->kdc_send_hook != NULL) {
490         retval = context->kdc_send_hook(context, context->kdc_send_hook_data,
491                                         realm, message, &hook_message,
492                                         &hook_reply);
493         if (retval)
494             goto cleanup;
495
496         if (hook_reply != NULL) {
497             *reply_out = *hook_reply;
498             free(hook_reply);
499             goto cleanup;
500         }
501
502         if (hook_message != NULL)
503             message = hook_message;
504     }
505
506     err = 0;
507     retval = k5_sendto(context, message, realm, &servers, strategy, NULL,
508                        &reply, NULL, NULL, &server_used,
509                        check_for_svc_unavailable, &err);
510     if (retval == KRB5_KDC_UNREACH) {
511         if (err == KDC_ERR_SVC_UNAVAILABLE) {
512             retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
513         } else {
514             k5_setmsg(context, retval,
515                       _("Cannot contact any KDC for realm '%.*s'"),
516                       realm->length, realm->data);
517         }
518     }
519
520     if (context->kdc_recv_hook != NULL) {
521         oldret = retval;
522         retval = context->kdc_recv_hook(context, context->kdc_recv_hook_data,
523                                         retval, realm, message, &reply,
524                                         &hook_reply);
525         if (oldret && !retval) {
526             /* The hook must set a reply if it overrides an error from
527              * k5_sendto().  Treat this reply as coming from the master KDC. */
528             assert(hook_reply != NULL);
529             *use_master = 1;
530         }
531     }
532     if (retval)
533         goto cleanup;
534
535     if (hook_reply != NULL) {
536         *reply_out = *hook_reply;
537         free(hook_reply);
538     } else {
539         *reply_out = reply;
540         reply = empty_data();
541     }
542
543     /* Set use_master to 1 if we ended up talking to a master when we didn't
544      * explicitly request to. */
545     if (*use_master == 0) {
546         *use_master = k5_kdc_is_master(context, realm,
547                                        &servers.servers[server_used]);
548         TRACE_SENDTO_KDC_MASTER(context, *use_master);
549     }
550
551 cleanup:
552     krb5_free_data(context, hook_message);
553     krb5_free_data_contents(context, &reply);
554     k5_free_serverlist(&servers);
555     return retval;
556 }
557
558 /*
559  * Notes:
560  *
561  * Getting "connection refused" on a connected UDP socket causes
562  * select to indicate write capability on UNIX, but only shows up
563  * as an exception on Windows.  (I don't think any UNIX system flags
564  * the error as an exception.)  So we check for both, or make it
565  * system-specific.
566  *
567  * Always watch for responses from *any* of the servers.  Eventually
568  * fix the UDP code to do the same.
569  *
570  * To do:
571  * - TCP NOPUSH/CORK socket options?
572  * - error codes that don't suck
573  * - getsockopt(SO_ERROR) to check connect status
574  * - handle error RESPONSE_TOO_BIG from UDP server and use TCP
575  *   connections already in progress
576  */
577
578 static fd_handler_fn service_tcp_connect;
579 static fd_handler_fn service_tcp_write;
580 static fd_handler_fn service_tcp_read;
581 static fd_handler_fn service_udp_read;
582 static fd_handler_fn service_https_write;
583 static fd_handler_fn service_https_read;
584
585 static krb5_error_code
586 make_proxy_request(struct conn_state *state, const krb5_data *realm,
587                    const krb5_data *message, char **req_out, size_t *len_out)
588 {
589     krb5_kkdcp_message pm;
590     krb5_data *encoded_pm = NULL;
591     struct k5buf buf;
592     const char *uri_path;
593     krb5_error_code ret;
594
595     *req_out = NULL;
596     *len_out = 0;
597
598     /*
599      * Stuff the message length in at the front of the kerb_message field
600      * before encoding.  The proxied messages are actually the payload we'd
601      * be sending and receiving if we were using plain TCP.
602      */
603     memset(&pm, 0, sizeof(pm));
604     ret = alloc_data(&pm.kerb_message, message->length + 4);
605     if (ret != 0)
606         goto cleanup;
607     store_32_be(message->length, pm.kerb_message.data);
608     memcpy(pm.kerb_message.data + 4, message->data, message->length);
609     pm.target_domain = *realm;
610     ret = encode_krb5_kkdcp_message(&pm, &encoded_pm);
611     if (ret != 0)
612         goto cleanup;
613
614     /* Build the request to transmit: the headers + the proxy message. */
615     k5_buf_init_dynamic(&buf);
616     uri_path = (state->http.uri_path != NULL) ? state->http.uri_path : "";
617     k5_buf_add_fmt(&buf, "POST /%s HTTP/1.0\r\n", uri_path);
618     k5_buf_add_fmt(&buf, "Host: %s:%s\r\n", state->http.servername,
619                    state->http.port);
620     k5_buf_add(&buf, "Cache-Control: no-cache\r\n");
621     k5_buf_add(&buf, "Pragma: no-cache\r\n");
622     k5_buf_add(&buf, "User-Agent: kerberos/1.0\r\n");
623     k5_buf_add(&buf, "Content-type: application/kerberos\r\n");
624     k5_buf_add_fmt(&buf, "Content-Length: %d\r\n\r\n", encoded_pm->length);
625     k5_buf_add_len(&buf, encoded_pm->data, encoded_pm->length);
626     if (k5_buf_status(&buf) != 0) {
627         ret = ENOMEM;
628         goto cleanup;
629     }
630
631     *req_out = buf.data;
632     *len_out = buf.len;
633
634 cleanup:
635     krb5_free_data_contents(NULL, &pm.kerb_message);
636     krb5_free_data(NULL, encoded_pm);
637     return ret;
638 }
639
640 /* Set up the actual message we will send across the underlying transport to
641  * communicate the payload message, using one or both of state->out.sgbuf. */
642 static krb5_error_code
643 set_transport_message(struct conn_state *state, const krb5_data *realm,
644                       const krb5_data *message)
645 {
646     struct outgoing_message *out = &state->out;
647     char *req = NULL;
648     size_t reqlen;
649     krb5_error_code ret;
650
651     if (message == NULL || message->length == 0)
652         return 0;
653
654     if (state->addr.transport == TCP) {
655         store_32_be(message->length, out->msg_len_buf);
656         SG_SET(&out->sgbuf[0], out->msg_len_buf, 4);
657         SG_SET(&out->sgbuf[1], message->data, message->length);
658         out->sg_count = 2;
659         return 0;
660     } else if (state->addr.transport == HTTPS) {
661         ret = make_proxy_request(state, realm, message, &req, &reqlen);
662         if (ret != 0)
663             return ret;
664         SG_SET(&state->out.sgbuf[0], req, reqlen);
665         SG_SET(&state->out.sgbuf[1], 0, 0);
666         state->out.sg_count = 1;
667         free(state->http.https_request);
668         state->http.https_request = req;
669         return 0;
670     } else {
671         SG_SET(&out->sgbuf[0], message->data, message->length);
672         SG_SET(&out->sgbuf[1], NULL, 0);
673         out->sg_count = 1;
674         return 0;
675     }
676 }
677
678 static krb5_error_code
679 add_connection(struct conn_state **conns, k5_transport transport,
680                krb5_boolean defer, struct addrinfo *ai, size_t server_index,
681                const krb5_data *realm, const char *hostname,
682                const char *port, const char *uri_path, char **udpbufp)
683 {
684     struct conn_state *state, **tailptr;
685
686     state = calloc(1, sizeof(*state));
687     if (state == NULL)
688         return ENOMEM;
689     state->state = INITIALIZING;
690     state->out.sgp = state->out.sgbuf;
691     state->addr.transport = transport;
692     state->addr.family = ai->ai_family;
693     state->addr.len = ai->ai_addrlen;
694     memcpy(&state->addr.saddr, ai->ai_addr, ai->ai_addrlen);
695     state->defer = defer;
696     state->fd = INVALID_SOCKET;
697     state->server_index = server_index;
698     SG_SET(&state->out.sgbuf[1], NULL, 0);
699     if (transport == TCP) {
700         state->service_connect = service_tcp_connect;
701         state->service_write = service_tcp_write;
702         state->service_read = service_tcp_read;
703     } else if (transport == HTTPS) {
704         assert(hostname != NULL && port != NULL);
705         state->service_connect = service_tcp_connect;
706         state->service_write = service_https_write;
707         state->service_read = service_https_read;
708         state->http.uri_path = uri_path;
709         state->http.servername = hostname;
710         strlcpy(state->http.port, port, PORT_LENGTH);
711     } else {
712         state->service_connect = NULL;
713         state->service_write = NULL;
714         state->service_read = service_udp_read;
715
716         if (*udpbufp == NULL) {
717             *udpbufp = malloc(MAX_DGRAM_SIZE);
718             if (*udpbufp == 0)
719                 return ENOMEM;
720         }
721         state->in.buf = *udpbufp;
722         state->in.bufsize = MAX_DGRAM_SIZE;
723     }
724
725     /* Chain the new state onto the tail of the list. */
726     for (tailptr = conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
727     *tailptr = state;
728
729     return 0;
730 }
731
732 static int
733 translate_ai_error (int err)
734 {
735     switch (err) {
736     case 0:
737         return 0;
738     case EAI_BADFLAGS:
739     case EAI_FAMILY:
740     case EAI_SOCKTYPE:
741     case EAI_SERVICE:
742         /* All of these indicate bad inputs to getaddrinfo.  */
743         return EINVAL;
744     case EAI_AGAIN:
745         /* Translate to standard errno code.  */
746         return EAGAIN;
747     case EAI_MEMORY:
748         /* Translate to standard errno code.  */
749         return ENOMEM;
750 #ifdef EAI_ADDRFAMILY
751     case EAI_ADDRFAMILY:
752 #endif
753 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
754     case EAI_NODATA:
755 #endif
756     case EAI_NONAME:
757         /* Name not known or no address data, but no error.  Do
758            nothing more.  */
759         return 0;
760 #ifdef EAI_OVERFLOW
761     case EAI_OVERFLOW:
762         /* An argument buffer overflowed.  */
763         return EINVAL;          /* XXX */
764 #endif
765 #ifdef EAI_SYSTEM
766     case EAI_SYSTEM:
767         /* System error, obviously.  */
768         return errno;
769 #endif
770     default:
771         /* An error code we haven't handled?  */
772         return EINVAL;
773     }
774 }
775
776 /*
777  * Resolve the entry in servers with index ind, adding connections to the list
778  * *conns.  Connections are added for each of socktype1 and (if not zero)
779  * socktype2.  message and udpbufp are used to initialize the connections; see
780  * add_connection above.  If no addresses are available for an entry but no
781  * internal name resolution failure occurs, return 0 without adding any new
782  * connections.
783  */
784 static krb5_error_code
785 resolve_server(krb5_context context, const krb5_data *realm,
786                const struct serverlist *servers, size_t ind,
787                k5_transport_strategy strategy, const krb5_data *message,
788                char **udpbufp, struct conn_state **conns)
789 {
790     krb5_error_code retval;
791     struct server_entry *entry = &servers->servers[ind];
792     k5_transport transport;
793     struct addrinfo *addrs, *a, hint, ai;
794     krb5_boolean defer = FALSE;
795     int err, result;
796     char portbuf[PORT_LENGTH];
797
798     /* Skip UDP entries if we don't want UDP. */
799     if (strategy == NO_UDP && entry->transport == UDP)
800         return 0;
801
802     transport = (strategy == UDP_FIRST) ? UDP : TCP;
803     if (entry->hostname == NULL) {
804         /* Added by a module, so transport is either TCP or UDP. */
805         ai.ai_socktype = socktype_for_transport(entry->transport);
806         ai.ai_family = entry->family;
807         ai.ai_addrlen = entry->addrlen;
808         ai.ai_addr = (struct sockaddr *)&entry->addr;
809         defer = (entry->transport != transport);
810         return add_connection(conns, entry->transport, defer, &ai, ind, realm,
811                               NULL, NULL, entry->uri_path, udpbufp);
812     }
813
814     /* If the entry has a specified transport, use it, but possibly defer the
815      * addresses we add based on the strategy. */
816     if (entry->transport != TCP_OR_UDP) {
817         transport = entry->transport;
818         defer = (entry->transport == TCP && strategy == UDP_FIRST) ||
819             (entry->transport == UDP && strategy == UDP_LAST);
820     }
821
822     memset(&hint, 0, sizeof(hint));
823     hint.ai_family = entry->family;
824     hint.ai_socktype = socktype_for_transport(transport);
825     hint.ai_flags = AI_ADDRCONFIG;
826 #ifdef AI_NUMERICSERV
827     hint.ai_flags |= AI_NUMERICSERV;
828 #endif
829     result = snprintf(portbuf, sizeof(portbuf), "%d", entry->port);
830     if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
831         return EINVAL;
832     TRACE_SENDTO_KDC_RESOLVING(context, entry->hostname);
833     err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
834     if (err)
835         return translate_ai_error(err);
836
837     /* Add each address with the specified or preferred transport. */
838     retval = 0;
839     for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
840         retval = add_connection(conns, transport, defer, a, ind, realm,
841                                 entry->hostname, portbuf, entry->uri_path,
842                                 udpbufp);
843     }
844
845     /* For TCP_OR_UDP entries, add each address again with the non-preferred
846      * transport, unless we are avoiding UDP.  Flag these as deferred. */
847     if (retval == 0 && entry->transport == TCP_OR_UDP && strategy != NO_UDP) {
848         transport = (strategy == UDP_FIRST) ? TCP : UDP;
849         for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
850             a->ai_socktype = socktype_for_transport(transport);
851             retval = add_connection(conns, transport, TRUE, a, ind, realm,
852                                     entry->hostname, portbuf,
853                                     entry->uri_path, udpbufp);
854         }
855     }
856     freeaddrinfo(addrs);
857     return retval;
858 }
859
860 static int
861 start_connection(krb5_context context, struct conn_state *state,
862                  const krb5_data *message, struct select_state *selstate,
863                  const krb5_data *realm,
864                  struct sendto_callback_info *callback_info)
865 {
866     int fd, e, type;
867     static const int one = 1;
868     static const struct linger lopt = { 0, 0 };
869
870     type = socktype_for_transport(state->addr.transport);
871     fd = socket(state->addr.family, type, 0);
872     if (fd == INVALID_SOCKET)
873         return -1;              /* try other hosts */
874     set_cloexec_fd(fd);
875     /* Make it non-blocking.  */
876     ioctlsocket(fd, FIONBIO, (const void *) &one);
877     if (state->addr.transport == TCP) {
878         setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
879         TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr);
880     }
881
882     /* Start connecting to KDC.  */
883     e = connect(fd, (struct sockaddr *)&state->addr.saddr, state->addr.len);
884     if (e != 0) {
885         /*
886          * This is the path that should be followed for non-blocking
887          * connections.
888          */
889         if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
890             state->state = CONNECTING;
891             state->fd = fd;
892         } else {
893             (void) closesocket(fd);
894             state->state = FAILED;
895             return -2;
896         }
897     } else {
898         /*
899          * Connect returned zero even though we made it non-blocking.  This
900          * happens normally for UDP sockets, and can perhaps also happen for
901          * TCP sockets connecting to localhost.
902          */
903         state->state = WRITING;
904         state->fd = fd;
905     }
906
907     /*
908      * Here's where KPASSWD callback gets the socket information it needs for
909      * a kpasswd request
910      */
911     if (callback_info) {
912
913         e = callback_info->pfn_callback(state->fd, callback_info->data,
914                                         &state->callback_buffer);
915         if (e != 0) {
916             (void) closesocket(fd);
917             state->fd = INVALID_SOCKET;
918             state->state = FAILED;
919             return -3;
920         }
921
922         message = &state->callback_buffer;
923     }
924
925     e = set_transport_message(state, realm, message);
926     if (e != 0) {
927         TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(context, &state->addr, e);
928         (void) closesocket(state->fd);
929         state->fd = INVALID_SOCKET;
930         state->state = FAILED;
931         return -4;
932     }
933
934     if (state->addr.transport == UDP) {
935         /* Send it now.  */
936         ssize_t ret;
937         sg_buf *sg = &state->out.sgbuf[0];
938
939         TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, &state->addr);
940         ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
941         if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
942             TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, &state->addr,
943                                                     SOCKET_ERRNO);
944             (void) closesocket(state->fd);
945             state->fd = INVALID_SOCKET;
946             state->state = FAILED;
947             return -5;
948         } else {
949             state->state = READING;
950         }
951     }
952
953     if (!cm_add_fd(selstate, state->fd)) {
954         (void) closesocket(state->fd);
955         state->fd = INVALID_SOCKET;
956         state->state = FAILED;
957         return -1;
958     }
959     if (state->state == CONNECTING || state->state == WRITING)
960         cm_write(selstate, state->fd);
961     else
962         cm_read(selstate, state->fd);
963
964     return 0;
965 }
966
967 /* Return 0 if we sent something, non-0 otherwise.
968    If 0 is returned, the caller should delay waiting for a response.
969    Otherwise, the caller should immediately move on to process the
970    next connection.  */
971 static int
972 maybe_send(krb5_context context, struct conn_state *conn,
973            const krb5_data *message, struct select_state *selstate,
974            const krb5_data *realm,
975            struct sendto_callback_info *callback_info)
976 {
977     sg_buf *sg;
978     ssize_t ret;
979
980     if (conn->state == INITIALIZING) {
981         return start_connection(context, conn, message, selstate,
982                                 realm, callback_info);
983     }
984
985     /* Did we already shut down this channel?  */
986     if (conn->state == FAILED) {
987         return -1;
988     }
989
990     if (conn->addr.transport != UDP) {
991         /* The select callback will handle flushing any data we
992            haven't written yet, and we only write it once.  */
993         return -1;
994     }
995
996     /* UDP - retransmit after a previous attempt timed out. */
997     sg = &conn->out.sgbuf[0];
998     TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, &conn->addr);
999     ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
1000     if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
1001         TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(context, &conn->addr,
1002                                               SOCKET_ERRNO);
1003         /* Keep connection alive, we'll try again next pass.
1004
1005            Is this likely to catch any errors we didn't get from the
1006            select callbacks?  */
1007         return -1;
1008     }
1009     /* Yay, it worked.  */
1010     return 0;
1011 }
1012
1013 static void
1014 kill_conn(krb5_context context, struct conn_state *conn,
1015           struct select_state *selstate)
1016 {
1017     free_http_tls_data(context, conn);
1018
1019     if (socktype_for_transport(conn->addr.transport) == SOCK_STREAM)
1020         TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &conn->addr);
1021     cm_remove_fd(selstate, conn->fd);
1022
1023     closesocket(conn->fd);
1024     conn->fd = INVALID_SOCKET;
1025     conn->state = FAILED;
1026 }
1027
1028 /* Check socket for error.  */
1029 static int
1030 get_so_error(int fd)
1031 {
1032     int e, sockerr;
1033     socklen_t sockerrlen;
1034
1035     sockerr = 0;
1036     sockerrlen = sizeof(sockerr);
1037     e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
1038     if (e != 0) {
1039         /* What to do now?  */
1040         e = SOCKET_ERRNO;
1041         return e;
1042     }
1043     return sockerr;
1044 }
1045
1046 /* Perform next step in sending.  Return true on usable data. */
1047 static krb5_boolean
1048 service_dispatch(krb5_context context, const krb5_data *realm,
1049                  struct conn_state *conn, struct select_state *selstate,
1050                  int ssflags)
1051 {
1052     /* Check for a socket exception. */
1053     if (ssflags & SSF_EXCEPTION) {
1054         kill_conn(context, conn, selstate);
1055         return FALSE;
1056     }
1057
1058     switch (conn->state) {
1059     case CONNECTING:
1060         assert(conn->service_connect != NULL);
1061         return conn->service_connect(context, realm, conn, selstate);
1062     case WRITING:
1063         assert(conn->service_write != NULL);
1064         return conn->service_write(context, realm, conn, selstate);
1065     case READING:
1066         assert(conn->service_read != NULL);
1067         return conn->service_read(context, realm, conn, selstate);
1068     default:
1069         abort();
1070     }
1071 }
1072
1073 /* Initialize TCP transport. */
1074 static krb5_boolean
1075 service_tcp_connect(krb5_context context, const krb5_data *realm,
1076                     struct conn_state *conn, struct select_state *selstate)
1077 {
1078     /* Check whether the connection succeeded. */
1079     int e = get_so_error(conn->fd);
1080
1081     if (e) {
1082         TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e);
1083         kill_conn(context, conn, selstate);
1084         return FALSE;
1085     }
1086
1087     conn->state = WRITING;
1088
1089     /* Record this connection's timeout for service_fds. */
1090     if (get_curtime_ms(&conn->endtime) == 0)
1091         conn->endtime += 10000;
1092
1093     return conn->service_write(context, realm, conn, selstate);
1094 }
1095
1096 /* Sets conn->state to READING when done. */
1097 static krb5_boolean
1098 service_tcp_write(krb5_context context, const krb5_data *realm,
1099                   struct conn_state *conn, struct select_state *selstate)
1100 {
1101     ssize_t nwritten;
1102     SOCKET_WRITEV_TEMP tmp;
1103
1104     TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
1105     nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp);
1106     if (nwritten < 0) {
1107         TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO);
1108         kill_conn(context, conn, selstate);
1109         return FALSE;
1110     }
1111     while (nwritten) {
1112         sg_buf *sgp = conn->out.sgp;
1113         if ((size_t)nwritten < SG_LEN(sgp)) {
1114             SG_ADVANCE(sgp, (size_t)nwritten);
1115             nwritten = 0;
1116         } else {
1117             nwritten -= SG_LEN(sgp);
1118             conn->out.sgp++;
1119             conn->out.sg_count--;
1120         }
1121     }
1122     if (conn->out.sg_count == 0) {
1123         /* Done writing, switch to reading. */
1124         cm_read(selstate, conn->fd);
1125         conn->state = READING;
1126     }
1127     return FALSE;
1128 }
1129
1130 /* Return true on usable data. */
1131 static krb5_boolean
1132 service_tcp_read(krb5_context context, const krb5_data *realm,
1133                  struct conn_state *conn, struct select_state *selstate)
1134 {
1135     ssize_t nread;
1136     int e = 0;
1137     struct incoming_message *in = &conn->in;
1138
1139     if (in->bufsizebytes_read == 4) {
1140         /* Reading data.  */
1141         nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
1142         if (nread <= 0) {
1143             e = nread ? SOCKET_ERRNO : ECONNRESET;
1144             TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
1145             kill_conn(context, conn, selstate);
1146             return FALSE;
1147         }
1148         in->n_left -= nread;
1149         in->pos += nread;
1150         if (in->n_left <= 0)
1151             return TRUE;
1152     } else {
1153         /* Reading length.  */
1154         nread = SOCKET_READ(conn->fd, in->bufsizebytes + in->bufsizebytes_read,
1155                             4 - in->bufsizebytes_read);
1156         if (nread <= 0) {
1157             e = nread ? SOCKET_ERRNO : ECONNRESET;
1158             TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
1159             kill_conn(context, conn, selstate);
1160             return FALSE;
1161         }
1162         in->bufsizebytes_read += nread;
1163         if (in->bufsizebytes_read == 4) {
1164             unsigned long len = load_32_be(in->bufsizebytes);
1165             /* Arbitrary 1M cap.  */
1166             if (len > 1 * 1024 * 1024) {
1167                 kill_conn(context, conn, selstate);
1168                 return FALSE;
1169             }
1170             in->bufsize = in->n_left = len;
1171             in->pos = 0;
1172             in->buf = malloc(len);
1173             if (in->buf == NULL) {
1174                 kill_conn(context, conn, selstate);
1175                 return FALSE;
1176             }
1177         }
1178     }
1179     return FALSE;
1180 }
1181
1182 /* Process events on a UDP socket.  Return true if we get a reply. */
1183 static krb5_boolean
1184 service_udp_read(krb5_context context, const krb5_data *realm,
1185                  struct conn_state *conn, struct select_state *selstate)
1186 {
1187     int nread;
1188
1189     nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0);
1190     if (nread < 0) {
1191         TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO);
1192         kill_conn(context, conn, selstate);
1193         return FALSE;
1194     }
1195     conn->in.pos = nread;
1196     return TRUE;
1197 }
1198
1199 /* Set up conn->http.tls.  Return true on success. */
1200 static krb5_boolean
1201 setup_tls(krb5_context context, const krb5_data *realm,
1202           struct conn_state *conn, struct select_state *selstate)
1203 {
1204     krb5_error_code ret;
1205     krb5_boolean ok = FALSE;
1206     char **anchors = NULL, *realmstr = NULL;
1207     const char *names[4];
1208
1209     if (init_tls_vtable(context) != 0 || context->tls->setup == NULL)
1210         return FALSE;
1211
1212     realmstr = k5memdup0(realm->data, realm->length, &ret);
1213     if (realmstr == NULL)
1214         goto cleanup;
1215
1216     /* Load the configured anchors. */
1217     names[0] = KRB5_CONF_REALMS;
1218     names[1] = realmstr;
1219     names[2] = KRB5_CONF_HTTP_ANCHORS;
1220     names[3] = NULL;
1221     ret = profile_get_values(context->profile, names, &anchors);
1222     if (ret != 0 && ret != PROF_NO_RELATION)
1223         goto cleanup;
1224
1225     if (context->tls->setup(context, conn->fd, conn->http.servername, anchors,
1226                             &conn->http.tls) != 0) {
1227         TRACE_SENDTO_KDC_HTTPS_ERROR_CONNECT(context, &conn->addr);
1228         goto cleanup;
1229     }
1230
1231     ok = TRUE;
1232
1233 cleanup:
1234     free(realmstr);
1235     profile_free_list(anchors);
1236     return ok;
1237 }
1238
1239 /* Set conn->state to READING when done; otherwise, call a cm_set_. */
1240 static krb5_boolean
1241 service_https_write(krb5_context context, const krb5_data *realm,
1242                     struct conn_state *conn, struct select_state *selstate)
1243 {
1244     k5_tls_status st;
1245
1246     /* If this is our first time in here, set up the SSL context. */
1247     if (conn->http.tls == NULL && !setup_tls(context, realm, conn, selstate)) {
1248         kill_conn(context, conn, selstate);
1249         return FALSE;
1250     }
1251
1252     /* Try to transmit our request to the server. */
1253     st = context->tls->write(context, conn->http.tls, SG_BUF(conn->out.sgp),
1254                              SG_LEN(conn->out.sgbuf));
1255     if (st == DONE) {
1256         TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr);
1257         cm_read(selstate, conn->fd);
1258         conn->state = READING;
1259     } else if (st == WANT_READ) {
1260         cm_read(selstate, conn->fd);
1261     } else if (st == WANT_WRITE) {
1262         cm_write(selstate, conn->fd);
1263     } else if (st == ERROR_TLS) {
1264         TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(context, &conn->addr);
1265         kill_conn(context, conn, selstate);
1266     }
1267
1268     return FALSE;
1269 }
1270
1271 /* Return true on finished data.  Call a cm_read/write function and return
1272  * false if the TLS layer needs it.  Kill the connection on error. */
1273 static krb5_boolean
1274 https_read_bytes(krb5_context context, struct conn_state *conn,
1275                  struct select_state *selstate)
1276 {
1277     size_t bufsize, nread;
1278     k5_tls_status st;
1279     char *tmp;
1280     struct incoming_message *in = &conn->in;
1281
1282     for (;;) {
1283         if (in->buf == NULL || in->bufsize - in->pos < 1024) {
1284             bufsize = in->bufsize ? in->bufsize * 2 : 8192;
1285             if (bufsize > 1024 * 1024) {
1286                 kill_conn(context, conn, selstate);
1287                 return FALSE;
1288             }
1289             tmp = realloc(in->buf, bufsize);
1290             if (tmp == NULL) {
1291                 kill_conn(context, conn, selstate);
1292                 return FALSE;
1293             }
1294             in->buf = tmp;
1295             in->bufsize = bufsize;
1296         }
1297
1298         st = context->tls->read(context, conn->http.tls, &in->buf[in->pos],
1299                                 in->bufsize - in->pos - 1, &nread);
1300         if (st != DATA_READ)
1301             break;
1302
1303         in->pos += nread;
1304         in->buf[in->pos] = '\0';
1305     }
1306
1307     if (st == DONE)
1308         return TRUE;
1309
1310     if (st == WANT_READ) {
1311         cm_read(selstate, conn->fd);
1312     } else if (st == WANT_WRITE) {
1313         cm_write(selstate, conn->fd);
1314     } else if (st == ERROR_TLS) {
1315         TRACE_SENDTO_KDC_HTTPS_ERROR_RECV(context, &conn->addr);
1316         kill_conn(context, conn, selstate);
1317     }
1318     return FALSE;
1319 }
1320
1321 /* Return true on readable, valid KKDCPP data. */
1322 static krb5_boolean
1323 service_https_read(krb5_context context, const krb5_data *realm,
1324                    struct conn_state *conn, struct select_state *selstate)
1325 {
1326     krb5_kkdcp_message *pm = NULL;
1327     krb5_data buf;
1328     const char *rep;
1329     struct incoming_message *in = &conn->in;
1330
1331     /* Read data through the encryption layer. */
1332     if (!https_read_bytes(context, conn, selstate))
1333         return FALSE;
1334
1335     /* Find the beginning of the response body. */
1336     rep = strstr(in->buf, "\r\n\r\n");
1337     if (rep == NULL)
1338         goto kill_conn;
1339     rep += 4;
1340
1341     /* Decode the response. */
1342     buf = make_data((char *)rep, in->pos - (rep - in->buf));
1343     if (decode_krb5_kkdcp_message(&buf, &pm) != 0)
1344         goto kill_conn;
1345
1346     /* Check and discard the message length at the front of the kerb_message
1347      * field after decoding.  If it's wrong or missing, something broke. */
1348     if (pm->kerb_message.length < 4 ||
1349         load_32_be(pm->kerb_message.data) != pm->kerb_message.length - 4) {
1350         goto kill_conn;
1351     }
1352
1353     /* Replace all of the content that we read back with just the message. */
1354     memcpy(in->buf, pm->kerb_message.data + 4, pm->kerb_message.length - 4);
1355     in->pos = pm->kerb_message.length - 4;
1356     k5_free_kkdcp_message(context, pm);
1357
1358     return TRUE;
1359
1360 kill_conn:
1361     TRACE_SENDTO_KDC_HTTPS_ERROR(context, in->buf);
1362     k5_free_kkdcp_message(context, pm);
1363     kill_conn(context, conn, selstate);
1364     return FALSE;
1365 }
1366
1367 /* Return the maximum of endtime and the endtime fields of all currently active
1368  * TCP connections. */
1369 static time_ms
1370 get_endtime(time_ms endtime, struct conn_state *conns)
1371 {
1372     struct conn_state *state;
1373
1374     for (state = conns; state != NULL; state = state->next) {
1375         if (state->addr.transport == TCP &&
1376             (state->state == READING || state->state == WRITING) &&
1377             state->endtime > endtime)
1378             endtime = state->endtime;
1379     }
1380     return endtime;
1381 }
1382
1383 static krb5_boolean
1384 service_fds(krb5_context context, struct select_state *selstate,
1385             time_ms interval, struct conn_state *conns,
1386             struct select_state *seltemp, const krb5_data *realm,
1387             int (*msg_handler)(krb5_context, const krb5_data *, void *),
1388             void *msg_handler_data, struct conn_state **winner_out)
1389 {
1390     int e, selret = 0;
1391     time_ms endtime;
1392     struct conn_state *state;
1393
1394     *winner_out = NULL;
1395
1396     e = get_curtime_ms(&endtime);
1397     if (e)
1398         return TRUE;
1399     endtime += interval;
1400
1401     e = 0;
1402     while (selstate->nfds > 0) {
1403         e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
1404                               seltemp, &selret);
1405         if (e == EINTR)
1406             continue;
1407         if (e != 0)
1408             break;
1409
1410         if (selret == 0)
1411             /* Timeout, return to caller.  */
1412             return FALSE;
1413
1414         /* Got something on a socket, process it.  */
1415         for (state = conns; state != NULL; state = state->next) {
1416             int ssflags;
1417
1418             if (state->fd == INVALID_SOCKET)
1419                 continue;
1420             ssflags = cm_get_ssflags(seltemp, state->fd);
1421             if (!ssflags)
1422                 continue;
1423
1424             if (service_dispatch(context, realm, state, selstate, ssflags)) {
1425                 int stop = 1;
1426
1427                 if (msg_handler != NULL) {
1428                     krb5_data reply = make_data(state->in.buf, state->in.pos);
1429
1430                     stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1431                 }
1432
1433                 if (stop) {
1434                     *winner_out = state;
1435                     return TRUE;
1436                 }
1437             }
1438         }
1439     }
1440     if (e != 0)
1441         return TRUE;
1442     return FALSE;
1443 }
1444
1445 /*
1446  * Current worst-case timeout behavior:
1447  *
1448  * First pass, 1s per udp or tcp server, plus 2s at end.
1449  * Second pass, 1s per udp server, plus 4s.
1450  * Third pass, 1s per udp server, plus 8s.
1451  * Fourth => 16s, etc.
1452  *
1453  * Restated:
1454  * Per UDP server, 1s per pass.
1455  * Per TCP server, 1s.
1456  * Backoff delay, 2**(P+1) - 2, where P is total number of passes.
1457  *
1458  * Total = 2**(P+1) + U*P + T - 2.
1459  *
1460  * If P=3, Total = 3*U + T + 14.
1461  * If P=4, Total = 4*U + T + 30.
1462  *
1463  * Note that if you try to reach two ports on one server, it counts as two.
1464  *
1465  * There is one exception to the above rules.  Whenever a TCP connection is
1466  * established, we wait up to ten seconds for it to finish or fail before
1467  * moving on.  This reduces network traffic significantly in a TCP environment.
1468  */
1469
1470 krb5_error_code
1471 k5_sendto(krb5_context context, const krb5_data *message,
1472           const krb5_data *realm, const struct serverlist *servers,
1473           k5_transport_strategy strategy,
1474           struct sendto_callback_info* callback_info, krb5_data *reply,
1475           struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
1476           int *server_used,
1477           /* return 0 -> keep going, 1 -> quit */
1478           int (*msg_handler)(krb5_context, const krb5_data *, void *),
1479           void *msg_handler_data)
1480 {
1481     int pass;
1482     time_ms delay;
1483     krb5_error_code retval;
1484     struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
1485     size_t s;
1486     struct select_state *sel_state = NULL, *seltemp;
1487     char *udpbuf = NULL;
1488     krb5_boolean done = FALSE;
1489
1490     *reply = empty_data();
1491
1492     /* One for use here, listing all our fds in use, and one for
1493      * temporary use in service_fds, for the fds of interest.  */
1494     sel_state = malloc(2 * sizeof(*sel_state));
1495     if (sel_state == NULL) {
1496         retval = ENOMEM;
1497         goto cleanup;
1498     }
1499     seltemp = &sel_state[1];
1500     cm_init_selstate(sel_state);
1501
1502     /* First pass: resolve server hosts, communicate with resulting addresses
1503      * of the preferred transport, and wait 1s for an answer from each. */
1504     for (s = 0; s < servers->nservers && !done; s++) {
1505         /* Find the current tail pointer. */
1506         for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
1507         retval = resolve_server(context, realm, servers, s, strategy, message,
1508                                 &udpbuf, &conns);
1509         if (retval)
1510             goto cleanup;
1511         for (state = *tailptr; state != NULL && !done; state = state->next) {
1512             /* Contact each new connection, deferring those which use the
1513              * non-preferred RFC 4120 transport. */
1514             if (state->defer)
1515                 continue;
1516             if (maybe_send(context, state, message, sel_state, realm,
1517                            callback_info))
1518                 continue;
1519             done = service_fds(context, sel_state, 1000, conns, seltemp,
1520                                realm, msg_handler, msg_handler_data, &winner);
1521         }
1522     }
1523
1524     /* Complete the first pass by contacting servers of the non-preferred RFC
1525      * 4120 transport (if given), waiting 1s for an answer from each. */
1526     for (state = conns; state != NULL && !done; state = state->next) {
1527         if (!state->defer)
1528             continue;
1529         if (maybe_send(context, state, message, sel_state, realm,
1530                        callback_info))
1531             continue;
1532         done = service_fds(context, sel_state, 1000, conns, seltemp,
1533                            realm, msg_handler, msg_handler_data, &winner);
1534     }
1535
1536     /* Wait for two seconds at the end of the first pass. */
1537     if (!done) {
1538         done = service_fds(context, sel_state, 2000, conns, seltemp,
1539                            realm, msg_handler, msg_handler_data, &winner);
1540     }
1541
1542     /* Make remaining passes over all of the connections. */
1543     delay = 4000;
1544     for (pass = 1; pass < MAX_PASS && !done; pass++) {
1545         for (state = conns; state != NULL && !done; state = state->next) {
1546             if (maybe_send(context, state, message, sel_state, realm,
1547                            callback_info))
1548                 continue;
1549             done = service_fds(context, sel_state, 1000, conns, seltemp,
1550                                realm, msg_handler, msg_handler_data, &winner);
1551             if (sel_state->nfds == 0)
1552                 break;
1553         }
1554         /* Wait for the delay backoff at the end of this pass. */
1555         if (!done) {
1556             done = service_fds(context, sel_state, delay, conns, seltemp,
1557                                realm, msg_handler, msg_handler_data, &winner);
1558         }
1559         if (sel_state->nfds == 0)
1560             break;
1561         delay *= 2;
1562     }
1563
1564     if (sel_state->nfds == 0 || !done || winner == NULL) {
1565         retval = KRB5_KDC_UNREACH;
1566         goto cleanup;
1567     }
1568     /* Success!  */
1569     *reply = make_data(winner->in.buf, winner->in.pos);
1570     retval = 0;
1571     winner->in.buf = NULL;
1572     if (server_used != NULL)
1573         *server_used = winner->server_index;
1574     if (remoteaddr != NULL && remoteaddrlen != 0 && *remoteaddrlen > 0)
1575         (void)getpeername(winner->fd, remoteaddr, remoteaddrlen);
1576     TRACE_SENDTO_KDC_RESPONSE(context, reply->length, &winner->addr);
1577
1578 cleanup:
1579     for (state = conns; state != NULL; state = next) {
1580         next = state->next;
1581         if (state->fd != INVALID_SOCKET) {
1582             if (socktype_for_transport(state->addr.transport) == SOCK_STREAM)
1583                 TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &state->addr);
1584             closesocket(state->fd);
1585             free_http_tls_data(context, state);
1586         }
1587         if (state->in.buf != udpbuf)
1588             free(state->in.buf);
1589         if (callback_info) {
1590             callback_info->pfn_cleanup(callback_info->data,
1591                                        &state->callback_buffer);
1592         }
1593         free(state);
1594     }
1595
1596     if (reply->data != udpbuf)
1597         free(udpbuf);
1598     free(sel_state);
1599     return retval;
1600 }