Skip to content

Commit 51f3f70

Browse files
author
Marko Mikulicic
committed
Implement DNS protocol, fossa-style
1 parent d333c8b commit 51f3f70

File tree

7 files changed

+606
-4
lines changed

7 files changed

+606
-4
lines changed

fossa.c

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3384,3 +3384,190 @@ void ns_mqtt_disconnect(struct ns_connection *nc) {
33843384
}
33853385

33863386
#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 */

fossa.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,3 +808,53 @@ void ns_mqtt_pong(struct ns_connection *);
808808
#endif /* __cplusplus */
809809

810810
#endif /* NS_MQTT_HEADER_INCLUDED */
811+
/*
812+
* Copyright (c) 2014 Cesanta Software Limited
813+
* All rights reserved
814+
*/
815+
816+
#ifndef NS_DNS_HEADER_DEFINED
817+
#define NS_DNS_HEADER_DEFINED
818+
819+
820+
#ifdef __cplusplus
821+
extern "C" {
822+
#endif /* __cplusplus */
823+
824+
#define NS_DNS_A_RECORD 0x01 /* Lookup IP address */
825+
#define NS_DNS_CNAME_RECORD 0x05 /* Lookup CNAME */
826+
#define NS_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */
827+
#define NS_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */
828+
829+
void ns_dns_resolve(const char *, int, ns_event_handler_t);
830+
831+
/* low-level */
832+
#define NS_MAX_DNS_QUESTIONS 32
833+
#define NS_MAX_DNS_ANSWERS 32
834+
835+
struct ns_dns_resource_record {
836+
struct ns_str name; /* buffer with compressed name */
837+
int rtype;
838+
int rclass;
839+
int ttl;
840+
struct ns_str rdata; /* protocol data (can be a compressed name) */
841+
};
842+
843+
struct ns_dns_message {
844+
const char *pkt; /* packet body */
845+
int num_questions;
846+
int num_answers;
847+
struct ns_dns_resource_record questions[NS_MAX_DNS_QUESTIONS];
848+
struct ns_dns_resource_record answers[NS_MAX_DNS_ANSWERS];
849+
};
850+
851+
void ns_send_dns_query(struct ns_connection*, const char *, int);
852+
int ns_parse_dns(const char *, int, struct ns_dns_message *);
853+
854+
size_t ns_dns_uncompress_name(struct ns_dns_message *, struct ns_str *,
855+
char *, int);
856+
857+
#ifdef __cplusplus
858+
}
859+
#endif /* __cplusplus */
860+
#endif /* NS_HTTP_HEADER_DEFINED */

0 commit comments

Comments
 (0)