]> git.saurik.com Git - apple/mdnsresponder.git/blob - DSO/dso.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / DSO / dso.c
1 /* dso.c
2 *
3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 //*************************************************************************************************************
20 // Headers
21
22 #include <stdio.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include <netdb.h> // For gethostbyname()
31 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
32 #include <net/if.h> // For IF_NAMESIZE
33 #include <netinet/in.h> // For INADDR_NONE
34 #include <netinet/tcp.h> // For SOL_TCP, TCP_NOTSENT_LOWAT
35 #include <arpa/inet.h> // For inet_addr()
36 #include <unistd.h>
37 #include <errno.h>
38 #include <fcntl.h>
39
40 #include "DNSCommon.h"
41 #include "mDNSEmbeddedAPI.h"
42
43 #include "dso.h"
44
45 #ifdef STANDALONE
46 #undef LogMsg
47 #define LogMsg(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
48 #define mDNSRandom(x) arc4random_uniform(x)
49 #define mDNSPlatformMemAllocateClear(length) calloc(1, length)
50 #endif // STANDALONE
51
52 //*************************************************************************************************************
53 // Remaining work TODO
54
55 // - Add keepalive/inactivity timeout support
56 // - Notice if it takes a long time to get a response when establishing a session, and treat that
57 // as "DSO not supported."
58 // - TLS support
59 // - Actually use Network Framework
60
61
62 //*************************************************************************************************************
63 // Globals
64
65 static dso_state_t *dso_connections;
66 static dso_state_t *dso_connections_needing_cleanup; // DSO connections that have been shut down but aren't yet freed.
67 static uint32_t dso_serial; // Used to uniquely mark DSO objects, incremented once for each dso_state_t created.
68
69 dso_state_t *dso_find_by_serial(uint32_t serial)
70 {
71 dso_state_t *dsop;
72
73 for (dsop = dso_connections; dsop; dsop = dsop->next) {
74 if (dsop->serial == serial) {
75 return dsop;
76 }
77 }
78 return NULL;
79 }
80
81 // This function is called either when an error has occurred requiring the a DSO connection be
82 // dropped, or else when a connection to a DSO endpoint has been cleanly closed and is ready to be
83 // dropped for that reason.
84
85 void dso_drop(dso_state_t *dso)
86 {
87 dso_state_t *dsop;
88
89 if (dso_connections == dso) {
90 dso_connections = dso->next;
91 } else {
92 for (dsop = dso_connections; dsop != NULL && dsop->next != dso; dsop = dsop->next) {
93 LogMsg("dsop = %p dsop->next = %p dso = %p", dsop, dsop->next, dso);
94 }
95 if (dsop) {
96 dsop->next = dso->next;
97 // If we get to the end of the list without finding dso, it means that it's already
98 // been dropped.
99 } else {
100 return;
101 }
102 }
103 dso->next = dso_connections_needing_cleanup;
104 dso_connections_needing_cleanup = dso;
105 }
106
107 int64_t dso_idle(void *context, int64_t now, int64_t next_timer_event)
108 {
109 dso_state_t *dso, *dnext;
110 dso_activity_t *ap, *anext;
111
112 for (dso = dso_connections_needing_cleanup; dso; dso = dnext) {
113 dnext = dso->next;
114 // Finalize and then free any activities.
115 for (ap = dso->activities; ap; ap = anext) {
116 anext = ap->next;
117 if (ap->finalize) {
118 ap->finalize(ap);
119 }
120 free(ap);
121 }
122 if (dso->transport != NULL && dso->transport_finalize != NULL) {
123 dso->transport_finalize(dso->transport);
124 dso->transport = NULL;
125 }
126 if (dso->cb) {
127 dso_disconnect_context_t disconnect_context;
128 memset(&disconnect_context, 0, sizeof disconnect_context);
129 dso->cb(dso->context, &disconnect_context, dso, kDSOEventType_Disconnected);
130 dso->cb(dso->context, NULL, dso, kDSOEventType_Finalize);
131 } else {
132 free(dso);
133 }
134 }
135 dso_connections_needing_cleanup = NULL;
136
137 // Do keepalives.
138 for (dso = dso_connections; dso; dso = dso->next) {
139 if (dso->inactivity_due == 0) {
140 if (dso->inactivity_timeout != 0) {
141 dso->inactivity_due = now + dso->inactivity_timeout;
142 if (next_timer_event - dso->keepalive_due > 0) {
143 next_timer_event = dso->keepalive_due;
144 }
145 }
146 } else if (now - dso->inactivity_due > 0 && dso->cb != NULL) {
147 dso->cb(dso->context, 0, dso, kDSOEventType_Inactive);
148 }
149 if (dso->keepalive_due != 0 && dso->keepalive_due < now && dso->cb != NULL) {
150 dso_keepalive_context_t kc;
151 memset(&kc, 0, sizeof kc);
152 dso->cb(dso->context, &kc, dso, kDSOEventType_Keepalive);
153 dso->keepalive_due = now + dso->keepalive_interval;
154 if (next_timer_event - dso->keepalive_due > 0) {
155 next_timer_event = dso->keepalive_due;
156 }
157 }
158 }
159 return dso_transport_idle(context, now, next_timer_event);
160 }
161
162 // Called when something happens that establishes a DSO session.
163 static void dso_session_established(dso_state_t *dso)
164 {
165 dso->has_session = true;
166 // Set up inactivity timer and keepalive timer...
167 }
168
169 // Create a dso_state_t structure
170 dso_state_t *dso_create(bool is_server, int max_outstanding_queries, const char *remote_name,
171 dso_event_callback_t callback, void *context, dso_transport_t *transport)
172 {
173 dso_state_t *dso;
174 int namelen = strlen(remote_name) + 1;
175 int outsize = (sizeof (dso_outstanding_query_state_t)) + max_outstanding_queries * sizeof (dso_outstanding_query_t);
176
177 // We allocate everything in a single hunk so that we can free it together as well.
178 dso = (dso_state_t *) mDNSPlatformMemAllocateClear((sizeof *dso) + outsize + namelen);
179 if (dso == NULL) {
180 goto out;
181 }
182 dso->outstanding_queries = (dso_outstanding_query_state_t *)(dso + 1);
183 dso->outstanding_queries->max_outstanding_queries = max_outstanding_queries;
184
185 dso->remote_name = ((char *)dso->outstanding_queries) + outsize;
186 memcpy(dso->remote_name, remote_name, namelen);
187 dso->remote_name[namelen] = 0;
188
189 dso->cb = callback;
190 dso->context = context;
191 dso->transport = transport;
192 dso->is_server = is_server;
193 dso->serial = dso_serial++;
194
195 dso->next = dso_connections;
196 dso_connections = dso;
197 out:
198 return dso;
199 }
200
201 // Start building a TLV in an outgoing dso message.
202 void dso_start_tlv(dso_message_t *state, int opcode)
203 {
204 // Make sure there's room for the length and the TLV opcode.
205 if (state->cur + 4 >= state->max) {
206 LogMsg("dso_start_tlv called when no space in output buffer!");
207 assert(0);
208 }
209
210 // We need to not yet have a TLV.
211 if (state->building_tlv) {
212 LogMsg("dso_start_tlv called while already building a TLV!");
213 assert(0);
214 }
215 state->building_tlv = true;
216 state->tlv_len = 0;
217
218 // Set up the TLV header.
219 state->buf[state->cur] = opcode >> 8;
220 state->buf[state->cur + 1] = opcode & 255;
221 state->tlv_len_offset = state->cur + 2;
222 state->cur += 4;
223 }
224
225 // Add some bytes to a TLV that's being built, but don't copy them--just remember the
226 // pointer to the buffer. This is used so that when we have a message to forward, we
227 // don't copy it into the output buffer--we just use scatter/gather I/O.
228 void dso_add_tlv_bytes_no_copy(dso_message_t *state, const uint8_t *bytes, size_t len)
229 {
230 if (!state->building_tlv) {
231 LogMsg("add_tlv_bytes called when not building a TLV!");
232 assert(0);
233 }
234 if (state->no_copy_bytes_len) {
235 LogMsg("add_tlv_bytesNoCopy called twice on the same DSO message.");
236 assert(0);
237 }
238 state->no_copy_bytes_len = len;
239 state->no_copy_bytes = bytes;
240 state->no_copy_bytes_offset = state->cur;
241 state->tlv_len += len;
242 }
243
244 // Add some bytes to a TLV that's being built.
245 void dso_add_tlv_bytes(dso_message_t *state, const uint8_t *bytes, size_t len)
246 {
247 if (!state->building_tlv) {
248 LogMsg("add_tlv_bytes called when not building a TLV!");
249 assert(0);
250 }
251 if (state->cur + len > state->max) {
252 LogMsg("add_tlv_bytes called with no room in output buffer.");
253 assert(0);
254 }
255 memcpy(&state->buf[state->cur], bytes, len);
256 state->cur += len;
257 state->tlv_len += len;
258 }
259
260 // Add a single byte to a TLV that's being built.
261 void dso_add_tlv_byte(dso_message_t *state, uint8_t byte)
262 {
263 if (!state->building_tlv) {
264 LogMsg("dso_add_tlv_byte called when not building a TLV!");
265 assert(0);
266 }
267 if (state->cur + 1 > state->max) {
268 LogMsg("dso_add_tlv_byte called with no room in output buffer.");
269 assert(0);
270 }
271 state->buf[state->cur++] = byte;
272 state->tlv_len++;
273 }
274
275 // Add an uint16_t to a TLV that's being built.
276 void dso_add_tlv_u16(dso_message_t *state, uint16_t u16)
277 {
278 if (!state->building_tlv) {
279 LogMsg("dso_add_tlv_u16 called when not building a TLV!");
280 assert(0);
281 }
282 if ((state->cur + sizeof u16) > state->max) {
283 LogMsg("dso_add_tlv_u16 called with no room in output buffer.");
284 assert(0);
285 }
286 state->buf[state->cur++] = u16 >> 8;
287 state->buf[state->cur++] = u16 & 255;
288 state->tlv_len += 2;
289 }
290
291 // Add an uint32_t to a TLV that's being built.
292 void dso_add_tlv_u32(dso_message_t *state, uint32_t u32)
293 {
294 if (!state->building_tlv) {
295 LogMsg("dso_add_tlv_u32 called when not building a TLV!");
296 assert(0);
297 }
298 if ((state->cur + sizeof u32) > state->max) {
299 LogMsg("dso_add_tlv_u32 called with no room in output buffer.");
300 assert(0);
301 }
302 state->buf[state->cur++] = u32 >> 24;
303 state->buf[state->cur++] = (u32 >> 16) & 255;
304 state->buf[state->cur++] = (u32 >> 8) & 255;
305 state->buf[state->cur++] = u32 & 255;
306 state->tlv_len += 4;
307 }
308
309 // Finish building a TLV.
310 void dso_finish_tlv(dso_message_t *state)
311 {
312 if (!state->building_tlv) {
313 LogMsg("dso_finish_tlv called when not building a TLV!");
314 assert(0);
315 }
316
317 // A TLV can't be longer than this.
318 if (state->tlv_len > 65535) {
319 LogMsg("dso_finish_tlv was given more than 65535 bytes of TLV payload!");
320 assert(0);
321 }
322 state->buf[state->tlv_len_offset] = state->tlv_len >> 8;
323 state->buf[state->tlv_len_offset + 1] = state->tlv_len & 255;
324 state->tlv_len = 0;
325 state->building_tlv = false;
326 }
327
328 dso_activity_t *dso_find_activity(dso_state_t *dso, const char *name, const char *activity_type, void *context)
329 {
330 dso_activity_t *activity;
331
332 // If we haven't been given something to search for, don't search.
333 if (name == NULL && context == NULL) {
334 return NULL;
335 }
336
337 // An activity can be identified by name or context, but if name is present, that's what identifies it.
338 for (activity = dso->activities; activity; activity = activity->next) {
339 if (activity->activity_type == activity_type && ((activity->name == NULL || name == NULL|| !strcmp(activity->name, name)) &&
340 (context == NULL && context == activity->context))) {
341 return activity;
342 }
343 }
344 return NULL;
345 }
346
347 // Make an activity structure to hang off the DSO.
348 dso_activity_t *dso_add_activity(dso_state_t *dso, const char *name, const char *activity_type,
349 void *context, void (*finalize)(dso_activity_t *))
350 {
351 size_t namelen = name ? strlen(name) + 1 : 0;
352 size_t len;
353 dso_activity_t *activity;
354 void *ap;
355
356 // Shouldn't add an activity that's already been added.
357 activity = dso_find_activity(dso, name, activity_type, context);
358 if (activity != NULL) {
359 LogMsg("dso_add_activity: activity %s%s%p added twice.", name ? name : "", name ? " " : "", context);
360 return NULL;
361 }
362
363 len = namelen + sizeof *activity;
364 ap = mDNSPlatformMemAllocateClear(len);
365 if (ap == NULL) {
366 return NULL;
367 }
368 activity = (dso_activity_t *)ap;
369 ap = (char *)ap + sizeof *activity;
370
371 // Activities can be identified either by name or by context
372 if (namelen) {
373 activity->name = ap;
374 memcpy(activity->name, name, namelen);
375 } else {
376 activity->name = NULL;
377 }
378 activity->context = context;
379
380 // Activity type is expected to be a string constant; all activities of the same type must
381 // reference the same constant, not different constants with the same contents.
382 activity->activity_type = activity_type;
383 activity->finalize = finalize;
384
385 // Retain this activity on the list.
386 activity->next = dso->activities;
387 dso->activities = activity;
388 return activity;
389 }
390
391 void dso_drop_activity(dso_state_t *dso, dso_activity_t *activity)
392 {
393 dso_activity_t **app = &dso->activities;
394 bool matched = false;
395
396 // Remove this activity from the list.
397 while (*app) {
398 if (*app == activity) {
399 *app = activity->next;
400 matched = true;
401 } else {
402 app = &((*app)->next);
403 }
404 }
405
406 // If an activity that's not on the DSO list is passed here, it's an internal consistency
407 // error that probably indicates something is corrupted.
408 if (!matched) {
409 LogMsg("dso_drop_activity: FATAL: activity that's not on the list has been dropped!");
410 assert(0);
411 }
412
413 activity->finalize(activity);
414 free(activity);
415 }
416
417 void dso_ignore_response(dso_state_t *dso, void *context)
418 {
419 dso_outstanding_query_state_t *midState = dso->outstanding_queries;
420 int i;
421 for (i = 0; i < midState->max_outstanding_queries; i++) {
422 // The query is still be outstanding, and we want to know it when it comes back, but we forget the context,
423 // which presumably is a reference to something that's going away.
424 if (midState->queries[i].context == context) {
425 midState->queries[i].context = NULL;
426 }
427 }
428 }
429
430 bool dso_make_message(dso_message_t *state, uint8_t *outbuf, size_t outbuf_size,
431 dso_state_t *dso, bool unidirectional, void *callback_state)
432 {
433 DNSMessageHeader *msg_header;
434 dso_outstanding_query_state_t *midState = dso->outstanding_queries;
435
436 memset(state, 0, sizeof *state);
437 state->buf = outbuf;
438 state->max = outbuf_size;
439
440 // We need space for the TCP message length plus the DNS header.
441 if (state->max < sizeof *msg_header) {
442 LogMsg("dso_make_message: called without enough buffer space to store a DNS header!");
443 assert(0);
444 }
445
446 // This buffer should be 16-bit aligned.
447 msg_header = (DNSMessageHeader *)state->buf;
448
449 // The DNS header for a DSO message is mostly zeroes
450 memset(msg_header, 0, sizeof *msg_header);
451 msg_header->flags.b[0] = kDNSFlag0_QR_Query | kDNSFlag0_OP_DSO;
452
453 // Servers can't send DSO messages until there's a DSO session.
454 if (dso->is_server && !dso->has_session) {
455 LogMsg("dso_make_message: FATAL: server attempting to make a DSO message with no session!");
456 assert(0);
457 }
458
459 // Response-requiring messages need to have a message ID.
460 if (!unidirectional) {
461 bool msg_id_ok = true;
462 uint16_t message_id;
463 int looping = 0;
464 int i, avail = -1;
465
466 // If we don't have room for another outstanding message, the caller should try
467 // again later.
468 if (midState->outstanding_query_count == midState->max_outstanding_queries) {
469 return false;
470 }
471 // Generate a random message ID. This doesn't really need to be cryptographically sound
472 // (right?) because we're encrypting the whole data stream in TLS.
473 do {
474 // This would be a surprising fluke, but let's not get killed by it.
475 if (looping++ > 1000) {
476 return false;
477 }
478 message_id = mDNSRandom(65536);
479 msg_id_ok = true;
480 if (message_id == 0) {
481 msg_id_ok = false;
482 } else {
483 for (i = 0; i < midState->max_outstanding_queries; i++) {
484 if (midState->queries[i].id == 0 && avail == -1) {
485 avail = i;
486 } else if (midState->queries[i].id == message_id) {
487 msg_id_ok = false;
488 }
489 }
490 }
491 } while (!msg_id_ok);
492 midState->queries[avail].id = message_id;
493 midState->queries[avail].context = callback_state;
494 midState->outstanding_query_count++;
495 msg_header->id.NotAnInteger = message_id;
496 state->outstanding_query_number = avail;
497 } else {
498 // Clients aren't allowed to send unidirectional messages until there's a session.
499 if (!dso->has_session) {
500 LogMsg("dso_make_message: FATAL: client making a DSO unidirectional message with no session!");
501 assert(0);
502 }
503 state->outstanding_query_number = -1;
504 }
505
506 state->cur = sizeof *msg_header;
507 return true;
508 }
509
510 size_t dso_message_length(dso_message_t *state)
511 {
512 return state->cur + state->no_copy_bytes_len;
513 }
514
515 void dso_retry_delay(dso_state_t *dso, const DNSMessageHeader *header)
516 {
517 dso_disconnect_context_t context;
518 if (dso->cb) {
519 memset(&context, 0, sizeof context);
520 if (dso->primary.length != 4) {
521 LogMsg("Invalid DSO Retry Delay length %d from %s", dso->primary.length, dso->remote_name);
522 dso_send_formerr(dso, header);
523 return;
524 }
525 memcpy(&context, dso->primary.payload, dso->primary.length);
526 context.reconnect_delay = ntohl(context.reconnect_delay);
527 dso->cb(dso->context, &context, dso, kDSOEventType_RetryDelay);
528 }
529 }
530
531 void dso_keepalive(dso_state_t *dso, const DNSMessageHeader *header)
532 {
533 dso_keepalive_context_t context;
534 memset(&context, 0, sizeof context);
535 if (dso->primary.length != 8) {
536 LogMsg("Invalid DSO Keepalive length %d from %s", dso->primary.length, dso->remote_name);
537 dso_send_formerr(dso, header);
538 return;
539 }
540 memcpy(&context, dso->primary.payload, dso->primary.length);
541 context.inactivity_timeout = ntohl(context.inactivity_timeout);
542 context.keepalive_interval = ntohl(context.keepalive_interval);
543 if (dso->is_server) {
544 if (dso->cb) {
545 if (dso->keepalive_interval < context.keepalive_interval) {
546 context.keepalive_interval = dso->keepalive_interval;
547 }
548 if (dso->inactivity_timeout < context.inactivity_timeout) {
549 context.inactivity_timeout = dso->inactivity_timeout;
550 }
551 dso->cb(dso->context, &context, dso, kDSOEventType_KeepaliveRcvd);
552 }
553 } else {
554 if (dso->keepalive_interval > context.keepalive_interval) {
555 dso->keepalive_interval = context.keepalive_interval;
556 }
557 if (dso->inactivity_timeout > context.inactivity_timeout) {
558 dso->inactivity_timeout = context.inactivity_timeout;
559 }
560 }
561 }
562
563 // We received a DSO message; validate it, parse it and, if implemented, dispatch it.
564 void dso_message_received(dso_state_t *dso, const uint8_t *message, size_t message_length)
565 {
566 int i;
567 size_t offset;
568 const DNSMessageHeader *header = (const DNSMessageHeader *)message;
569 int response = (header->flags.b[0] & kDNSFlag0_QR_Mask) == kDNSFlag0_QR_Response;
570 dso_query_receive_context_t qcontext;
571
572 if (message_length < 12) {
573 LogMsg("dso_message_received: response too short: %ld bytes", (long)message_length);
574 dso_drop(dso);
575 goto out;
576 }
577
578 // See if we have sent a message for which a response is expected.
579 if (response) {
580 bool expected = false;
581
582 // A zero ID on a response is not permitted.
583 if (header->id.NotAnInteger == 0) {
584 LogMsg("dso_message_received: response with id==0 received from %s", dso->remote_name);
585 dso_drop(dso);
586 goto out;
587 }
588 // It's possible for a DSO response to contain no TLVs, but if that's the case, the length
589 // should always be twelve.
590 if (message_length < 16 && message_length != 12) {
591 LogMsg("dso_message_received: response with bogus length==%ld received from %s", (long)message_length, dso->remote_name);
592 dso_drop(dso);
593 goto out;
594 }
595 for (i = 0; i < dso->outstanding_queries->max_outstanding_queries; i++) {
596 if (dso->outstanding_queries->queries[i].id == header->id.NotAnInteger) {
597 qcontext.query_context = dso->outstanding_queries->queries[i].context;
598 qcontext.rcode = header->flags.b[1] & kDNSFlag1_RC_Mask;
599
600 // If we are a client, and we just got an acknowledgment, a session has been established.
601 if (!dso->is_server && !dso->has_session && (header->flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr) {
602 dso_session_established(dso);
603 }
604 dso->outstanding_queries->queries[i].id = 0;
605 dso->outstanding_queries->queries[i].context = 0;
606 dso->outstanding_queries->outstanding_query_count--;
607 if (dso->outstanding_queries->outstanding_query_count < 0) {
608 LogMsg("dso_message_receive: programming error: outstanding_query_count went negative.");
609 assert(0);
610 }
611 // If there were no TLVs, we don't need to parse them.
612 expected = true;
613 if (message_length == 12) {
614 dso->primary.opcode = 0;
615 dso->primary.length = 0;
616 dso->num_additls = 0;
617 }
618 break;
619 }
620 }
621
622 // This is fatal because we've received a response to a message we didn't send, so
623 // it's not just that we don't understand what was sent.
624 if (!expected) {
625 LogMsg("dso_message_received: fatal: %s sent %ld byte message, QR=1", dso->remote_name, (long)message_length);
626 dso_drop(dso);
627 goto out;
628 }
629 }
630
631 // Make sure that the DNS header is okay (QDCOUNT, ANCOUNT, NSCOUNT and ARCOUNT are all zero)
632 for (i = 0; i < 4; i++) {
633 if (message[4 + i * 2] != 0 || message[4 + i * 2 + 1] != 0) {
634 LogMsg("dso_message_received: fatal: %s sent %ld byte DSO message, %s is nonzero",
635 dso->remote_name, (long)message_length,
636 (i == 0 ? "QDCOUNT" : (i == 1 ? "ANCOUNT" : ( i == 2 ? "NSCOUNT" : "ARCOUNT"))));
637 dso_drop(dso);
638 goto out;
639 }
640 }
641
642 // Check that there is space for there to be a primary TLV
643 if (message_length < 16 && message_length != 12) {
644 LogMsg("dso_message_received: fatal: %s sent short (%ld byte) DSO message",
645 dso->remote_name, (long)message_length);
646
647 // Short messages are a fatal error. XXX check DSO document
648 dso_drop(dso);
649 goto out;
650 }
651
652 // If we are a server, and we don't have a session, and this is a message, then we have now established a session.
653 if (!dso->has_session && dso->is_server && !response) {
654 dso_session_established(dso);
655 }
656
657 // If a DSO session isn't yet established, make sure the message is a request (if is_server) or a
658 // response (if not).
659 if (!dso->has_session && ((dso->is_server && response) || (!dso->is_server && !response))) {
660 LogMsg("dso_message_received: received a %s with no established session from %s",
661 response ? "response" : "request", dso->remote_name);
662 dso_drop(dso);
663 }
664
665 // Get the primary TLV and count how many TLVs there are in total
666 offset = 12;
667 while (offset < message_length) {
668 // Get the TLV opcode
669 int opcode = (((unsigned)message[offset]) << 8) + message[offset + 1];
670 // And the length
671 size_t length = (((unsigned)message[offset + 2]) << 8) + message[offset + 3];
672
673 // Is there room for the contents of this TLV?
674 if (length + offset > message_length) {
675 LogMsg("dso_message_received: fatal: %s: TLV (%d %ld) extends past end (%ld)",
676 dso->remote_name, opcode, (long)length, (long)message_length);
677
678 // Short messages are a fatal error. XXX check DSO document
679 dso_drop(dso);
680 goto out;
681 }
682
683 // Is this the primary TLV?
684 if (offset == 12) {
685 dso->primary.opcode = opcode;
686 dso->primary.length = length;
687 dso->primary.payload = &message[offset + 4];
688 dso->num_additls = 0;
689 } else {
690 if (dso->num_additls < MAX_ADDITLS) {
691 dso->additl[dso->num_additls].opcode = opcode;
692 dso->additl[dso->num_additls].length = length;
693 dso->additl[dso->num_additls].payload = &message[offset + 4];
694 dso->num_additls++;
695 } else {
696 // XXX MAX_ADDITLS should be enough for all possible additional TLVs, so this
697 // XXX should never happen; if it does, maybe it's a fatal error.
698 LogMsg("dso_message_received: %s: ignoring additional TLV (%d %ld) in excess of %d",
699 dso->remote_name, opcode, (long)length, MAX_ADDITLS);
700 }
701 }
702 offset += 4 + length;
703 }
704
705 // Call the callback with the message or response
706 if (dso->cb) {
707 if (message_length != 12 && dso->primary.opcode == kDSOType_Keepalive) {
708 dso_keepalive(dso, header);
709 } else if (message_length != 12 && dso->primary.opcode == kDSOType_RetryDelay) {
710 dso_retry_delay(dso, header);
711 } else {
712 if (response) {
713 dso->cb(dso->context, &qcontext, dso, kDSOEventType_DSOResponse);
714 } else {
715 dso->cb(dso->context, header, dso, kDSOEventType_DSOMessage);
716 }
717 }
718 }
719 out:
720 ;
721 }
722
723 // This code is currently assuming that we won't get a DNS message, but that's not true. Fix.
724 void dns_message_received(dso_state_t *dso, const uint8_t *message, size_t message_length)
725 {
726 DNSMessageHeader *header;
727 int opcode, response;
728
729 // We can safely assume that the header is 16-bit aligned.
730 header = (DNSMessageHeader *)message;
731 opcode = header->flags.b[0] & kDNSFlag0_OP_Mask;
732 response = (header->flags.b[0] & kDNSFlag0_QR_Mask) == kDNSFlag0_QR_Response;
733
734 // Validate the length of the DNS message.
735 if (message_length < 12) {
736 LogMsg("dns_message_received: fatal: %s sent short (%ld byte) message",
737 dso->remote_name, (long)message_length);
738
739 // Short messages are a fatal error.
740 dso_drop(dso);
741 return;
742 }
743
744 // This is not correct for the general case.
745 if (opcode != kDNSFlag0_OP_DSO) {
746 LogMsg("dns_message_received: %s sent %ld byte %s, QTYPE=%d",
747 dso->remote_name, (long)message_length, (response ? "response" : "request"), opcode);
748 if (dso->cb) {
749 dso->cb(dso->context, header, dso,
750 response ? kDSOEventType_DNSMessage : kDSOEventType_DNSResponse);
751 }
752 } else {
753 dso_message_received(dso, message, message_length);
754 }
755 }
756
757 // Local Variables:
758 // mode: C
759 // tab-width: 4
760 // c-file-style: "bsd"
761 // c-basic-offset: 4
762 // fill-column: 108
763 // indent-tabs-mode: nil
764 // End: