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