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