@@ -3384,3 +3384,190 @@ void ns_mqtt_disconnect(struct ns_connection *nc) {
3384
3384
}
3385
3385
3386
3386
#endif /* NS_DISABLE_MQTT */
3387
+ /*
3388
+ * Copyright (c) 2014 Cesanta Software Limited
3389
+ * All rights reserved
3390
+ */
3391
+
3392
+ /*
3393
+ * == DNS API
3394
+ */
3395
+
3396
+ #ifndef NS_DISABLE_DNS
3397
+
3398
+
3399
+ #define MAX_DNS_PACKET_LEN 2048
3400
+
3401
+ static int ns_dns_tid = 0xa0 ;
3402
+
3403
+ struct ns_dns_header {
3404
+ uint16_t transaction_id ;
3405
+ uint16_t flags ;
3406
+ uint16_t num_questions ;
3407
+ uint16_t num_answers ;
3408
+ uint16_t num_authority_prs ;
3409
+ uint16_t num_other_prs ;
3410
+ };
3411
+
3412
+ /*
3413
+ * Low-level: send a dns query to the remote end
3414
+ */
3415
+ void ns_send_dns_query (struct ns_connection * nc , const char * name ,
3416
+ int query_type ) {
3417
+ struct ns_dns_header header ;
3418
+ const char * s ;
3419
+ int n , name_len ;
3420
+ uint16_t num ;
3421
+ struct iobuf pkt ;
3422
+
3423
+ iobuf_init (& pkt , MAX_DNS_PACKET_LEN );
3424
+
3425
+ memset (& header , 0 , sizeof (header ));
3426
+ header .transaction_id = ++ ns_dns_tid ;
3427
+ header .flags = htons (0x100 ); /* recursion allowed */
3428
+ header .num_questions = htons (1 );
3429
+
3430
+ iobuf_append (& pkt , & header , sizeof (header ));
3431
+
3432
+ name_len = strlen (name );
3433
+ do {
3434
+ if ((s = strchr (name , '.' )) == NULL )
3435
+ s = name + name_len ;
3436
+
3437
+ n = s - name ; /* chunk length */
3438
+ iobuf_append (& pkt , & n , 1 ); /* send length */
3439
+ iobuf_append (& pkt , name , n );
3440
+
3441
+ if (* s == '.' )
3442
+ n ++ ;
3443
+
3444
+ name += n ;
3445
+ name_len -= n ;
3446
+ } while (* s != '\0' );
3447
+ iobuf_append (& pkt , "\0" , 1 ); /* Mark end of host name */
3448
+
3449
+ num = htons (query_type );
3450
+ iobuf_append (& pkt , & num , 2 );
3451
+ num = htons (0x0001 ); /* Class: inet */
3452
+ iobuf_append (& pkt , & num , 2 );
3453
+
3454
+ /* TCP DNS requires messages to be prefixed with len */
3455
+ if (!(nc -> flags & NSF_UDP )) {
3456
+ uint16_t len = htons (pkt .len );
3457
+ iobuf_prepend (& pkt , & len , 2 );
3458
+ }
3459
+
3460
+ ns_send (nc , pkt .buf , pkt .len );
3461
+ iobuf_free (& pkt );
3462
+ }
3463
+
3464
+ static unsigned char * ns_parse_dns_resource_record (
3465
+ unsigned char * data , struct ns_dns_resource_record * rr , int reply ) {
3466
+ unsigned char * name = data ;
3467
+ int chunk_len , data_len ;
3468
+
3469
+ while ((chunk_len = * data )) {
3470
+ if (((unsigned char * )data )[0 ] & 0xc0 ) {
3471
+ data += 1 ;
3472
+ break ;
3473
+ }
3474
+ data += chunk_len + 1 ;
3475
+ }
3476
+
3477
+ rr -> name .p = (char * ) name ;
3478
+ rr -> name .len = data - name + 1 ;
3479
+
3480
+ data ++ ;
3481
+
3482
+ rr -> rtype = data [0 ] << 8 | data [1 ];
3483
+ data += 2 ;
3484
+
3485
+ rr -> rclass = data [0 ] << 8 | data [1 ];
3486
+ data += 2 ;
3487
+
3488
+ if (reply ) {
3489
+ rr -> ttl = data [0 ] << 24 | data [1 ] << 16 | data [2 ] << 8 | data [3 ];
3490
+ data += 4 ;
3491
+
3492
+ data_len = * data << 8 | * (data + 1 );
3493
+ data += 2 ;
3494
+
3495
+ rr -> rdata .p = (char * ) data ;
3496
+ rr -> rdata .len = data_len ;
3497
+ data += data_len ;
3498
+ }
3499
+ return data ;
3500
+ }
3501
+
3502
+ /* Low-level: parses a DNS response. */
3503
+ int ns_parse_dns (const char * buf , int len , struct ns_dns_message * msg ) {
3504
+ struct ns_dns_header * header = (struct ns_dns_header * ) buf ;
3505
+ unsigned char * data = (unsigned char * ) buf + sizeof (* header );
3506
+ int i ;
3507
+ msg -> pkt = buf ;
3508
+
3509
+ if (len < (int )sizeof (* header )) {
3510
+ return -1 ;
3511
+ }
3512
+
3513
+ msg -> num_questions = ntohs (header -> num_questions );
3514
+ msg -> num_answers = ntohs (header -> num_answers );
3515
+
3516
+ /* TODO(mkm): check bounds */
3517
+
3518
+ for (i = 0 ; i < msg -> num_questions
3519
+ && i < (int )ARRAY_SIZE (msg -> questions ); i ++ ) {
3520
+ data = ns_parse_dns_resource_record (data , & msg -> questions [i ], 0 );
3521
+ }
3522
+
3523
+ for (i = 0 ; i < msg -> num_answers
3524
+ && i < (int )ARRAY_SIZE (msg -> answers ); i ++ ) {
3525
+ data = ns_parse_dns_resource_record (data , & msg -> answers [i ], 1 );
3526
+ }
3527
+
3528
+ return 0 ;
3529
+ }
3530
+
3531
+ /*
3532
+ * Uncompress a DNS compressed name.
3533
+ *
3534
+ * The containing dns message is required because the compressed encoding
3535
+ * and reference suffixes present elsewhere in the packet.
3536
+ *
3537
+ * If name is less than `dst_len` characters long, the remainder
3538
+ * of `dst` is terminated with `\0' characters. Otherwise, `dst` is not terminated.
3539
+ *
3540
+ * If `dst_len` is 0 `dst` can be NULL.
3541
+ * Returns the uncompressed name length.
3542
+ */
3543
+ size_t ns_dns_uncompress_name (struct ns_dns_message * msg , struct ns_str * name ,
3544
+ char * dst , int dst_len ) {
3545
+ int chunk_len ;
3546
+ char * old_dst = dst ;
3547
+ const unsigned char * data = (unsigned char * ) name -> p ;
3548
+
3549
+ while ((chunk_len = * data ++ )) {
3550
+ int leeway = dst_len - (dst - old_dst );
3551
+ if (chunk_len & 0xc0 ) {
3552
+ uint16_t off = (data [-1 ] & (~0xc0 )) << 8 | data [0 ];
3553
+ data = (unsigned char * )msg -> pkt + off ;
3554
+ continue ;
3555
+ }
3556
+ if (chunk_len > leeway ) {
3557
+ chunk_len = leeway ;
3558
+ }
3559
+
3560
+ memcpy (dst , data , chunk_len );
3561
+ data += chunk_len ;
3562
+ dst += chunk_len ;
3563
+ leeway -= chunk_len ;
3564
+ if (leeway == 0 ) {
3565
+ return dst - old_dst ;
3566
+ }
3567
+ * dst ++ = '.' ;
3568
+ }
3569
+ * -- dst = 0 ;
3570
+ return dst - old_dst ;
3571
+ }
3572
+
3573
+ #endif /* NS_DISABLE_DNS */
0 commit comments