Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2010 - 2013 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "krb5_locl.h"
37 : #include "send_to_kdc_plugin.h"
38 :
39 : /**
40 : * @section send_to_kdc Locating and sending packets to the KDC
41 : *
42 : * The send to kdc code is responsible to request the list of KDC from
43 : * the locate-kdc subsystem and then send requests to each of them.
44 : *
45 : * - Each second a new hostname is tried.
46 : * - If the hostname have several addresses, the first will be tried
47 : * directly then in turn the other will be tried every 3 seconds
48 : * (host_timeout).
49 : * - UDP requests are tried 3 times, and it tried with a individual timeout of kdc_timeout / 3.
50 : * - TCP and HTTP requests are tried 1 time.
51 : *
52 : * Total wait time shorter then (number of addresses * 3) + kdc_timeout seconds.
53 : *
54 : */
55 :
56 : static int
57 0 : init_port(const char *s, int fallback)
58 : {
59 0 : int tmp;
60 :
61 0 : if (s && sscanf(s, "%d", &tmp) == 1)
62 0 : return htons(tmp);
63 0 : return fallback;
64 : }
65 :
66 : struct send_via_plugin_s {
67 : krb5_const_realm realm;
68 : krb5_krbhst_info *hi;
69 : time_t timeout;
70 : const krb5_data *send_data;
71 : krb5_data *receive;
72 : };
73 :
74 :
75 : static krb5_error_code KRB5_LIB_CALL
76 73814 : kdccallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
77 : {
78 73814 : const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
79 73814 : struct send_via_plugin_s *ctx = userctx;
80 :
81 73814 : if (service->send_to_kdc == NULL)
82 0 : return KRB5_PLUGIN_NO_HANDLE;
83 73814 : return service->send_to_kdc(context, plugctx, ctx->hi, ctx->timeout,
84 : ctx->send_data, ctx->receive);
85 : }
86 :
87 : static krb5_error_code KRB5_LIB_CALL
88 85946 : realmcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
89 : {
90 85946 : const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
91 85946 : struct send_via_plugin_s *ctx = userctx;
92 :
93 85946 : if (service->send_to_realm == NULL)
94 0 : return KRB5_PLUGIN_NO_HANDLE;
95 85946 : return service->send_to_realm(context, plugctx, ctx->realm, ctx->timeout,
96 : ctx->send_data, ctx->receive);
97 : }
98 :
99 : static const char *const send_to_kdc_plugin_deps[] = { "krb5", NULL };
100 :
101 : static const struct heim_plugin_data
102 : send_to_kdc_plugin_data = {
103 : "krb5",
104 : KRB5_PLUGIN_SEND_TO_KDC,
105 : KRB5_PLUGIN_SEND_TO_KDC_VERSION_0,
106 : send_to_kdc_plugin_deps,
107 : krb5_get_instance
108 : };
109 :
110 : static krb5_error_code
111 74337 : kdc_via_plugin(krb5_context context,
112 : krb5_krbhst_info *hi,
113 : time_t timeout,
114 : const krb5_data *send_data,
115 : krb5_data *receive)
116 : {
117 3413 : struct send_via_plugin_s userctx;
118 :
119 74337 : userctx.realm = NULL;
120 74337 : userctx.hi = hi;
121 74337 : userctx.timeout = timeout;
122 74337 : userctx.send_data = send_data;
123 74337 : userctx.receive = receive;
124 :
125 74337 : return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
126 : &userctx, kdccallback);
127 : }
128 :
129 : static krb5_error_code
130 86355 : realm_via_plugin(krb5_context context,
131 : krb5_const_realm realm,
132 : time_t timeout,
133 : const krb5_data *send_data,
134 : krb5_data *receive)
135 : {
136 2828 : struct send_via_plugin_s userctx;
137 :
138 86355 : userctx.realm = realm;
139 86355 : userctx.hi = NULL;
140 86355 : userctx.timeout = timeout;
141 86355 : userctx.send_data = send_data;
142 86355 : userctx.receive = receive;
143 :
144 86355 : return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
145 : &userctx, realmcallback);
146 : }
147 :
148 : struct krb5_sendto_ctx_data {
149 : int flags;
150 : int type;
151 : krb5_sendto_ctx_func func;
152 : void *data;
153 : char *hostname;
154 : char *sitename;
155 : krb5_krbhst_handle krbhst;
156 :
157 : /* context2 */
158 : const krb5_data *send_data;
159 : krb5_data response;
160 : heim_array_t hosts;
161 : int stateflags;
162 : #define KRBHST_COMPLETED 1
163 :
164 : /* prexmit */
165 : krb5_sendto_prexmit prexmit_func;
166 : void *prexmit_ctx;
167 :
168 : /* stats */
169 : struct {
170 : struct timeval start_time;
171 : struct timeval name_resolution;
172 : struct timeval krbhst;
173 : unsigned long sent_packets;
174 : unsigned long num_hosts;
175 : } stats;
176 : unsigned int stid;
177 : };
178 :
179 : static void KRB5_CALLCONV
180 71298 : dealloc_sendto_ctx(void *ptr)
181 : {
182 71298 : krb5_sendto_ctx ctx = (krb5_sendto_ctx)ptr;
183 71298 : if (ctx->hostname)
184 0 : free(ctx->hostname);
185 71298 : if (ctx->sitename)
186 0 : free(ctx->sitename);
187 71298 : heim_release(ctx->hosts);
188 71298 : heim_release(ctx->krbhst);
189 71298 : }
190 :
191 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
192 71298 : krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
193 : {
194 71298 : *ctx = heim_alloc(sizeof(**ctx), "sendto-context", dealloc_sendto_ctx);
195 71298 : if (*ctx == NULL)
196 0 : return krb5_enomem(context);
197 71298 : (*ctx)->hosts = heim_array_create();
198 :
199 71298 : return 0;
200 : }
201 :
202 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
203 12431 : krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
204 : {
205 12431 : ctx->flags |= flags;
206 12431 : }
207 :
208 : KRB5_LIB_FUNCTION int KRB5_LIB_CALL
209 12431 : krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
210 : {
211 12431 : return ctx->flags;
212 : }
213 :
214 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
215 0 : krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
216 : {
217 0 : ctx->type = type;
218 0 : }
219 :
220 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
221 71298 : krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
222 : krb5_sendto_ctx_func func,
223 : void *data)
224 : {
225 71298 : ctx->func = func;
226 71298 : ctx->data = data;
227 71298 : }
228 :
229 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
230 0 : _krb5_sendto_ctx_set_prexmit(krb5_sendto_ctx ctx,
231 : krb5_sendto_prexmit prexmit,
232 : void *data)
233 : {
234 0 : ctx->prexmit_func = prexmit;
235 0 : ctx->prexmit_ctx = data;
236 0 : }
237 :
238 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
239 0 : krb5_sendto_set_hostname(krb5_context context,
240 : krb5_sendto_ctx ctx,
241 : const char *hostname)
242 : {
243 0 : char *newname;
244 :
245 : /*
246 : * Handle the case where hostname == ctx->hostname by copying it first, and
247 : * disposing of any previous value after.
248 : */
249 0 : newname = strdup(hostname);
250 0 : if (newname == NULL)
251 0 : return krb5_enomem(context);
252 0 : free(ctx->hostname);
253 0 : ctx->hostname = newname;
254 0 : return 0;
255 : }
256 :
257 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
258 0 : krb5_sendto_set_sitename(krb5_context context,
259 : krb5_sendto_ctx ctx,
260 : const char *sitename)
261 : {
262 0 : char *newname;
263 :
264 0 : newname = strdup(sitename);
265 0 : if (newname == NULL)
266 0 : return krb5_enomem(context);
267 0 : free(ctx->sitename);
268 0 : ctx->sitename = newname;
269 0 : return 0;
270 : }
271 :
272 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
273 0 : _krb5_sendto_ctx_set_krb5hst(krb5_context context,
274 : krb5_sendto_ctx ctx,
275 : krb5_krbhst_handle handle)
276 : {
277 0 : heim_release(ctx->krbhst);
278 0 : ctx->krbhst = heim_retain(handle);
279 0 : }
280 :
281 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
282 71298 : krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
283 : {
284 71298 : heim_release(ctx);
285 71298 : }
286 :
287 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
288 74333 : _krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
289 : const krb5_data *reply, int *action)
290 : {
291 3413 : krb5_error_code ret;
292 3413 : KRB_ERROR error;
293 :
294 74333 : if(krb5_rd_error(context, reply, &error))
295 46622 : return 0;
296 :
297 25468 : ret = krb5_error_from_rd_error(context, &error, NULL);
298 25468 : krb5_free_error_contents(context, &error);
299 :
300 25468 : switch(ret) {
301 12431 : case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
302 12431 : if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
303 0 : break;
304 12431 : krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
305 12431 : *action = KRB5_SENDTO_RESET;
306 12431 : break;
307 : }
308 0 : case KRB5KDC_ERR_SVC_UNAVAILABLE:
309 0 : *action = KRB5_SENDTO_RESET;
310 0 : break;
311 : }
312 24298 : return 0;
313 : }
314 :
315 : /*
316 : *
317 : */
318 :
319 : struct host;
320 :
321 : struct host_fun {
322 : krb5_error_code (*prepare)(krb5_context, struct host *, const krb5_data *);
323 : krb5_error_code (*send_fn)(krb5_context, struct host *);
324 : krb5_error_code (*recv_fn)(krb5_context, struct host *, krb5_data *);
325 : int ntries;
326 : };
327 :
328 : struct host {
329 : enum host_state { CONNECT, CONNECTING, CONNECTED, WAITING_REPLY, DEAD } state;
330 : krb5_krbhst_info *hi;
331 : struct addrinfo *freeai;
332 : struct addrinfo *ai;
333 : rk_socket_t fd;
334 : const struct host_fun *fun;
335 : unsigned int tries;
336 : time_t timeout;
337 : krb5_data data;
338 : unsigned int tid;
339 : };
340 :
341 : static void
342 : debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
343 : __attribute__ ((__format__ (__printf__, 4, 5)));
344 :
345 : static void
346 176630 : debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
347 : {
348 176630 : const char *proto = "unknown";
349 6930 : const char *state;
350 6930 : char name[NI_MAXHOST], port[NI_MAXSERV];
351 176630 : char *text = NULL;
352 6930 : va_list ap;
353 6930 : int ret;
354 :
355 176630 : if (!_krb5_have_debug(context, 5))
356 176614 : return;
357 :
358 16 : va_start(ap, fmt);
359 16 : ret = vasprintf(&text, fmt, ap);
360 16 : va_end(ap);
361 16 : if (ret == -1 || text == NULL)
362 0 : return;
363 :
364 16 : if (host->hi->proto == KRB5_KRBHST_HTTP)
365 0 : proto = "http";
366 16 : else if (host->hi->proto == KRB5_KRBHST_TCP)
367 4 : proto = "tcp";
368 12 : else if (host->hi->proto == KRB5_KRBHST_UDP)
369 12 : proto = "udp";
370 :
371 16 : if (getnameinfo(host->ai->ai_addr, host->ai->ai_addrlen,
372 : name, sizeof(name), port, sizeof(port),
373 : NI_NUMERICHOST|NI_NUMERICSERV|NI_NUMERICSCOPE) != 0)
374 0 : name[0] = '\0';
375 :
376 16 : switch (host->state) {
377 4 : case CONNECT: state = "CONNECT"; break;
378 0 : case CONNECTING: state = "CONNECTING"; break;
379 4 : case CONNECTED: state = "CONNECTED"; break;
380 8 : case WAITING_REPLY: state = "WAITING_REPLY"; break;
381 0 : case DEAD: state = "DEAD"; break;
382 0 : default: state = "unknown"; break;
383 : }
384 :
385 16 : _krb5_debug(context, level, "%s: %s %s:%s (%s) state=%s tid: %08x", text,
386 16 : proto, name, port, host->hi->hostname, state, host->tid);
387 16 : free(text);
388 : }
389 :
390 :
391 : static void HEIM_CALLCONV
392 41795 : deallocate_host(void *ptr)
393 : {
394 41795 : struct host *host = ptr;
395 41795 : if (!rk_IS_BAD_SOCKET(host->fd))
396 41795 : rk_closesocket(host->fd);
397 41795 : krb5_data_free(&host->data);
398 41795 : if (host->freeai)
399 0 : freeaddrinfo(host->freeai);
400 41795 : host->freeai = NULL;
401 41795 : host->ai = NULL;
402 41795 : }
403 :
404 : static void
405 0 : host_dead(krb5_context context, struct host *host, const char *msg)
406 : {
407 0 : debug_host(context, 5, host, "%s", msg);
408 0 : rk_closesocket(host->fd);
409 0 : host->fd = rk_INVALID_SOCKET;
410 0 : host->state = DEAD;
411 0 : }
412 :
413 : static krb5_error_code
414 38193 : send_stream(krb5_context context, struct host *host)
415 : {
416 1658 : ssize_t len;
417 :
418 38193 : len = krb5_net_write(context, &host->fd, host->data.data, host->data.length);
419 :
420 38193 : if (len < 0)
421 0 : return errno;
422 38193 : else if (len < host->data.length) {
423 0 : host->data.length -= len;
424 0 : memmove(host->data.data, ((uint8_t *)host->data.data) + len, host->data.length - len);
425 0 : return -1;
426 : } else {
427 38193 : krb5_data_free(&host->data);
428 38193 : return 0;
429 : }
430 : }
431 :
432 : static krb5_error_code
433 47691 : recv_stream(krb5_context context, struct host *host)
434 : {
435 1956 : krb5_error_code ret;
436 1956 : size_t oldlen;
437 1956 : ssize_t sret;
438 1956 : int nbytes;
439 :
440 47691 : if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
441 0 : return HEIM_NET_CONN_REFUSED;
442 :
443 47691 : if (context->max_msg_size - host->data.length < nbytes) {
444 0 : krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
445 0 : N_("TCP message from KDC too large %d", ""),
446 0 : (int)(host->data.length + nbytes));
447 0 : return KRB5KRB_ERR_FIELD_TOOLONG;
448 : }
449 :
450 47691 : oldlen = host->data.length;
451 :
452 47691 : ret = krb5_data_realloc(&host->data, oldlen + nbytes + 1 /* NUL */);
453 47691 : if (ret)
454 0 : return ret;
455 :
456 47691 : sret = krb5_net_read(context, &host->fd, ((uint8_t *)host->data.data) + oldlen, nbytes);
457 47691 : if (sret <= 0) {
458 0 : ret = errno;
459 0 : return ret;
460 : }
461 47691 : host->data.length = oldlen + sret;
462 : /* zero terminate for http transport */
463 47691 : ((uint8_t *)host->data.data)[host->data.length] = '\0';
464 :
465 47691 : return 0;
466 : }
467 :
468 : /*
469 : *
470 : */
471 :
472 : static void
473 41779 : host_next_timeout(krb5_context context, struct host *host)
474 : {
475 41779 : host->timeout = context->kdc_timeout / host->fun->ntries;
476 41779 : if (host->timeout == 0)
477 0 : host->timeout = 1;
478 :
479 41779 : host->timeout += time(NULL);
480 41779 : }
481 :
482 : /*
483 : * connected host
484 : */
485 :
486 : static void
487 41779 : host_connected(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
488 : {
489 1658 : krb5_error_code ret;
490 :
491 41779 : host->state = CONNECTED;
492 : /*
493 : * Now prepare data to send to host
494 : */
495 41779 : if (ctx->prexmit_func) {
496 0 : krb5_data data;
497 :
498 0 : krb5_data_zero(&data);
499 :
500 0 : ret = ctx->prexmit_func(context, host->hi->proto,
501 : ctx->prexmit_ctx, host->fd, &data);
502 0 : if (ret == 0) {
503 0 : if (data.length == 0) {
504 0 : host_dead(context, host, "prexmit function didn't send data");
505 0 : return;
506 : }
507 0 : ret = host->fun->prepare(context, host, &data);
508 0 : krb5_data_free(&data);
509 : }
510 :
511 : } else {
512 41779 : ret = host->fun->prepare(context, host, ctx->send_data);
513 : }
514 41779 : if (ret)
515 0 : debug_host(context, 5, host, "failed to prexmit/prepare");
516 : }
517 :
518 : /*
519 : * connect host
520 : */
521 :
522 : static void
523 41779 : host_connect(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
524 : {
525 41779 : krb5_krbhst_info *hi = host->hi;
526 41779 : struct addrinfo *ai = host->ai;
527 :
528 41779 : debug_host(context, 5, host, "connecting to host");
529 :
530 41779 : if (connect(host->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
531 : #ifdef HAVE_WINSOCK
532 : if (WSAGetLastError() == WSAEWOULDBLOCK)
533 : errno = EINPROGRESS;
534 : #endif /* HAVE_WINSOCK */
535 0 : if (errno == EINPROGRESS && (hi->proto == KRB5_KRBHST_HTTP || hi->proto == KRB5_KRBHST_TCP)) {
536 0 : debug_host(context, 5, host, "connecting to %d", host->fd);
537 0 : host->state = CONNECTING;
538 : } else {
539 0 : host_dead(context, host, "failed to connect");
540 : }
541 : } else {
542 41779 : host_connected(context, ctx, host);
543 : }
544 :
545 41779 : host_next_timeout(context, host);
546 41779 : }
547 :
548 : /*
549 : * HTTP transport
550 : */
551 :
552 : static krb5_error_code
553 0 : prepare_http(krb5_context context, struct host *host, const krb5_data *data)
554 : {
555 0 : char *str = NULL, *request = NULL;
556 0 : krb5_error_code ret;
557 0 : int len;
558 :
559 0 : heim_assert(host->data.length == 0, "prepare_http called twice");
560 :
561 0 : len = rk_base64_encode(data->data, data->length, &str);
562 0 : if(len < 0)
563 0 : return ENOMEM;
564 :
565 0 : if (context->http_proxy)
566 0 : ret = asprintf(&request, "GET http://%s/%s HTTP/1.0\r\n\r\n", host->hi->hostname, str);
567 : else
568 0 : ret = asprintf(&request, "GET /%s HTTP/1.0\r\n\r\n", str);
569 0 : free(str);
570 0 : if(ret < 0 || request == NULL)
571 0 : return ENOMEM;
572 :
573 0 : host->data.data = request;
574 0 : host->data.length = strlen(request);
575 :
576 0 : return 0;
577 : }
578 :
579 : static krb5_error_code
580 0 : recv_http(krb5_context context, struct host *host, krb5_data *data)
581 : {
582 0 : krb5_error_code ret;
583 0 : unsigned long rep_len;
584 0 : size_t len;
585 0 : char *p;
586 :
587 : /*
588 : * recv_stream returns a NUL terminated stream
589 : */
590 :
591 0 : ret = recv_stream(context, host);
592 0 : if (ret)
593 0 : return ret;
594 :
595 0 : p = strstr(host->data.data, "\r\n\r\n");
596 0 : if (p == NULL)
597 0 : return -1;
598 0 : p += 4;
599 :
600 0 : len = host->data.length - (p - (char *)host->data.data);
601 0 : if (len < 4)
602 0 : return -1;
603 :
604 0 : _krb5_get_int(p, &rep_len, 4);
605 0 : if (len < rep_len)
606 0 : return -1;
607 :
608 0 : p += 4;
609 :
610 0 : memmove(host->data.data, p, rep_len);
611 0 : host->data.length = rep_len;
612 :
613 0 : *data = host->data;
614 0 : krb5_data_zero(&host->data);
615 :
616 0 : return 0;
617 : }
618 :
619 : /*
620 : * TCP transport
621 : */
622 :
623 : static krb5_error_code
624 38193 : prepare_tcp(krb5_context context, struct host *host, const krb5_data *data)
625 : {
626 1658 : krb5_error_code ret;
627 1658 : krb5_storage *sp;
628 :
629 38193 : heim_assert(host->data.length == 0, "prepare_tcp called twice");
630 :
631 38193 : sp = krb5_storage_emem();
632 38193 : if (sp == NULL)
633 0 : return ENOMEM;
634 :
635 38193 : ret = krb5_store_data(sp, *data);
636 38193 : if (ret) {
637 0 : krb5_storage_free(sp);
638 0 : return ret;
639 : }
640 38193 : ret = krb5_storage_to_data(sp, &host->data);
641 38193 : krb5_storage_free(sp);
642 :
643 38193 : return ret;
644 : }
645 :
646 : static krb5_error_code
647 47691 : recv_tcp(krb5_context context, struct host *host, krb5_data *data)
648 : {
649 1956 : krb5_error_code ret;
650 1956 : unsigned long pktlen;
651 :
652 47691 : ret = recv_stream(context, host);
653 47691 : if (ret)
654 0 : return ret;
655 :
656 47691 : if (host->data.length < 4)
657 0 : return -1;
658 :
659 47691 : _krb5_get_int(host->data.data, &pktlen, 4);
660 :
661 47691 : if (pktlen > host->data.length - 4)
662 9200 : return -1;
663 :
664 38193 : memmove(host->data.data, ((uint8_t *)host->data.data) + 4, host->data.length - 4);
665 38193 : host->data.length -= 4;
666 :
667 38193 : *data = host->data;
668 38193 : krb5_data_zero(&host->data);
669 :
670 38193 : return 0;
671 : }
672 :
673 : /*
674 : * UDP transport
675 : */
676 :
677 : static krb5_error_code
678 3586 : prepare_udp(krb5_context context, struct host *host, const krb5_data *data)
679 : {
680 3586 : return krb5_data_copy(&host->data, data->data, data->length);
681 : }
682 :
683 : static krb5_error_code
684 3586 : send_udp(krb5_context context, struct host *host)
685 : {
686 3586 : if (send(host->fd, host->data.data, host->data.length, 0) < 0)
687 0 : return errno;
688 3586 : return 0;
689 : }
690 :
691 : static krb5_error_code
692 3586 : recv_udp(krb5_context context, struct host *host, krb5_data *data)
693 : {
694 0 : krb5_error_code ret;
695 0 : int nbytes;
696 :
697 :
698 3586 : if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
699 0 : return HEIM_NET_CONN_REFUSED;
700 :
701 3586 : if (context->max_msg_size < nbytes) {
702 0 : krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
703 0 : N_("UDP message from KDC too large %d", ""),
704 : (int)nbytes);
705 0 : return KRB5KRB_ERR_FIELD_TOOLONG;
706 : }
707 :
708 3586 : ret = krb5_data_alloc(data, nbytes);
709 3586 : if (ret)
710 0 : return ret;
711 :
712 3586 : ret = recv(host->fd, data->data, data->length, 0);
713 3586 : if (ret < 0) {
714 0 : ret = errno;
715 0 : krb5_data_free(data);
716 0 : return ret;
717 : }
718 3586 : data->length = ret;
719 :
720 3586 : return 0;
721 : }
722 :
723 : static const struct host_fun http_fun = {
724 : prepare_http,
725 : send_stream,
726 : recv_http,
727 : 1
728 : };
729 : static const struct host_fun tcp_fun = {
730 : prepare_tcp,
731 : send_stream,
732 : recv_tcp,
733 : 1
734 : };
735 : static const struct host_fun udp_fun = {
736 : prepare_udp,
737 : send_udp,
738 : recv_udp,
739 : 3
740 : };
741 :
742 :
743 : /*
744 : * Host state machine
745 : */
746 :
747 : static int
748 93074 : eval_host_state(krb5_context context,
749 : krb5_sendto_ctx ctx,
750 : struct host *host,
751 : int readable, int writeable)
752 : {
753 3614 : krb5_error_code ret;
754 :
755 93074 : if (host->state == CONNECT) {
756 : /* check if its this host time to connect */
757 18 : if (host->timeout < time(NULL))
758 0 : host_connect(context, ctx, host);
759 18 : return 0;
760 : }
761 :
762 93056 : if (host->state == CONNECTING && writeable)
763 0 : host_connected(context, ctx, host);
764 :
765 93056 : if (readable) {
766 :
767 51277 : debug_host(context, 5, host, "reading packet");
768 :
769 51277 : ret = host->fun->recv_fn(context, host, &ctx->response);
770 51277 : if (ret == -1) {
771 : /* not done yet */
772 41779 : } else if (ret == 0) {
773 : /* if recv_foo function returns 0, we have a complete reply */
774 41779 : debug_host(context, 5, host, "host completed");
775 41779 : return 1;
776 : } else {
777 0 : host_dead(context, host, "host disconnected");
778 : }
779 : }
780 :
781 : /* check if there is anything to send, state might DEAD after read */
782 51277 : if (writeable && host->state == CONNECTED) {
783 :
784 41779 : ctx->stats.sent_packets++;
785 :
786 41779 : debug_host(context, 5, host, "writing packet");
787 :
788 41779 : ret = host->fun->send_fn(context, host);
789 41779 : if (ret == -1) {
790 : /* not done yet */
791 41779 : } else if (ret) {
792 0 : host_dead(context, host, "host dead, write failed");
793 : } else
794 41779 : host->state = WAITING_REPLY;
795 : }
796 :
797 49321 : return 0;
798 : }
799 :
800 : /*
801 : *
802 : */
803 :
804 : static krb5_error_code
805 74337 : submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi)
806 : {
807 74337 : unsigned long submitted_host = 0;
808 74337 : struct addrinfo *freeai = NULL;
809 3413 : struct timeval nrstart, nrstop;
810 3413 : krb5_error_code ret;
811 74337 : struct addrinfo *ai = NULL, *a;
812 3413 : struct host *host;
813 :
814 74337 : ret = kdc_via_plugin(context, hi, context->kdc_timeout,
815 : ctx->send_data, &ctx->response);
816 74337 : if (ret == 0) {
817 30799 : return 0;
818 41783 : } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
819 4 : _krb5_debug(context, 5, "send via plugin failed %s: %d",
820 4 : hi->hostname, ret);
821 4 : return ret;
822 : }
823 :
824 : /*
825 : * If we have a proxy, let use the address of the proxy instead of
826 : * the KDC and let the proxy deal with the resolving of the KDC.
827 : */
828 :
829 41779 : gettimeofday(&nrstart, NULL);
830 :
831 41779 : if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
832 0 : char *proxy2 = strdup(context->http_proxy);
833 0 : char *el, *proxy = proxy2;
834 0 : struct addrinfo hints;
835 0 : char portstr[NI_MAXSERV];
836 0 : unsigned short nport;
837 :
838 0 : if (proxy == NULL)
839 0 : return ENOMEM;
840 0 : if (strncmp(proxy, "http://", 7) == 0)
841 0 : proxy += 7;
842 :
843 : /* check for url terminating slash */
844 0 : el = strchr(proxy, '/');
845 0 : if (el != NULL)
846 0 : *el = '\0';
847 :
848 : /* check for port in hostname, used below as port */
849 0 : el = strchr(proxy, ':');
850 0 : if(el != NULL)
851 0 : *el++ = '\0';
852 :
853 0 : memset(&hints, 0, sizeof(hints));
854 0 : hints.ai_family = PF_UNSPEC;
855 0 : hints.ai_socktype = SOCK_STREAM;
856 :
857 : /* On some systems ntohs(foo(..., htons(...))) causes shadowing */
858 0 : nport = init_port(el, htons(80));
859 0 : snprintf(portstr, sizeof(portstr), "%d", ntohs(nport));
860 :
861 0 : if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
862 : NULL)) {
863 0 : hints.ai_flags &= ~AI_CANONNAME;
864 0 : hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
865 : }
866 0 : ret = getaddrinfo(proxy, portstr, &hints, &ai);
867 0 : free(proxy2);
868 0 : if (ret)
869 0 : return krb5_eai_to_heim_errno(ret, errno);
870 :
871 0 : freeai = ai;
872 :
873 : } else {
874 41779 : ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
875 41779 : if (ret)
876 0 : return ret;
877 : }
878 :
879 : /* add up times */
880 41779 : gettimeofday(&nrstop, NULL);
881 41779 : timevalsub(&nrstop, &nrstart);
882 41779 : timevaladd(&ctx->stats.name_resolution, &nrstop);
883 :
884 41779 : ctx->stats.num_hosts++;
885 :
886 83574 : for (a = ai; a != NULL; a = a->ai_next) {
887 1658 : rk_socket_t fd;
888 :
889 41795 : fd = socket(a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
890 41795 : if (rk_IS_BAD_SOCKET(fd))
891 0 : continue;
892 41795 : rk_cloexec(fd);
893 :
894 : #ifndef NO_LIMIT_FD_SETSIZE
895 41795 : if (fd >= FD_SETSIZE) {
896 0 : _krb5_debug(context, 0, "fd too large for select");
897 0 : rk_closesocket(fd);
898 0 : continue;
899 : }
900 : #endif
901 41795 : socket_set_nonblocking(fd, 1);
902 :
903 41795 : host = heim_alloc(sizeof(*host), "sendto-host", deallocate_host);
904 41795 : if (host == NULL) {
905 0 : if (freeai)
906 0 : freeaddrinfo(freeai);
907 0 : rk_closesocket(fd);
908 0 : return ENOMEM;
909 : }
910 41795 : host->hi = hi;
911 41795 : host->fd = fd;
912 41795 : host->ai = a;
913 41795 : host->freeai = freeai;
914 41795 : freeai = NULL;
915 : /* next version of stid */
916 41795 : host->tid = ctx->stid = (ctx->stid & 0xffff0000) | ((ctx->stid & 0xffff) + 1);
917 :
918 41795 : host->state = CONNECT;
919 :
920 41795 : switch (host->hi->proto) {
921 0 : case KRB5_KRBHST_HTTP :
922 0 : host->fun = &http_fun;
923 0 : break;
924 38203 : case KRB5_KRBHST_TCP :
925 38203 : host->fun = &tcp_fun;
926 38203 : break;
927 3592 : case KRB5_KRBHST_UDP :
928 3592 : host->fun = &udp_fun;
929 3592 : break;
930 0 : default:
931 0 : heim_abort("undefined http transport protocol: %d", (int)host->hi->proto);
932 : }
933 :
934 41795 : host->tries = host->fun->ntries;
935 :
936 : /*
937 : * Connect directly next host, wait a host_timeout for each next address.
938 : * We try host_connect() here, checking the return code because as we do
939 : * non-blocking connects, any error here indicates that the address is just
940 : * offline. That is, it's something like "No route to host" which is not
941 : * worth retrying. And so, we fail directly and immediately to the next
942 : * address for this host without enqueueing the address for retries.
943 : */
944 41795 : if (submitted_host == 0) {
945 41779 : host_connect(context, ctx, host);
946 41779 : if (host->state == DEAD)
947 0 : continue;
948 : } else {
949 16 : debug_host(context, 5, host,
950 : "Queuing host in future (in %ds), its the %lu address on the same name",
951 16 : (int)(context->host_timeout * submitted_host), submitted_host + 1);
952 16 : host->timeout = time(NULL) + (submitted_host * context->host_timeout);
953 : }
954 :
955 41795 : heim_array_append_value(ctx->hosts, host);
956 41795 : heim_release(host);
957 41795 : submitted_host++;
958 : }
959 :
960 41779 : if (freeai)
961 1658 : freeai = NULL;
962 :
963 41779 : if (submitted_host == 0)
964 0 : return KRB5_KDC_UNREACH;
965 :
966 40121 : return 0;
967 : }
968 :
969 : struct wait_ctx {
970 : krb5_context context;
971 : krb5_sendto_ctx ctx;
972 : fd_set rfds;
973 : fd_set wfds;
974 : rk_socket_t max_fd;
975 : int got_reply;
976 : time_t timenow;
977 : };
978 :
979 : static void
980 93090 : wait_setup(heim_object_t obj, void *iter_ctx, int *stop)
981 : {
982 93090 : struct wait_ctx *wait_ctx = iter_ctx;
983 93090 : struct host *h = (struct host *)obj;
984 :
985 93090 : if (h->state == CONNECT) {
986 34 : if (h->timeout >= wait_ctx->timenow)
987 34 : return;
988 0 : host_connect(wait_ctx->context, wait_ctx->ctx, h);
989 : }
990 :
991 : /* skip dead hosts */
992 93056 : if (h->state == DEAD)
993 0 : return;
994 :
995 : /* if host timed out, dec tries and (retry or kill host) */
996 93056 : if (h->timeout < wait_ctx->timenow) {
997 0 : heim_assert(h->tries != 0, "tries should not reach 0");
998 0 : h->tries--;
999 0 : if (h->tries == 0) {
1000 0 : host_dead(wait_ctx->context, h, "host timed out");
1001 0 : return;
1002 : } else {
1003 0 : debug_host(wait_ctx->context, 5, h, "retrying sending to");
1004 0 : host_next_timeout(wait_ctx->context, h);
1005 0 : host_connected(wait_ctx->context, wait_ctx->ctx, h);
1006 : }
1007 : }
1008 :
1009 : #ifndef NO_LIMIT_FD_SETSIZE
1010 93056 : heim_assert(h->fd < FD_SETSIZE, "fd too large");
1011 : #endif
1012 93056 : switch (h->state) {
1013 51277 : case WAITING_REPLY:
1014 51277 : FD_SET(h->fd, &wait_ctx->rfds);
1015 51277 : break;
1016 41779 : case CONNECTING:
1017 : case CONNECTED:
1018 41779 : FD_SET(h->fd, &wait_ctx->rfds);
1019 41779 : FD_SET(h->fd, &wait_ctx->wfds);
1020 41779 : break;
1021 0 : default:
1022 0 : debug_host(wait_ctx->context, 5, h, "invalid sendto host state");
1023 0 : heim_abort("invalid sendto host state");
1024 : }
1025 93056 : if (h->fd > wait_ctx->max_fd || wait_ctx->max_fd == rk_INVALID_SOCKET)
1026 93056 : wait_ctx->max_fd = h->fd;
1027 : }
1028 :
1029 : static int
1030 93090 : wait_filter_dead(heim_object_t obj, void *ctx)
1031 : {
1032 93090 : struct host *h = (struct host *)obj;
1033 93090 : return (int)((h->state == DEAD) ? true : false);
1034 : }
1035 :
1036 : static void
1037 0 : wait_accelerate(heim_object_t obj, void *ctx, int *stop)
1038 : {
1039 0 : struct host *h = (struct host *)obj;
1040 :
1041 0 : if (h->state == CONNECT && h->timeout > 0)
1042 0 : h->timeout--;
1043 0 : }
1044 :
1045 : static void
1046 93074 : wait_process(heim_object_t obj, void *ctx, int *stop)
1047 : {
1048 93074 : struct wait_ctx *wait_ctx = ctx;
1049 93074 : struct host *h = (struct host *)obj;
1050 3614 : int readable, writeable;
1051 93074 : heim_assert(h->state != DEAD, "dead host resurected");
1052 :
1053 : #ifndef NO_LIMIT_FD_SETSIZE
1054 93074 : heim_assert(h->fd < FD_SETSIZE, "fd too large");
1055 : #endif
1056 93074 : readable = FD_ISSET(h->fd, &wait_ctx->rfds);
1057 93074 : writeable = FD_ISSET(h->fd, &wait_ctx->wfds);
1058 :
1059 93074 : if (readable || writeable || h->state == CONNECT)
1060 93074 : wait_ctx->got_reply |= eval_host_state(wait_ctx->context, wait_ctx->ctx, h, readable, writeable);
1061 :
1062 : /* if there is already a reply, just fall though the array */
1063 93074 : if (wait_ctx->got_reply)
1064 41779 : *stop = 1;
1065 93074 : }
1066 :
1067 : static krb5_error_code
1068 133590 : wait_response(krb5_context context, int *action, krb5_sendto_ctx ctx)
1069 : {
1070 5369 : struct wait_ctx wait_ctx;
1071 5369 : struct timeval tv;
1072 5369 : int ret;
1073 :
1074 133590 : wait_ctx.context = context;
1075 133590 : wait_ctx.ctx = ctx;
1076 2271030 : FD_ZERO(&wait_ctx.rfds);
1077 2271030 : FD_ZERO(&wait_ctx.wfds);
1078 133590 : wait_ctx.max_fd = rk_INVALID_SOCKET;
1079 :
1080 : /* oh, we have a reply, it must be a plugin that got it for us */
1081 133590 : if (ctx->response.length) {
1082 32554 : *action = KRB5_SENDTO_FILTER;
1083 32554 : return 0;
1084 : }
1085 :
1086 101036 : wait_ctx.timenow = time(NULL);
1087 :
1088 101036 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_setup);
1089 101036 : heim_array_filter_f(ctx->hosts, &wait_ctx, wait_filter_dead);
1090 :
1091 101036 : if (heim_array_get_length(ctx->hosts) == 0) {
1092 7980 : if (ctx->stateflags & KRBHST_COMPLETED) {
1093 7980 : _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
1094 : "trying to pulling more hosts");
1095 7980 : *action = KRB5_SENDTO_FAILED;
1096 : } else {
1097 0 : _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
1098 : "and no more hosts -> failure");
1099 0 : *action = KRB5_SENDTO_TIMEOUT;
1100 : }
1101 7980 : return 0;
1102 : }
1103 :
1104 93056 : if (wait_ctx.max_fd == rk_INVALID_SOCKET) {
1105 : /*
1106 : * If we don't find a host which can make progress, then
1107 : * we accelerate the process by moving all of the contestants
1108 : * up by 1s.
1109 : */
1110 0 : _krb5_debug(context, 5, "wait_response: moving the contestants forward");
1111 0 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_accelerate);
1112 0 : return 0;
1113 : }
1114 :
1115 93056 : tv.tv_sec = 1;
1116 93056 : tv.tv_usec = 0;
1117 :
1118 93056 : ret = select(wait_ctx.max_fd + 1, &wait_ctx.rfds, &wait_ctx.wfds, NULL, &tv);
1119 93056 : if (ret < 0)
1120 0 : return errno;
1121 93056 : if (ret == 0) {
1122 0 : *action = KRB5_SENDTO_TIMEOUT;
1123 0 : return 0;
1124 : }
1125 :
1126 93056 : wait_ctx.got_reply = 0;
1127 93056 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_process);
1128 93056 : if (wait_ctx.got_reply)
1129 41779 : *action = KRB5_SENDTO_FILTER;
1130 : else
1131 51277 : *action = KRB5_SENDTO_CONTINUE;
1132 :
1133 89442 : return 0;
1134 : }
1135 :
1136 : static void
1137 98786 : reset_context(krb5_context context, krb5_sendto_ctx ctx)
1138 : {
1139 98786 : krb5_data_free(&ctx->response);
1140 98786 : heim_release(ctx->hosts);
1141 98786 : ctx->hosts = heim_array_create();
1142 98786 : ctx->stateflags = 0;
1143 98786 : }
1144 :
1145 :
1146 : /*
1147 : *
1148 : */
1149 :
1150 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1151 86355 : krb5_sendto_context(krb5_context context,
1152 : krb5_sendto_ctx ctx,
1153 : const krb5_data *send_data,
1154 : krb5_const_realm realm,
1155 : krb5_data *receive)
1156 : {
1157 86355 : krb5_error_code ret = 0;
1158 86355 : krb5_krbhst_handle handle = NULL;
1159 2828 : struct timeval nrstart, nrstop, stop_time;
1160 86355 : int type, freectx = 0;
1161 2828 : int action;
1162 86355 : int numreset = 0;
1163 :
1164 86355 : krb5_data_zero(receive);
1165 :
1166 86355 : if (ctx == NULL) {
1167 0 : ret = krb5_sendto_ctx_alloc(context, &ctx);
1168 0 : if (ret)
1169 0 : goto out;
1170 0 : freectx = 1;
1171 : }
1172 :
1173 86355 : ctx->stid = (context->num_kdc_requests++) << 16;
1174 :
1175 86355 : memset(&ctx->stats, 0, sizeof(ctx->stats));
1176 86355 : gettimeofday(&ctx->stats.start_time, NULL);
1177 :
1178 86355 : type = ctx->type;
1179 86355 : if (type == 0) {
1180 86355 : if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
1181 0 : type = KRB5_KRBHST_ADMIN;
1182 : else
1183 86355 : type = KRB5_KRBHST_KDC;
1184 : }
1185 :
1186 86355 : ctx->send_data = send_data;
1187 :
1188 86355 : if ((int)send_data->length > context->large_msg_size)
1189 48039 : ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
1190 :
1191 : /* loop until we get back a appropriate response */
1192 :
1193 86355 : action = KRB5_SENDTO_INITIAL;
1194 :
1195 319144 : while (1) {
1196 15608 : krb5_krbhst_info *hi;
1197 :
1198 405499 : switch (action) {
1199 86355 : case KRB5_SENDTO_INITIAL:
1200 89183 : ret = realm_via_plugin(context, realm, context->kdc_timeout,
1201 86355 : send_data, &ctx->response);
1202 86355 : if (ret == 0 || ret != KRB5_PLUGIN_NO_HANDLE) {
1203 16473 : action = KRB5_SENDTO_DONE;
1204 16473 : break;
1205 : }
1206 69882 : action = KRB5_SENDTO_KRBHST;
1207 3413 : HEIM_FALLTHROUGH;
1208 82313 : case KRB5_SENDTO_KRBHST:
1209 82313 : if (ctx->krbhst == NULL) {
1210 82313 : ret = krb5_krbhst_init_flags(context, realm, type,
1211 78900 : ctx->flags, &handle);
1212 82313 : if (ret)
1213 86355 : goto out;
1214 :
1215 82313 : if (ctx->hostname) {
1216 0 : ret = krb5_krbhst_set_hostname(context, handle, ctx->hostname);
1217 0 : if (ret)
1218 0 : goto out;
1219 : }
1220 82313 : if (ctx->sitename) {
1221 0 : ret = krb5_krbhst_set_sitename(context, handle, ctx->sitename);
1222 0 : if (ret)
1223 0 : goto out;
1224 : }
1225 : } else {
1226 0 : handle = heim_retain(ctx->krbhst);
1227 : }
1228 82313 : action = KRB5_SENDTO_TIMEOUT;
1229 3413 : HEIM_FALLTHROUGH;
1230 82317 : case KRB5_SENDTO_TIMEOUT:
1231 :
1232 : /*
1233 : * If we completed, just got to next step
1234 : */
1235 :
1236 82317 : if (ctx->stateflags & KRBHST_COMPLETED) {
1237 0 : action = KRB5_SENDTO_CONTINUE;
1238 0 : break;
1239 : }
1240 :
1241 : /*
1242 : * Pull out next host, if there is no more, close the
1243 : * handle and mark as completed.
1244 : *
1245 : * Collect time spent in krbhst (dns, plugin, etc)
1246 : */
1247 :
1248 :
1249 82317 : gettimeofday(&nrstart, NULL);
1250 :
1251 82317 : ret = krb5_krbhst_next(context, handle, &hi);
1252 :
1253 82317 : gettimeofday(&nrstop, NULL);
1254 82317 : timevalsub(&nrstop, &nrstart);
1255 82317 : timevaladd(&ctx->stats.krbhst, &nrstop);
1256 :
1257 82317 : action = KRB5_SENDTO_CONTINUE;
1258 82317 : if (ret == 0) {
1259 74337 : _krb5_debug(context, 5, "submitting new requests to new host");
1260 74337 : if (submit_request(context, ctx, hi) != 0)
1261 4 : action = KRB5_SENDTO_TIMEOUT;
1262 : } else {
1263 7980 : _krb5_debug(context, 5, "out of hosts, waiting for replies");
1264 7980 : ctx->stateflags |= KRBHST_COMPLETED;
1265 : }
1266 :
1267 78904 : break;
1268 133590 : case KRB5_SENDTO_CONTINUE:
1269 :
1270 133590 : ret = wait_response(context, &action, ctx);
1271 133590 : if (ret)
1272 0 : goto out;
1273 :
1274 128221 : break;
1275 12431 : case KRB5_SENDTO_RESET:
1276 : /* start over */
1277 12431 : _krb5_debug(context, 5,
1278 : "krb5_sendto trying over again (reset): %d",
1279 : numreset);
1280 12431 : reset_context(context, ctx);
1281 12431 : if (handle) {
1282 12431 : krb5_krbhst_free(context, handle);
1283 12431 : handle = NULL;
1284 : }
1285 12431 : numreset++;
1286 12431 : if (numreset >= 3)
1287 0 : action = KRB5_SENDTO_FAILED;
1288 : else
1289 12431 : action = KRB5_SENDTO_KRBHST;
1290 :
1291 11846 : break;
1292 74333 : case KRB5_SENDTO_FILTER:
1293 : /* default to next state, the filter function might modify this */
1294 74333 : action = KRB5_SENDTO_DONE;
1295 :
1296 74333 : if (ctx->func) {
1297 77746 : ret = (*ctx->func)(context, ctx, ctx->data,
1298 74333 : &ctx->response, &action);
1299 74333 : if (ret)
1300 0 : goto out;
1301 :
1302 : /*
1303 : * If we are not done, ask to continue/reset
1304 : */
1305 74333 : switch (action) {
1306 59074 : case KRB5_SENDTO_DONE:
1307 59074 : break;
1308 12431 : case KRB5_SENDTO_RESET:
1309 : case KRB5_SENDTO_CONTINUE:
1310 : /* free response to clear it out so we don't loop */
1311 12431 : krb5_data_free(&ctx->response);
1312 12431 : break;
1313 0 : default:
1314 0 : ret = KRB5_KDC_UNREACH;
1315 0 : krb5_set_error_message(context, ret,
1316 : "sendto filter funcation return unsupported state: %d", (int)action);
1317 0 : goto out;
1318 : }
1319 : }
1320 70920 : break;
1321 7980 : case KRB5_SENDTO_FAILED:
1322 7980 : ret = KRB5_KDC_UNREACH;
1323 7980 : goto out;
1324 78375 : case KRB5_SENDTO_DONE:
1325 78375 : ret = 0;
1326 78375 : goto out;
1327 0 : default:
1328 0 : heim_abort("invalid krb5_sendto_context state");
1329 : }
1330 : }
1331 :
1332 86355 : out:
1333 86355 : gettimeofday(&stop_time, NULL);
1334 86355 : timevalsub(&stop_time, &ctx->stats.start_time);
1335 86355 : if (ret == 0 && ctx->response.length) {
1336 77295 : *receive = ctx->response;
1337 77295 : krb5_data_zero(&ctx->response);
1338 : } else {
1339 9060 : krb5_data_free(&ctx->response);
1340 9060 : krb5_clear_error_message (context);
1341 9060 : ret = KRB5_KDC_UNREACH;
1342 9060 : krb5_set_error_message(context, ret,
1343 9060 : N_("unable to reach any KDC in realm %s", ""),
1344 : realm);
1345 : }
1346 :
1347 86355 : _krb5_debug(context, 1,
1348 : "%s %s done: %d hosts: %lu packets: %lu"
1349 : " wc: %lld.%06lu nr: %lld.%06lu kh: %lld.%06lu tid: %08x",
1350 : __func__, realm, ret,
1351 83527 : ctx->stats.num_hosts, ctx->stats.sent_packets,
1352 86355 : (long long)stop_time.tv_sec,
1353 86355 : (unsigned long)stop_time.tv_usec,
1354 86355 : (long long)ctx->stats.name_resolution.tv_sec,
1355 86355 : (unsigned long)ctx->stats.name_resolution.tv_usec,
1356 86355 : (long long)ctx->stats.krbhst.tv_sec,
1357 86355 : (unsigned long)ctx->stats.krbhst.tv_usec, ctx->stid);
1358 :
1359 :
1360 86355 : if (freectx)
1361 0 : krb5_sendto_ctx_free(context, ctx);
1362 : else
1363 86355 : reset_context(context, ctx);
1364 :
1365 86355 : if (handle)
1366 69882 : krb5_krbhst_free(context, handle);
1367 :
1368 86355 : return ret;
1369 : }
|