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 72835 : kdccallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
77 : {
78 72835 : const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
79 72835 : struct send_via_plugin_s *ctx = userctx;
80 :
81 72835 : if (service->send_to_kdc == NULL)
82 0 : return KRB5_PLUGIN_NO_HANDLE;
83 72835 : 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 82600 : realmcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
89 : {
90 82600 : const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
91 82600 : struct send_via_plugin_s *ctx = userctx;
92 :
93 82600 : if (service->send_to_realm == NULL)
94 0 : return KRB5_PLUGIN_NO_HANDLE;
95 82600 : 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 73361 : 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 73361 : userctx.realm = NULL;
120 73361 : userctx.hi = hi;
121 73361 : userctx.timeout = timeout;
122 73361 : userctx.send_data = send_data;
123 73361 : userctx.receive = receive;
124 :
125 73361 : return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
126 : &userctx, kdccallback);
127 : }
128 :
129 : static krb5_error_code
130 83011 : 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 83011 : userctx.realm = realm;
139 83011 : userctx.hi = NULL;
140 83011 : userctx.timeout = timeout;
141 83011 : userctx.send_data = send_data;
142 83011 : userctx.receive = receive;
143 :
144 83011 : 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 68614 : dealloc_sendto_ctx(void *ptr)
181 : {
182 68614 : krb5_sendto_ctx ctx = (krb5_sendto_ctx)ptr;
183 68614 : if (ctx->hostname)
184 0 : free(ctx->hostname);
185 68614 : if (ctx->sitename)
186 0 : free(ctx->sitename);
187 68614 : heim_release(ctx->hosts);
188 68614 : heim_release(ctx->krbhst);
189 68614 : }
190 :
191 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
192 68614 : krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
193 : {
194 68614 : *ctx = heim_alloc(sizeof(**ctx), "sendto-context", dealloc_sendto_ctx);
195 68614 : if (*ctx == NULL)
196 0 : return krb5_enomem(context);
197 68614 : (*ctx)->hosts = heim_array_create();
198 :
199 68614 : return 0;
200 : }
201 :
202 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
203 12179 : krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
204 : {
205 12179 : ctx->flags |= flags;
206 12179 : }
207 :
208 : KRB5_LIB_FUNCTION int KRB5_LIB_CALL
209 12179 : krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
210 : {
211 12179 : 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 68614 : krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
222 : krb5_sendto_ctx_func func,
223 : void *data)
224 : {
225 68614 : ctx->func = func;
226 68614 : ctx->data = data;
227 68614 : }
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 68614 : krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
283 : {
284 68614 : heim_release(ctx);
285 68614 : }
286 :
287 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
288 73357 : _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 73357 : if(krb5_rd_error(context, reply, &error))
295 46153 : return 0;
296 :
297 24961 : ret = krb5_error_from_rd_error(context, &error, NULL);
298 24961 : krb5_free_error_contents(context, &error);
299 :
300 24961 : switch(ret) {
301 12179 : case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
302 12179 : if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
303 0 : break;
304 12179 : krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
305 12179 : *action = KRB5_SENDTO_RESET;
306 12179 : break;
307 : }
308 0 : case KRB5KDC_ERR_SVC_UNAVAILABLE:
309 0 : *action = KRB5_SENDTO_RESET;
310 0 : break;
311 : }
312 23791 : 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 *ai;
332 : rk_socket_t fd;
333 : const struct host_fun *fun;
334 : unsigned int tries;
335 : time_t timeout;
336 : krb5_data data;
337 : unsigned int tid;
338 : };
339 :
340 : static void
341 : debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
342 : __attribute__ ((__format__ (__printf__, 4, 5)));
343 :
344 : static void
345 174954 : debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
346 : {
347 174954 : const char *proto = "unknown";
348 6924 : const char *state;
349 6924 : char name[NI_MAXHOST], port[NI_MAXSERV];
350 174954 : char *text = NULL;
351 6924 : va_list ap;
352 6924 : int ret;
353 :
354 174954 : if (!_krb5_have_debug(context, 5))
355 174938 : return;
356 :
357 16 : va_start(ap, fmt);
358 16 : ret = vasprintf(&text, fmt, ap);
359 16 : va_end(ap);
360 16 : if (ret == -1 || text == NULL)
361 0 : return;
362 :
363 16 : if (host->hi->proto == KRB5_KRBHST_HTTP)
364 0 : proto = "http";
365 16 : else if (host->hi->proto == KRB5_KRBHST_TCP)
366 4 : proto = "tcp";
367 12 : else if (host->hi->proto == KRB5_KRBHST_UDP)
368 12 : proto = "udp";
369 :
370 16 : if (getnameinfo(host->ai->ai_addr, host->ai->ai_addrlen,
371 : name, sizeof(name), port, sizeof(port), NI_NUMERICHOST) != 0)
372 0 : name[0] = '\0';
373 :
374 16 : switch (host->state) {
375 4 : case CONNECT: state = "CONNECT"; break;
376 0 : case CONNECTING: state = "CONNECTING"; break;
377 4 : case CONNECTED: state = "CONNECTED"; break;
378 8 : case WAITING_REPLY: state = "WAITING_REPLY"; break;
379 0 : case DEAD: state = "DEAD"; break;
380 0 : default: state = "unknown"; break;
381 : }
382 :
383 16 : _krb5_debug(context, level, "%s: %s %s:%s (%s) state=%s tid: %08x", text,
384 16 : proto, name, port, host->hi->hostname, state, host->tid);
385 16 : free(text);
386 : }
387 :
388 :
389 : static void HEIM_CALLCONV
390 41582 : deallocate_host(void *ptr)
391 : {
392 41582 : struct host *host = ptr;
393 41582 : if (!rk_IS_BAD_SOCKET(host->fd))
394 41582 : rk_closesocket(host->fd);
395 41582 : krb5_data_free(&host->data);
396 41582 : host->ai = NULL;
397 41582 : }
398 :
399 : static void
400 0 : host_dead(krb5_context context, struct host *host, const char *msg)
401 : {
402 0 : debug_host(context, 5, host, "%s", msg);
403 0 : rk_closesocket(host->fd);
404 0 : host->fd = rk_INVALID_SOCKET;
405 0 : host->state = DEAD;
406 0 : }
407 :
408 : static krb5_error_code
409 37977 : send_stream(krb5_context context, struct host *host)
410 : {
411 1658 : ssize_t len;
412 :
413 37977 : len = krb5_net_write(context, &host->fd, host->data.data, host->data.length);
414 :
415 37977 : if (len < 0)
416 0 : return errno;
417 37977 : else if (len < host->data.length) {
418 0 : host->data.length -= len;
419 0 : memmove(host->data.data, ((uint8_t *)host->data.data) + len, host->data.length - len);
420 0 : return -1;
421 : } else {
422 37977 : krb5_data_free(&host->data);
423 37977 : return 0;
424 : }
425 : }
426 :
427 : static krb5_error_code
428 46654 : recv_stream(krb5_context context, struct host *host)
429 : {
430 1950 : krb5_error_code ret;
431 1950 : size_t oldlen;
432 1950 : ssize_t sret;
433 1950 : int nbytes;
434 :
435 46654 : if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
436 0 : return HEIM_NET_CONN_REFUSED;
437 :
438 46654 : if (context->max_msg_size - host->data.length < nbytes) {
439 0 : krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
440 0 : N_("TCP message from KDC too large %d", ""),
441 0 : (int)(host->data.length + nbytes));
442 0 : return KRB5KRB_ERR_FIELD_TOOLONG;
443 : }
444 :
445 46654 : oldlen = host->data.length;
446 :
447 46654 : ret = krb5_data_realloc(&host->data, oldlen + nbytes + 1 /* NUL */);
448 46654 : if (ret)
449 0 : return ret;
450 :
451 46654 : sret = krb5_net_read(context, &host->fd, ((uint8_t *)host->data.data) + oldlen, nbytes);
452 46654 : if (sret <= 0) {
453 0 : ret = errno;
454 0 : return ret;
455 : }
456 46654 : host->data.length = oldlen + sret;
457 : /* zero terminate for http transport */
458 46654 : ((uint8_t *)host->data.data)[host->data.length] = '\0';
459 :
460 46654 : return 0;
461 : }
462 :
463 : /*
464 : *
465 : */
466 :
467 : static void
468 41565 : host_next_timeout(krb5_context context, struct host *host)
469 : {
470 41565 : host->timeout = context->kdc_timeout / host->fun->ntries;
471 41565 : if (host->timeout == 0)
472 0 : host->timeout = 1;
473 :
474 41565 : host->timeout += time(NULL);
475 41565 : }
476 :
477 : /*
478 : * connected host
479 : */
480 :
481 : static void
482 41565 : host_connected(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
483 : {
484 1658 : krb5_error_code ret;
485 :
486 41565 : host->state = CONNECTED;
487 : /*
488 : * Now prepare data to send to host
489 : */
490 41565 : if (ctx->prexmit_func) {
491 0 : krb5_data data;
492 :
493 0 : krb5_data_zero(&data);
494 :
495 0 : ret = ctx->prexmit_func(context, host->hi->proto,
496 : ctx->prexmit_ctx, host->fd, &data);
497 0 : if (ret == 0) {
498 0 : if (data.length == 0) {
499 0 : host_dead(context, host, "prexmit function didn't send data");
500 0 : return;
501 : }
502 0 : ret = host->fun->prepare(context, host, &data);
503 0 : krb5_data_free(&data);
504 : }
505 :
506 : } else {
507 41565 : ret = host->fun->prepare(context, host, ctx->send_data);
508 : }
509 41565 : if (ret)
510 0 : debug_host(context, 5, host, "failed to prexmit/prepare");
511 : }
512 :
513 : /*
514 : * connect host
515 : */
516 :
517 : static void
518 41565 : host_connect(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
519 : {
520 41565 : krb5_krbhst_info *hi = host->hi;
521 41565 : struct addrinfo *ai = host->ai;
522 :
523 41565 : debug_host(context, 5, host, "connecting to host");
524 :
525 41565 : if (connect(host->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
526 : #ifdef HAVE_WINSOCK
527 : if (WSAGetLastError() == WSAEWOULDBLOCK)
528 : errno = EINPROGRESS;
529 : #endif /* HAVE_WINSOCK */
530 0 : if (errno == EINPROGRESS && (hi->proto == KRB5_KRBHST_HTTP || hi->proto == KRB5_KRBHST_TCP)) {
531 0 : debug_host(context, 5, host, "connecting to %d", host->fd);
532 0 : host->state = CONNECTING;
533 : } else {
534 0 : host_dead(context, host, "failed to connect");
535 : }
536 : } else {
537 41565 : host_connected(context, ctx, host);
538 : }
539 :
540 41565 : host_next_timeout(context, host);
541 41565 : }
542 :
543 : /*
544 : * HTTP transport
545 : */
546 :
547 : static krb5_error_code
548 0 : prepare_http(krb5_context context, struct host *host, const krb5_data *data)
549 : {
550 0 : char *str = NULL, *request = NULL;
551 0 : krb5_error_code ret;
552 0 : int len;
553 :
554 0 : heim_assert(host->data.length == 0, "prepare_http called twice");
555 :
556 0 : len = rk_base64_encode(data->data, data->length, &str);
557 0 : if(len < 0)
558 0 : return ENOMEM;
559 :
560 0 : if (context->http_proxy)
561 0 : ret = asprintf(&request, "GET http://%s/%s HTTP/1.0\r\n\r\n", host->hi->hostname, str);
562 : else
563 0 : ret = asprintf(&request, "GET /%s HTTP/1.0\r\n\r\n", str);
564 0 : free(str);
565 0 : if(ret < 0 || request == NULL)
566 0 : return ENOMEM;
567 :
568 0 : host->data.data = request;
569 0 : host->data.length = strlen(request);
570 :
571 0 : return 0;
572 : }
573 :
574 : static krb5_error_code
575 0 : recv_http(krb5_context context, struct host *host, krb5_data *data)
576 : {
577 0 : krb5_error_code ret;
578 0 : unsigned long rep_len;
579 0 : size_t len;
580 0 : char *p;
581 :
582 : /*
583 : * recv_stream returns a NUL terminated stream
584 : */
585 :
586 0 : ret = recv_stream(context, host);
587 0 : if (ret)
588 0 : return ret;
589 :
590 0 : p = strstr(host->data.data, "\r\n\r\n");
591 0 : if (p == NULL)
592 0 : return -1;
593 0 : p += 4;
594 :
595 0 : len = host->data.length - (p - (char *)host->data.data);
596 0 : if (len < 4)
597 0 : return -1;
598 :
599 0 : _krb5_get_int(p, &rep_len, 4);
600 0 : if (len < rep_len)
601 0 : return -1;
602 :
603 0 : p += 4;
604 :
605 0 : memmove(host->data.data, p, rep_len);
606 0 : host->data.length = rep_len;
607 :
608 0 : *data = host->data;
609 0 : krb5_data_zero(&host->data);
610 :
611 0 : return 0;
612 : }
613 :
614 : /*
615 : * TCP transport
616 : */
617 :
618 : static krb5_error_code
619 37977 : prepare_tcp(krb5_context context, struct host *host, const krb5_data *data)
620 : {
621 1658 : krb5_error_code ret;
622 1658 : krb5_storage *sp;
623 :
624 37977 : heim_assert(host->data.length == 0, "prepare_tcp called twice");
625 :
626 37977 : sp = krb5_storage_emem();
627 37977 : if (sp == NULL)
628 0 : return ENOMEM;
629 :
630 37977 : ret = krb5_store_data(sp, *data);
631 37977 : if (ret) {
632 0 : krb5_storage_free(sp);
633 0 : return ret;
634 : }
635 37977 : ret = krb5_storage_to_data(sp, &host->data);
636 37977 : krb5_storage_free(sp);
637 :
638 37977 : return ret;
639 : }
640 :
641 : static krb5_error_code
642 46654 : recv_tcp(krb5_context context, struct host *host, krb5_data *data)
643 : {
644 1950 : krb5_error_code ret;
645 1950 : unsigned long pktlen;
646 :
647 46654 : ret = recv_stream(context, host);
648 46654 : if (ret)
649 0 : return ret;
650 :
651 46654 : if (host->data.length < 4)
652 0 : return -1;
653 :
654 46654 : _krb5_get_int(host->data.data, &pktlen, 4);
655 :
656 46654 : if (pktlen > host->data.length - 4)
657 8385 : return -1;
658 :
659 37977 : memmove(host->data.data, ((uint8_t *)host->data.data) + 4, host->data.length - 4);
660 37977 : host->data.length -= 4;
661 :
662 37977 : *data = host->data;
663 37977 : krb5_data_zero(&host->data);
664 :
665 37977 : return 0;
666 : }
667 :
668 : /*
669 : * UDP transport
670 : */
671 :
672 : static krb5_error_code
673 3588 : prepare_udp(krb5_context context, struct host *host, const krb5_data *data)
674 : {
675 3588 : return krb5_data_copy(&host->data, data->data, data->length);
676 : }
677 :
678 : static krb5_error_code
679 3588 : send_udp(krb5_context context, struct host *host)
680 : {
681 3588 : if (send(host->fd, host->data.data, host->data.length, 0) < 0)
682 0 : return errno;
683 3588 : return 0;
684 : }
685 :
686 : static krb5_error_code
687 3588 : recv_udp(krb5_context context, struct host *host, krb5_data *data)
688 : {
689 0 : krb5_error_code ret;
690 0 : int nbytes;
691 :
692 :
693 3588 : if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
694 0 : return HEIM_NET_CONN_REFUSED;
695 :
696 3588 : if (context->max_msg_size < nbytes) {
697 0 : krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
698 0 : N_("UDP message from KDC too large %d", ""),
699 : (int)nbytes);
700 0 : return KRB5KRB_ERR_FIELD_TOOLONG;
701 : }
702 :
703 3588 : ret = krb5_data_alloc(data, nbytes);
704 3588 : if (ret)
705 0 : return ret;
706 :
707 3588 : ret = recv(host->fd, data->data, data->length, 0);
708 3588 : if (ret < 0) {
709 0 : ret = errno;
710 0 : krb5_data_free(data);
711 0 : return ret;
712 : }
713 3588 : data->length = ret;
714 :
715 3588 : return 0;
716 : }
717 :
718 : static const struct host_fun http_fun = {
719 : prepare_http,
720 : send_stream,
721 : recv_http,
722 : 1
723 : };
724 : static const struct host_fun tcp_fun = {
725 : prepare_tcp,
726 : send_stream,
727 : recv_tcp,
728 : 1
729 : };
730 : static const struct host_fun udp_fun = {
731 : prepare_udp,
732 : send_udp,
733 : recv_udp,
734 : 3
735 : };
736 :
737 :
738 : /*
739 : * Host state machine
740 : */
741 :
742 : static int
743 91825 : eval_host_state(krb5_context context,
744 : krb5_sendto_ctx ctx,
745 : struct host *host,
746 : int readable, int writeable)
747 : {
748 3608 : krb5_error_code ret;
749 :
750 91825 : if (host->state == CONNECT) {
751 : /* check if its this host time to connect */
752 18 : if (host->timeout < time(NULL))
753 0 : host_connect(context, ctx, host);
754 18 : return 0;
755 : }
756 :
757 91807 : if (host->state == CONNECTING && writeable)
758 0 : host_connected(context, ctx, host);
759 :
760 91807 : if (readable) {
761 :
762 50242 : debug_host(context, 5, host, "reading packet");
763 :
764 50242 : ret = host->fun->recv_fn(context, host, &ctx->response);
765 50242 : if (ret == -1) {
766 : /* not done yet */
767 41565 : } else if (ret == 0) {
768 : /* if recv_foo function returns 0, we have a complete reply */
769 41565 : debug_host(context, 5, host, "host completed");
770 41565 : return 1;
771 : } else {
772 0 : host_dead(context, host, "host disconnected");
773 : }
774 : }
775 :
776 : /* check if there is anything to send, state might DEAD after read */
777 50242 : if (writeable && host->state == CONNECTED) {
778 :
779 41565 : ctx->stats.sent_packets++;
780 :
781 41565 : debug_host(context, 5, host, "writing packet");
782 :
783 41565 : ret = host->fun->send_fn(context, host);
784 41565 : if (ret == -1) {
785 : /* not done yet */
786 41565 : } else if (ret) {
787 0 : host_dead(context, host, "host dead, write failed");
788 : } else
789 41565 : host->state = WAITING_REPLY;
790 : }
791 :
792 48292 : return 0;
793 : }
794 :
795 : /*
796 : *
797 : */
798 :
799 : static krb5_error_code
800 73361 : submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi)
801 : {
802 73361 : unsigned long submitted_host = 0;
803 73361 : krb5_boolean freeai = FALSE;
804 3413 : struct timeval nrstart, nrstop;
805 3413 : krb5_error_code ret;
806 73361 : struct addrinfo *ai = NULL, *a;
807 3413 : struct host *host;
808 :
809 73361 : ret = kdc_via_plugin(context, hi, context->kdc_timeout,
810 : ctx->send_data, &ctx->response);
811 73361 : if (ret == 0) {
812 30037 : return 0;
813 41569 : } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
814 4 : _krb5_debug(context, 5, "send via plugin failed %s: %d",
815 4 : hi->hostname, ret);
816 4 : return ret;
817 : }
818 :
819 : /*
820 : * If we have a proxy, let use the address of the proxy instead of
821 : * the KDC and let the proxy deal with the resolving of the KDC.
822 : */
823 :
824 41565 : gettimeofday(&nrstart, NULL);
825 :
826 41565 : if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
827 0 : char *proxy2 = strdup(context->http_proxy);
828 0 : char *el, *proxy = proxy2;
829 0 : struct addrinfo hints;
830 0 : char portstr[NI_MAXSERV];
831 0 : unsigned short nport;
832 :
833 0 : if (proxy == NULL)
834 0 : return ENOMEM;
835 0 : if (strncmp(proxy, "http://", 7) == 0)
836 0 : proxy += 7;
837 :
838 : /* check for url terminating slash */
839 0 : el = strchr(proxy, '/');
840 0 : if (el != NULL)
841 0 : *el = '\0';
842 :
843 : /* check for port in hostname, used below as port */
844 0 : el = strchr(proxy, ':');
845 0 : if(el != NULL)
846 0 : *el++ = '\0';
847 :
848 0 : memset(&hints, 0, sizeof(hints));
849 0 : hints.ai_family = PF_UNSPEC;
850 0 : hints.ai_socktype = SOCK_STREAM;
851 :
852 : /* On some systems ntohs(foo(..., htons(...))) causes shadowing */
853 0 : nport = init_port(el, htons(80));
854 0 : snprintf(portstr, sizeof(portstr), "%d", ntohs(nport));
855 :
856 0 : ret = getaddrinfo(proxy, portstr, &hints, &ai);
857 0 : free(proxy2);
858 0 : if (ret)
859 0 : return krb5_eai_to_heim_errno(ret, errno);
860 :
861 0 : freeai = TRUE;
862 :
863 : } else {
864 41565 : ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
865 41565 : if (ret)
866 0 : return ret;
867 : }
868 :
869 : /* add up times */
870 41565 : gettimeofday(&nrstop, NULL);
871 41565 : timevalsub(&nrstop, &nrstart);
872 41565 : timevaladd(&ctx->stats.name_resolution, &nrstop);
873 :
874 41565 : ctx->stats.num_hosts++;
875 :
876 83147 : for (a = ai; a != NULL; a = a->ai_next) {
877 1658 : rk_socket_t fd;
878 :
879 41582 : fd = socket(a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
880 41582 : if (rk_IS_BAD_SOCKET(fd))
881 0 : continue;
882 41582 : rk_cloexec(fd);
883 :
884 : #ifndef NO_LIMIT_FD_SETSIZE
885 41582 : if (fd >= FD_SETSIZE) {
886 0 : _krb5_debug(context, 0, "fd too large for select");
887 0 : rk_closesocket(fd);
888 0 : continue;
889 : }
890 : #endif
891 41582 : socket_set_nonblocking(fd, 1);
892 :
893 41582 : host = heim_alloc(sizeof(*host), "sendto-host", deallocate_host);
894 41582 : if (host == NULL) {
895 0 : if (freeai)
896 0 : freeaddrinfo(ai);
897 0 : rk_closesocket(fd);
898 0 : return ENOMEM;
899 : }
900 41582 : host->hi = hi;
901 41582 : host->fd = fd;
902 41582 : host->ai = a;
903 : /* next version of stid */
904 41582 : host->tid = ctx->stid = (ctx->stid & 0xffff0000) | ((ctx->stid & 0xffff) + 1);
905 :
906 41582 : host->state = CONNECT;
907 :
908 41582 : switch (host->hi->proto) {
909 0 : case KRB5_KRBHST_HTTP :
910 0 : host->fun = &http_fun;
911 0 : break;
912 37988 : case KRB5_KRBHST_TCP :
913 37988 : host->fun = &tcp_fun;
914 37988 : break;
915 3594 : case KRB5_KRBHST_UDP :
916 3594 : host->fun = &udp_fun;
917 3594 : break;
918 0 : default:
919 0 : heim_abort("undefined http transport protocol: %d", (int)host->hi->proto);
920 : }
921 :
922 41582 : host->tries = host->fun->ntries;
923 :
924 : /*
925 : * Connect directly next host, wait a host_timeout for each next address.
926 : * We try host_connect() here, checking the return code because as we do
927 : * non-blocking connects, any error here indicates that the address is just
928 : * offline. That is, it's something like "No route to host" which is not
929 : * worth retrying. And so, we fail directly and immediately to the next
930 : * address for this host without enqueueing the address for retries.
931 : */
932 41582 : if (submitted_host == 0) {
933 41565 : host_connect(context, ctx, host);
934 41565 : if (host->state == DEAD)
935 0 : continue;
936 : } else {
937 17 : debug_host(context, 5, host,
938 : "Queuing host in future (in %ds), its the %lu address on the same name",
939 17 : (int)(context->host_timeout * submitted_host), submitted_host + 1);
940 17 : host->timeout = time(NULL) + (submitted_host * context->host_timeout);
941 : }
942 :
943 41582 : heim_array_append_value(ctx->hosts, host);
944 41582 : heim_release(host);
945 41582 : submitted_host++;
946 : }
947 :
948 41565 : if (freeai)
949 0 : freeaddrinfo(ai);
950 :
951 41565 : if (submitted_host == 0)
952 0 : return KRB5_KDC_UNREACH;
953 :
954 39907 : return 0;
955 : }
956 :
957 : struct wait_ctx {
958 : krb5_context context;
959 : krb5_sendto_ctx ctx;
960 : fd_set rfds;
961 : fd_set wfds;
962 : rk_socket_t max_fd;
963 : int got_reply;
964 : time_t timenow;
965 : };
966 :
967 : static void
968 91842 : wait_setup(heim_object_t obj, void *iter_ctx, int *stop)
969 : {
970 91842 : struct wait_ctx *wait_ctx = iter_ctx;
971 91842 : struct host *h = (struct host *)obj;
972 :
973 91842 : if (h->state == CONNECT) {
974 35 : if (h->timeout >= wait_ctx->timenow)
975 35 : return;
976 0 : host_connect(wait_ctx->context, wait_ctx->ctx, h);
977 : }
978 :
979 : /* skip dead hosts */
980 91807 : if (h->state == DEAD)
981 0 : return;
982 :
983 : /* if host timed out, dec tries and (retry or kill host) */
984 91807 : if (h->timeout < wait_ctx->timenow) {
985 0 : heim_assert(h->tries != 0, "tries should not reach 0");
986 0 : h->tries--;
987 0 : if (h->tries == 0) {
988 0 : host_dead(wait_ctx->context, h, "host timed out");
989 0 : return;
990 : } else {
991 0 : debug_host(wait_ctx->context, 5, h, "retrying sending to");
992 0 : host_next_timeout(wait_ctx->context, h);
993 0 : host_connected(wait_ctx->context, wait_ctx->ctx, h);
994 : }
995 : }
996 :
997 : #ifndef NO_LIMIT_FD_SETSIZE
998 91807 : heim_assert(h->fd < FD_SETSIZE, "fd too large");
999 : #endif
1000 91807 : switch (h->state) {
1001 50242 : case WAITING_REPLY:
1002 50242 : FD_SET(h->fd, &wait_ctx->rfds);
1003 50242 : break;
1004 41565 : case CONNECTING:
1005 : case CONNECTED:
1006 41565 : FD_SET(h->fd, &wait_ctx->rfds);
1007 41565 : FD_SET(h->fd, &wait_ctx->wfds);
1008 41565 : break;
1009 0 : default:
1010 0 : debug_host(wait_ctx->context, 5, h, "invalid sendto host state");
1011 0 : heim_abort("invalid sendto host state");
1012 : }
1013 91807 : if (h->fd > wait_ctx->max_fd || wait_ctx->max_fd == rk_INVALID_SOCKET)
1014 91807 : wait_ctx->max_fd = h->fd;
1015 : }
1016 :
1017 : static int
1018 91842 : wait_filter_dead(heim_object_t obj, void *ctx)
1019 : {
1020 91842 : struct host *h = (struct host *)obj;
1021 91842 : return (int)((h->state == DEAD) ? true : false);
1022 : }
1023 :
1024 : static void
1025 0 : wait_accelerate(heim_object_t obj, void *ctx, int *stop)
1026 : {
1027 0 : struct host *h = (struct host *)obj;
1028 :
1029 0 : if (h->state == CONNECT && h->timeout > 0)
1030 0 : h->timeout--;
1031 0 : }
1032 :
1033 : static void
1034 91825 : wait_process(heim_object_t obj, void *ctx, int *stop)
1035 : {
1036 91825 : struct wait_ctx *wait_ctx = ctx;
1037 91825 : struct host *h = (struct host *)obj;
1038 3608 : int readable, writeable;
1039 91825 : heim_assert(h->state != DEAD, "dead host resurected");
1040 :
1041 : #ifndef NO_LIMIT_FD_SETSIZE
1042 91825 : heim_assert(h->fd < FD_SETSIZE, "fd too large");
1043 : #endif
1044 91825 : readable = FD_ISSET(h->fd, &wait_ctx->rfds);
1045 91825 : writeable = FD_ISSET(h->fd, &wait_ctx->wfds);
1046 :
1047 91825 : if (readable || writeable || h->state == CONNECT)
1048 91825 : wait_ctx->got_reply |= eval_host_state(wait_ctx->context, wait_ctx->ctx, h, readable, writeable);
1049 :
1050 : /* if there is already a reply, just fall though the array */
1051 91825 : if (wait_ctx->got_reply)
1052 41565 : *stop = 1;
1053 91825 : }
1054 :
1055 : static krb5_error_code
1056 131143 : wait_response(krb5_context context, int *action, krb5_sendto_ctx ctx)
1057 : {
1058 5363 : struct wait_ctx wait_ctx;
1059 5363 : struct timeval tv;
1060 5363 : int ret;
1061 :
1062 131143 : wait_ctx.context = context;
1063 131143 : wait_ctx.ctx = ctx;
1064 2229431 : FD_ZERO(&wait_ctx.rfds);
1065 2229431 : FD_ZERO(&wait_ctx.wfds);
1066 131143 : wait_ctx.max_fd = rk_INVALID_SOCKET;
1067 :
1068 : /* oh, we have a reply, it must be a plugin that got it for us */
1069 131143 : if (ctx->response.length) {
1070 31792 : *action = KRB5_SENDTO_FILTER;
1071 31792 : return 0;
1072 : }
1073 :
1074 99351 : wait_ctx.timenow = time(NULL);
1075 :
1076 99351 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_setup);
1077 99351 : heim_array_filter_f(ctx->hosts, &wait_ctx, wait_filter_dead);
1078 :
1079 99351 : if (heim_array_get_length(ctx->hosts) == 0) {
1080 7544 : if (ctx->stateflags & KRBHST_COMPLETED) {
1081 7544 : _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
1082 : "trying to pulling more hosts");
1083 7544 : *action = KRB5_SENDTO_FAILED;
1084 : } else {
1085 0 : _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
1086 : "and no more hosts -> failure");
1087 0 : *action = KRB5_SENDTO_TIMEOUT;
1088 : }
1089 7544 : return 0;
1090 : }
1091 :
1092 91807 : if (wait_ctx.max_fd == rk_INVALID_SOCKET) {
1093 : /*
1094 : * If we don't find a host which can make progress, then
1095 : * we accelerate the process by moving all of the contestants
1096 : * up by 1s.
1097 : */
1098 0 : _krb5_debug(context, 5, "wait_response: moving the contestants forward");
1099 0 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_accelerate);
1100 0 : return 0;
1101 : }
1102 :
1103 91807 : tv.tv_sec = 1;
1104 91807 : tv.tv_usec = 0;
1105 :
1106 91807 : ret = select(wait_ctx.max_fd + 1, &wait_ctx.rfds, &wait_ctx.wfds, NULL, &tv);
1107 91807 : if (ret < 0)
1108 0 : return errno;
1109 91807 : if (ret == 0) {
1110 0 : *action = KRB5_SENDTO_TIMEOUT;
1111 0 : return 0;
1112 : }
1113 :
1114 91807 : wait_ctx.got_reply = 0;
1115 91807 : heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_process);
1116 91807 : if (wait_ctx.got_reply)
1117 41565 : *action = KRB5_SENDTO_FILTER;
1118 : else
1119 50242 : *action = KRB5_SENDTO_CONTINUE;
1120 :
1121 88199 : return 0;
1122 : }
1123 :
1124 : static void
1125 95190 : reset_context(krb5_context context, krb5_sendto_ctx ctx)
1126 : {
1127 95190 : krb5_data_free(&ctx->response);
1128 95190 : heim_release(ctx->hosts);
1129 95190 : ctx->hosts = heim_array_create();
1130 95190 : ctx->stateflags = 0;
1131 95190 : }
1132 :
1133 :
1134 : /*
1135 : *
1136 : */
1137 :
1138 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1139 83011 : krb5_sendto_context(krb5_context context,
1140 : krb5_sendto_ctx ctx,
1141 : const krb5_data *send_data,
1142 : krb5_const_realm realm,
1143 : krb5_data *receive)
1144 : {
1145 83011 : krb5_error_code ret = 0;
1146 83011 : krb5_krbhst_handle handle = NULL;
1147 2828 : struct timeval nrstart, nrstop, stop_time;
1148 83011 : int type, freectx = 0;
1149 2828 : int action;
1150 83011 : int numreset = 0;
1151 :
1152 83011 : krb5_data_zero(receive);
1153 :
1154 83011 : if (ctx == NULL) {
1155 0 : ret = krb5_sendto_ctx_alloc(context, &ctx);
1156 0 : if (ret)
1157 0 : goto out;
1158 0 : freectx = 1;
1159 : }
1160 :
1161 83011 : ctx->stid = (context->num_kdc_requests++) << 16;
1162 :
1163 83011 : memset(&ctx->stats, 0, sizeof(ctx->stats));
1164 83011 : gettimeofday(&ctx->stats.start_time, NULL);
1165 :
1166 83011 : type = ctx->type;
1167 83011 : if (type == 0) {
1168 83011 : if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
1169 0 : type = KRB5_KRBHST_ADMIN;
1170 : else
1171 83011 : type = KRB5_KRBHST_KDC;
1172 : }
1173 :
1174 83011 : ctx->send_data = send_data;
1175 :
1176 83011 : if ((int)send_data->length > context->large_msg_size)
1177 46454 : ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
1178 :
1179 : /* loop until we get back a appropriate response */
1180 :
1181 83011 : action = KRB5_SENDTO_INITIAL;
1182 :
1183 311873 : while (1) {
1184 15602 : krb5_krbhst_info *hi;
1185 :
1186 394884 : switch (action) {
1187 83011 : case KRB5_SENDTO_INITIAL:
1188 85839 : ret = realm_via_plugin(context, realm, context->kdc_timeout,
1189 83011 : send_data, &ctx->response);
1190 83011 : if (ret == 0 || ret != KRB5_PLUGIN_NO_HANDLE) {
1191 14289 : action = KRB5_SENDTO_DONE;
1192 14289 : break;
1193 : }
1194 68722 : action = KRB5_SENDTO_KRBHST;
1195 3413 : HEIM_FALLTHROUGH;
1196 80901 : case KRB5_SENDTO_KRBHST:
1197 80901 : if (ctx->krbhst == NULL) {
1198 80901 : ret = krb5_krbhst_init_flags(context, realm, type,
1199 77488 : ctx->flags, &handle);
1200 80901 : if (ret)
1201 83011 : goto out;
1202 :
1203 80901 : if (ctx->hostname) {
1204 0 : ret = krb5_krbhst_set_hostname(context, handle, ctx->hostname);
1205 0 : if (ret)
1206 0 : goto out;
1207 : }
1208 80901 : if (ctx->sitename) {
1209 0 : ret = krb5_krbhst_set_sitename(context, handle, ctx->sitename);
1210 0 : if (ret)
1211 0 : goto out;
1212 : }
1213 : } else {
1214 0 : handle = heim_retain(ctx->krbhst);
1215 : }
1216 80901 : action = KRB5_SENDTO_TIMEOUT;
1217 3413 : HEIM_FALLTHROUGH;
1218 80905 : case KRB5_SENDTO_TIMEOUT:
1219 :
1220 : /*
1221 : * If we completed, just got to next step
1222 : */
1223 :
1224 80905 : if (ctx->stateflags & KRBHST_COMPLETED) {
1225 0 : action = KRB5_SENDTO_CONTINUE;
1226 0 : break;
1227 : }
1228 :
1229 : /*
1230 : * Pull out next host, if there is no more, close the
1231 : * handle and mark as completed.
1232 : *
1233 : * Collect time spent in krbhst (dns, plugin, etc)
1234 : */
1235 :
1236 :
1237 80905 : gettimeofday(&nrstart, NULL);
1238 :
1239 80905 : ret = krb5_krbhst_next(context, handle, &hi);
1240 :
1241 80905 : gettimeofday(&nrstop, NULL);
1242 80905 : timevalsub(&nrstop, &nrstart);
1243 80905 : timevaladd(&ctx->stats.krbhst, &nrstop);
1244 :
1245 80905 : action = KRB5_SENDTO_CONTINUE;
1246 80905 : if (ret == 0) {
1247 73361 : _krb5_debug(context, 5, "submitting new requests to new host");
1248 73361 : if (submit_request(context, ctx, hi) != 0)
1249 4 : action = KRB5_SENDTO_TIMEOUT;
1250 : } else {
1251 7544 : _krb5_debug(context, 5, "out of hosts, waiting for replies");
1252 7544 : ctx->stateflags |= KRBHST_COMPLETED;
1253 : }
1254 :
1255 77492 : break;
1256 131143 : case KRB5_SENDTO_CONTINUE:
1257 :
1258 131143 : ret = wait_response(context, &action, ctx);
1259 131143 : if (ret)
1260 0 : goto out;
1261 :
1262 125780 : break;
1263 12179 : case KRB5_SENDTO_RESET:
1264 : /* start over */
1265 12179 : _krb5_debug(context, 5,
1266 : "krb5_sendto trying over again (reset): %d",
1267 : numreset);
1268 12179 : reset_context(context, ctx);
1269 12179 : if (handle) {
1270 12179 : krb5_krbhst_free(context, handle);
1271 12179 : handle = NULL;
1272 : }
1273 12179 : numreset++;
1274 12179 : if (numreset >= 3)
1275 0 : action = KRB5_SENDTO_FAILED;
1276 : else
1277 12179 : action = KRB5_SENDTO_KRBHST;
1278 :
1279 11594 : break;
1280 73357 : case KRB5_SENDTO_FILTER:
1281 : /* default to next state, the filter function might modify this */
1282 73357 : action = KRB5_SENDTO_DONE;
1283 :
1284 73357 : if (ctx->func) {
1285 76770 : ret = (*ctx->func)(context, ctx, ctx->data,
1286 73357 : &ctx->response, &action);
1287 73357 : if (ret)
1288 0 : goto out;
1289 :
1290 : /*
1291 : * If we are not done, ask to continue/reset
1292 : */
1293 73357 : switch (action) {
1294 58350 : case KRB5_SENDTO_DONE:
1295 58350 : break;
1296 12179 : case KRB5_SENDTO_RESET:
1297 : case KRB5_SENDTO_CONTINUE:
1298 : /* free response to clear it out so we don't loop */
1299 12179 : krb5_data_free(&ctx->response);
1300 12179 : break;
1301 0 : default:
1302 0 : ret = KRB5_KDC_UNREACH;
1303 0 : krb5_set_error_message(context, ret,
1304 : "sendto filter funcation return unsupported state: %d", (int)action);
1305 0 : goto out;
1306 : }
1307 : }
1308 69944 : break;
1309 7544 : case KRB5_SENDTO_FAILED:
1310 7544 : ret = KRB5_KDC_UNREACH;
1311 7544 : goto out;
1312 75467 : case KRB5_SENDTO_DONE:
1313 75467 : ret = 0;
1314 75467 : goto out;
1315 0 : default:
1316 0 : heim_abort("invalid krb5_sendto_context state");
1317 : }
1318 : }
1319 :
1320 83011 : out:
1321 83011 : gettimeofday(&stop_time, NULL);
1322 83011 : timevalsub(&stop_time, &ctx->stats.start_time);
1323 83011 : if (ret == 0 && ctx->response.length) {
1324 74927 : *receive = ctx->response;
1325 74927 : krb5_data_zero(&ctx->response);
1326 : } else {
1327 8084 : krb5_data_free(&ctx->response);
1328 8084 : krb5_clear_error_message (context);
1329 8084 : ret = KRB5_KDC_UNREACH;
1330 8084 : krb5_set_error_message(context, ret,
1331 8084 : N_("unable to reach any KDC in realm %s", ""),
1332 : realm);
1333 : }
1334 :
1335 83011 : _krb5_debug(context, 1,
1336 : "%s %s done: %d hosts: %lu packets: %lu"
1337 : " wc: %lld.%06lu nr: %lld.%06lu kh: %lld.%06lu tid: %08x",
1338 : __func__, realm, ret,
1339 80183 : ctx->stats.num_hosts, ctx->stats.sent_packets,
1340 83011 : (long long)stop_time.tv_sec,
1341 83011 : (unsigned long)stop_time.tv_usec,
1342 83011 : (long long)ctx->stats.name_resolution.tv_sec,
1343 83011 : (unsigned long)ctx->stats.name_resolution.tv_usec,
1344 83011 : (long long)ctx->stats.krbhst.tv_sec,
1345 83011 : (unsigned long)ctx->stats.krbhst.tv_usec, ctx->stid);
1346 :
1347 :
1348 83011 : if (freectx)
1349 0 : krb5_sendto_ctx_free(context, ctx);
1350 : else
1351 83011 : reset_context(context, ctx);
1352 :
1353 83011 : if (handle)
1354 68722 : krb5_krbhst_free(context, handle);
1355 :
1356 83011 : return ret;
1357 : }
|