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 //*************************************************************************************************************
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()
41 #include "DNSCommon.h"
42 #include "mDNSEmbeddedAPI.h"
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)
54 //*************************************************************************************************************
55 // Remaining work TODO
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."
61 // - Actually use Network Framework
64 //*************************************************************************************************************
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.
71 dso_state_t
*dso_find_by_serial(uint32_t serial
)
75 for (dsop
= dso_connections
; dsop
; dsop
= dsop
->next
) {
76 if (dsop
->serial
== serial
) {
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.
87 void dso_drop(dso_state_t
*dso
)
91 if (dso_connections
== dso
) {
92 dso_connections
= dso
->next
;
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
);
98 dsop
->next
= dso
->next
;
99 // If we get to the end of the list without finding dso, it means that it's already
105 dso
->next
= dso_connections_needing_cleanup
;
106 dso_connections_needing_cleanup
= dso
;
109 int32_t dso_idle(void *context
, int64_t now
, int64_t next_timer_event
)
111 dso_state_t
*dso
, *dnext
;
112 dso_activity_t
*ap
, *anext
;
114 for (dso
= dso_connections_needing_cleanup
; dso
; dso
= dnext
) {
116 // Finalize and then free any activities.
117 for (ap
= dso
->activities
; ap
; ap
= anext
) {
124 if (dso
->transport
!= NULL
&& dso
->transport_finalize
!= NULL
) {
125 dso
->transport_finalize(dso
->transport
);
126 dso
->transport
= NULL
;
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
);
137 dso_connections_needing_cleanup
= NULL
;
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
;
148 } else if (now
- dso
->inactivity_due
> 0 && dso
->cb
!= NULL
) {
149 dso
->cb(dso
->context
, 0, dso
, kDSOEventType_Inactive
);
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
;
161 return dso_transport_idle(context
, now
, next_timer_event
);
164 // Called when something happens that establishes a DSO session.
165 static void dso_session_established(dso_state_t
*dso
)
167 dso
->has_session
= true;
168 // Set up inactivity timer and keepalive timer...
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
)
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
);
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
);
184 dso
->outstanding_queries
= (dso_outstanding_query_state_t
*)(dso
+ 1);
185 dso
->outstanding_queries
->max_outstanding_queries
= max_outstanding_queries
;
187 dso
->remote_name
= ((char *)dso
->outstanding_queries
) + outsize
;
188 memcpy(dso
->remote_name
, remote_name
, namelen
);
189 dso
->remote_name
[namelen
] = 0;
192 dso
->context
= context
;
193 dso
->transport
= transport
;
194 dso
->is_server
= is_server
;
195 dso
->serial
= dso_serial
++;
197 dso
->next
= dso_connections
;
198 dso_connections
= dso
;
203 // Start building a TLV in an outgoing dso message.
204 void dso_start_tlv(dso_message_t
*state
, int opcode
)
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!");
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!");
217 state
->building_tlv
= true;
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;
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
)
232 if (!state
->building_tlv
) {
233 LogMsg("add_tlv_bytes called when not building a TLV!");
236 if (state
->no_copy_bytes_len
) {
237 LogMsg("add_tlv_bytesNoCopy called twice on the same DSO message.");
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
;
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
)
249 if (!state
->building_tlv
) {
250 LogMsg("add_tlv_bytes called when not building a TLV!");
253 if (state
->cur
+ len
> state
->max
) {
254 LogMsg("add_tlv_bytes called with no room in output buffer.");
257 memcpy(&state
->buf
[state
->cur
], bytes
, len
);
259 state
->tlv_len
+= len
;
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
)
265 if (!state
->building_tlv
) {
266 LogMsg("dso_add_tlv_byte called when not building a TLV!");
269 if (state
->cur
+ 1 > state
->max
) {
270 LogMsg("dso_add_tlv_byte called with no room in output buffer.");
273 state
->buf
[state
->cur
++] = byte
;
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
)
280 if (!state
->building_tlv
) {
281 LogMsg("dso_add_tlv_u16 called when not building a TLV!");
284 if ((state
->cur
+ sizeof u16
) > state
->max
) {
285 LogMsg("dso_add_tlv_u16 called with no room in output buffer.");
288 state
->buf
[state
->cur
++] = u16
>> 8;
289 state
->buf
[state
->cur
++] = u16
& 255;
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
)
296 if (!state
->building_tlv
) {
297 LogMsg("dso_add_tlv_u32 called when not building a TLV!");
300 if ((state
->cur
+ sizeof u32
) > state
->max
) {
301 LogMsg("dso_add_tlv_u32 called with no room in output buffer.");
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;
311 // Finish building a TLV.
312 void dso_finish_tlv(dso_message_t
*state
)
314 if (!state
->building_tlv
) {
315 LogMsg("dso_finish_tlv called when not building a TLV!");
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!");
324 state
->buf
[state
->tlv_len_offset
] = state
->tlv_len
>> 8;
325 state
->buf
[state
->tlv_len_offset
+ 1] = state
->tlv_len
& 255;
327 state
->building_tlv
= false;
330 dso_activity_t
*dso_find_activity(dso_state_t
*dso
, const char *name
, const char *activity_type
, void *context
)
332 dso_activity_t
*activity
;
334 // If we haven't been given something to search for, don't search.
335 if (name
== NULL
&& context
== NULL
) {
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
))) {
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
*))
353 size_t namelen
= name
? strlen(name
) + 1 : 0;
355 dso_activity_t
*activity
;
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
);
365 len
= namelen
+ sizeof *activity
;
366 ap
= mDNSPlatformMemAllocateClear((mDNSu32
)len
);
370 activity
= (dso_activity_t
*)ap
;
371 ap
= (char *)ap
+ sizeof *activity
;
373 // Activities can be identified either by name or by context
376 memcpy(activity
->name
, name
, namelen
);
378 activity
->name
= NULL
;
380 activity
->context
= context
;
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
;
387 // Retain this activity on the list.
388 activity
->next
= dso
->activities
;
389 dso
->activities
= activity
;
393 void dso_drop_activity(dso_state_t
*dso
, dso_activity_t
*activity
)
395 dso_activity_t
**app
= &dso
->activities
;
396 bool matched
= false;
398 // Remove this activity from the list.
400 if (*app
== activity
) {
401 *app
= activity
->next
;
404 app
= &((*app
)->next
);
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.
411 LogMsg("dso_drop_activity: FATAL: activity that's not on the list has been dropped!");
415 activity
->finalize(activity
);
419 void dso_ignore_response(dso_state_t
*dso
, void *context
)
421 dso_outstanding_query_state_t
*midState
= dso
->outstanding_queries
;
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
;
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
)
435 DNSMessageHeader
*msg_header
;
436 dso_outstanding_query_state_t
*midState
= dso
->outstanding_queries
;
438 memset(state
, 0, sizeof *state
);
440 state
->max
= outbuf_size
;
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!");
448 // This buffer should be 16-bit aligned.
449 msg_header
= (DNSMessageHeader
*)state
->buf
;
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
;
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!");
461 // Response-requiring messages need to have a message ID.
462 if (!unidirectional
) {
463 bool msg_id_ok
= true;
468 // If we don't have room for another outstanding message, the caller should try
470 if (midState
->outstanding_query_count
== midState
->max_outstanding_queries
) {
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.
476 // This would be a surprising fluke, but let's not get killed by it.
477 if (looping
++ > 1000) {
480 message_id
= mDNSRandom(65536);
482 if (message_id
== 0) {
485 for (i
= 0; i
< midState
->max_outstanding_queries
; i
++) {
486 if (midState
->queries
[i
].id
== 0 && avail
== -1) {
488 } else if (midState
->queries
[i
].id
== message_id
) {
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
;
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!");
505 state
->outstanding_query_number
= -1;
508 state
->cur
= sizeof *msg_header
;
512 size_t dso_message_length(dso_message_t
*state
)
514 return state
->cur
+ state
->no_copy_bytes_len
;
517 void dso_retry_delay(dso_state_t
*dso
, const DNSMessageHeader
*header
)
519 dso_disconnect_context_t context
;
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
);
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
);
533 void dso_keepalive(dso_state_t
*dso
, const DNSMessageHeader
*header
)
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
);
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
) {
547 if (dso
->keepalive_interval
< context
.keepalive_interval
) {
548 context
.keepalive_interval
= dso
->keepalive_interval
;
550 if (dso
->inactivity_timeout
< context
.inactivity_timeout
) {
551 context
.inactivity_timeout
= dso
->inactivity_timeout
;
553 dso
->cb(dso
->context
, &context
, dso
, kDSOEventType_KeepaliveRcvd
);
556 if (dso
->keepalive_interval
> context
.keepalive_interval
) {
557 dso
->keepalive_interval
= context
.keepalive_interval
;
559 if (dso
->inactivity_timeout
> context
.inactivity_timeout
) {
560 dso
->inactivity_timeout
= context
.inactivity_timeout
;
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
)
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
;
574 if (message_length
< 12) {
575 LogMsg("dso_message_received: response too short: %ld bytes", (long)message_length
);
580 // See if we have sent a message for which a response is expected.
582 bool expected
= false;
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
);
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
);
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
;
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
);
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.");
613 // If there were no TLVs, we don't need to parse them.
615 if (message_length
== 12) {
616 dso
->primary
.opcode
= 0;
617 dso
->primary
.length
= 0;
618 dso
->num_additls
= 0;
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.
627 LogMsg("dso_message_received: fatal: %s sent %ld byte message, QR=1", dso
->remote_name
, (long)message_length
);
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"))));
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
);
649 // Short messages are a fatal error. XXX check DSO document
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
);
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
);
667 // Get the primary TLV and count how many TLVs there are in total
669 while (offset
< message_length
) {
670 // Get the TLV opcode
671 int opcode
= (((unsigned)message
[offset
]) << 8) + message
[offset
+ 1];
673 size_t length
= (((unsigned)message
[offset
+ 2]) << 8) + message
[offset
+ 3];
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
);
680 // Short messages are a fatal error. XXX check DSO document
685 // Is this the primary TLV?
687 dso
->primary
.opcode
= opcode
;
688 dso
->primary
.length
= length
;
689 dso
->primary
.payload
= &message
[offset
+ 4];
690 dso
->num_additls
= 0;
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];
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
);
704 offset
+= 4 + length
;
707 // Call the callback with the message or response
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
);
715 dso
->cb(dso
->context
, &qcontext
, dso
, kDSOEventType_DSOResponse
);
717 dso
->cb(dso
->context
, header
, dso
, kDSOEventType_DSOMessage
);
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
)
728 DNSMessageHeader
*header
;
729 int opcode
, response
;
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
;
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
);
741 // Short messages are a fatal error.
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
);
751 dso
->cb(dso
->context
, header
, dso
,
752 response
? kDSOEventType_DNSMessage
: kDSOEventType_DNSResponse
);
755 dso_message_received(dso
, message
, message_length
);
762 // c-file-style: "bsd"
765 // indent-tabs-mode: nil