3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 //*************************************************************************************************************
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()
40 #include "DNSCommon.h"
41 #include "mDNSEmbeddedAPI.h"
47 #define LogMsg(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
48 #define mDNSRandom(x) arc4random_uniform(x)
49 #define mDNSPlatformMemAllocateClear(length) calloc(1, length)
52 //*************************************************************************************************************
53 // Remaining work TODO
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."
59 // - Actually use Network Framework
62 //*************************************************************************************************************
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.
69 dso_state_t
*dso_find_by_serial(uint32_t serial
)
73 for (dsop
= dso_connections
; dsop
; dsop
= dsop
->next
) {
74 if (dsop
->serial
== serial
) {
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.
85 void dso_drop(dso_state_t
*dso
)
89 if (dso_connections
== dso
) {
90 dso_connections
= dso
->next
;
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
);
96 dsop
->next
= dso
->next
;
97 // If we get to the end of the list without finding dso, it means that it's already
103 dso
->next
= dso_connections_needing_cleanup
;
104 dso_connections_needing_cleanup
= dso
;
107 int64_t dso_idle(void *context
, int64_t now
, int64_t next_timer_event
)
109 dso_state_t
*dso
, *dnext
;
110 dso_activity_t
*ap
, *anext
;
112 for (dso
= dso_connections_needing_cleanup
; dso
; dso
= dnext
) {
114 // Finalize and then free any activities.
115 for (ap
= dso
->activities
; ap
; ap
= anext
) {
122 if (dso
->transport
!= NULL
&& dso
->transport_finalize
!= NULL
) {
123 dso
->transport_finalize(dso
->transport
);
124 dso
->transport
= NULL
;
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
);
135 dso_connections_needing_cleanup
= NULL
;
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
;
146 } else if (now
- dso
->inactivity_due
> 0 && dso
->cb
!= NULL
) {
147 dso
->cb(dso
->context
, 0, dso
, kDSOEventType_Inactive
);
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
;
159 return dso_transport_idle(context
, now
, next_timer_event
);
162 // Called when something happens that establishes a DSO session.
163 static void dso_session_established(dso_state_t
*dso
)
165 dso
->has_session
= true;
166 // Set up inactivity timer and keepalive timer...
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
)
174 int namelen
= strlen(remote_name
) + 1;
175 int outsize
= (sizeof (dso_outstanding_query_state_t
)) + max_outstanding_queries
* sizeof (dso_outstanding_query_t
);
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
);
182 dso
->outstanding_queries
= (dso_outstanding_query_state_t
*)(dso
+ 1);
183 dso
->outstanding_queries
->max_outstanding_queries
= max_outstanding_queries
;
185 dso
->remote_name
= ((char *)dso
->outstanding_queries
) + outsize
;
186 memcpy(dso
->remote_name
, remote_name
, namelen
);
187 dso
->remote_name
[namelen
] = 0;
190 dso
->context
= context
;
191 dso
->transport
= transport
;
192 dso
->is_server
= is_server
;
193 dso
->serial
= dso_serial
++;
195 dso
->next
= dso_connections
;
196 dso_connections
= dso
;
201 // Start building a TLV in an outgoing dso message.
202 void dso_start_tlv(dso_message_t
*state
, int opcode
)
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!");
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!");
215 state
->building_tlv
= true;
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;
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
)
230 if (!state
->building_tlv
) {
231 LogMsg("add_tlv_bytes called when not building a TLV!");
234 if (state
->no_copy_bytes_len
) {
235 LogMsg("add_tlv_bytesNoCopy called twice on the same DSO message.");
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
;
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
)
247 if (!state
->building_tlv
) {
248 LogMsg("add_tlv_bytes called when not building a TLV!");
251 if (state
->cur
+ len
> state
->max
) {
252 LogMsg("add_tlv_bytes called with no room in output buffer.");
255 memcpy(&state
->buf
[state
->cur
], bytes
, len
);
257 state
->tlv_len
+= len
;
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
)
263 if (!state
->building_tlv
) {
264 LogMsg("dso_add_tlv_byte called when not building a TLV!");
267 if (state
->cur
+ 1 > state
->max
) {
268 LogMsg("dso_add_tlv_byte called with no room in output buffer.");
271 state
->buf
[state
->cur
++] = byte
;
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
)
278 if (!state
->building_tlv
) {
279 LogMsg("dso_add_tlv_u16 called when not building a TLV!");
282 if ((state
->cur
+ sizeof u16
) > state
->max
) {
283 LogMsg("dso_add_tlv_u16 called with no room in output buffer.");
286 state
->buf
[state
->cur
++] = u16
>> 8;
287 state
->buf
[state
->cur
++] = u16
& 255;
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
)
294 if (!state
->building_tlv
) {
295 LogMsg("dso_add_tlv_u32 called when not building a TLV!");
298 if ((state
->cur
+ sizeof u32
) > state
->max
) {
299 LogMsg("dso_add_tlv_u32 called with no room in output buffer.");
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;
309 // Finish building a TLV.
310 void dso_finish_tlv(dso_message_t
*state
)
312 if (!state
->building_tlv
) {
313 LogMsg("dso_finish_tlv called when not building a TLV!");
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!");
322 state
->buf
[state
->tlv_len_offset
] = state
->tlv_len
>> 8;
323 state
->buf
[state
->tlv_len_offset
+ 1] = state
->tlv_len
& 255;
325 state
->building_tlv
= false;
328 dso_activity_t
*dso_find_activity(dso_state_t
*dso
, const char *name
, const char *activity_type
, void *context
)
330 dso_activity_t
*activity
;
332 // If we haven't been given something to search for, don't search.
333 if (name
== NULL
&& context
== NULL
) {
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
))) {
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
*))
351 size_t namelen
= name
? strlen(name
) + 1 : 0;
353 dso_activity_t
*activity
;
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
);
363 len
= namelen
+ sizeof *activity
;
364 ap
= mDNSPlatformMemAllocateClear(len
);
368 activity
= (dso_activity_t
*)ap
;
369 ap
= (char *)ap
+ sizeof *activity
;
371 // Activities can be identified either by name or by context
374 memcpy(activity
->name
, name
, namelen
);
376 activity
->name
= NULL
;
378 activity
->context
= context
;
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
;
385 // Retain this activity on the list.
386 activity
->next
= dso
->activities
;
387 dso
->activities
= activity
;
391 void dso_drop_activity(dso_state_t
*dso
, dso_activity_t
*activity
)
393 dso_activity_t
**app
= &dso
->activities
;
394 bool matched
= false;
396 // Remove this activity from the list.
398 if (*app
== activity
) {
399 *app
= activity
->next
;
402 app
= &((*app
)->next
);
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.
409 LogMsg("dso_drop_activity: FATAL: activity that's not on the list has been dropped!");
413 activity
->finalize(activity
);
417 void dso_ignore_response(dso_state_t
*dso
, void *context
)
419 dso_outstanding_query_state_t
*midState
= dso
->outstanding_queries
;
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
;
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
)
433 DNSMessageHeader
*msg_header
;
434 dso_outstanding_query_state_t
*midState
= dso
->outstanding_queries
;
436 memset(state
, 0, sizeof *state
);
438 state
->max
= outbuf_size
;
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!");
446 // This buffer should be 16-bit aligned.
447 msg_header
= (DNSMessageHeader
*)state
->buf
;
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
;
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!");
459 // Response-requiring messages need to have a message ID.
460 if (!unidirectional
) {
461 bool msg_id_ok
= true;
466 // If we don't have room for another outstanding message, the caller should try
468 if (midState
->outstanding_query_count
== midState
->max_outstanding_queries
) {
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.
474 // This would be a surprising fluke, but let's not get killed by it.
475 if (looping
++ > 1000) {
478 message_id
= mDNSRandom(65536);
480 if (message_id
== 0) {
483 for (i
= 0; i
< midState
->max_outstanding_queries
; i
++) {
484 if (midState
->queries
[i
].id
== 0 && avail
== -1) {
486 } else if (midState
->queries
[i
].id
== message_id
) {
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
;
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!");
503 state
->outstanding_query_number
= -1;
506 state
->cur
= sizeof *msg_header
;
510 size_t dso_message_length(dso_message_t
*state
)
512 return state
->cur
+ state
->no_copy_bytes_len
;
515 void dso_retry_delay(dso_state_t
*dso
, const DNSMessageHeader
*header
)
517 dso_disconnect_context_t context
;
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
);
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
);
531 void dso_keepalive(dso_state_t
*dso
, const DNSMessageHeader
*header
)
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
);
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
) {
545 if (dso
->keepalive_interval
< context
.keepalive_interval
) {
546 context
.keepalive_interval
= dso
->keepalive_interval
;
548 if (dso
->inactivity_timeout
< context
.inactivity_timeout
) {
549 context
.inactivity_timeout
= dso
->inactivity_timeout
;
551 dso
->cb(dso
->context
, &context
, dso
, kDSOEventType_KeepaliveRcvd
);
554 if (dso
->keepalive_interval
> context
.keepalive_interval
) {
555 dso
->keepalive_interval
= context
.keepalive_interval
;
557 if (dso
->inactivity_timeout
> context
.inactivity_timeout
) {
558 dso
->inactivity_timeout
= context
.inactivity_timeout
;
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
)
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
;
572 if (message_length
< 12) {
573 LogMsg("dso_message_received: response too short: %ld bytes", (long)message_length
);
578 // See if we have sent a message for which a response is expected.
580 bool expected
= false;
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
);
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
);
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
;
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
);
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.");
611 // If there were no TLVs, we don't need to parse them.
613 if (message_length
== 12) {
614 dso
->primary
.opcode
= 0;
615 dso
->primary
.length
= 0;
616 dso
->num_additls
= 0;
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.
625 LogMsg("dso_message_received: fatal: %s sent %ld byte message, QR=1", dso
->remote_name
, (long)message_length
);
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"))));
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
);
647 // Short messages are a fatal error. XXX check DSO document
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
);
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
);
665 // Get the primary TLV and count how many TLVs there are in total
667 while (offset
< message_length
) {
668 // Get the TLV opcode
669 int opcode
= (((unsigned)message
[offset
]) << 8) + message
[offset
+ 1];
671 size_t length
= (((unsigned)message
[offset
+ 2]) << 8) + message
[offset
+ 3];
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
);
678 // Short messages are a fatal error. XXX check DSO document
683 // Is this the primary TLV?
685 dso
->primary
.opcode
= opcode
;
686 dso
->primary
.length
= length
;
687 dso
->primary
.payload
= &message
[offset
+ 4];
688 dso
->num_additls
= 0;
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];
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
);
702 offset
+= 4 + length
;
705 // Call the callback with the message or response
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
);
713 dso
->cb(dso
->context
, &qcontext
, dso
, kDSOEventType_DSOResponse
);
715 dso
->cb(dso
->context
, header
, dso
, kDSOEventType_DSOMessage
);
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
)
726 DNSMessageHeader
*header
;
727 int opcode
, response
;
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
;
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
);
739 // Short messages are a fatal error.
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
);
749 dso
->cb(dso
->context
, header
, dso
,
750 response
? kDSOEventType_DNSMessage
: kDSOEventType_DNSResponse
);
753 dso_message_received(dso
, message
, message_length
);
760 // c-file-style: "bsd"
763 // indent-tabs-mode: nil