| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072 | /* * tinysvcmdns - a tiny MDNS implementation for publishing services * Copyright (C) 2011 Darell Tan * All rights reserved. *  * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "mdns.h"#include <stdint.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <assert.h>#ifdef _WIN32#include <winsock.h>#include <in6addr.h>#else#include <netinet/in.h>#endifstruct name_comp {	uint8_t *label;	// label	size_t pos;		// position in msg	struct name_comp *next;};// ----- label functions -----// duplicates a name//inline uint8_t *dup_nlabel(const uint8_t *n) {uint8_t *dup_nlabel(const uint8_t *n) {	assert(n[0] <= 63);	// prevent mis-use	return (uint8_t *) strdup((char *) n);}// duplicates a labeluint8_t *dup_label(const uint8_t *label) {	int len = *label + 1;	uint8_t *newlabel;	if (len > 63)		return NULL;	newlabel = malloc(len + 1);	strncpy((char *) newlabel, (char *) label, len);	newlabel[len] = '\0';	return newlabel;}uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) {	int len1, len2;	uint8_t *s;	assert(n1[0] <= 63 && n2[0] <= 63);	// detect misuse	len1 = strlen((char *) n1);	len2 = strlen((char *) n2);	s = malloc(len1 + len2 + 1);	strncpy((char *) s, (char *) n1, len1);	strncpy((char *) s+len1, (char *) n2, len2);	s[len1 + len2] = '\0';	return s;}// returns a human-readable name label in dotted formchar *nlabel_to_str(const uint8_t *name) {	char *label, *labelp;	const uint8_t *p;	size_t buf_len = 256;	assert(name != NULL);	label = labelp = malloc(buf_len);	for (p = name; *p; p++) {		uint8_t label_len = *p;		if (buf_len <= label_len)			break;		strncpy(labelp, (char *)p + 1, label_len);		labelp += label_len;		*labelp = '.';		labelp++;		buf_len -= label_len + 1;		p += label_len;	}	// avoid writing NULL past end of buffer	if (buf_len == 0)		labelp--;	*labelp = '\0';	return label;}// returns the length of a label field// does NOT uncompress the field, so it could be as small as 2 bytes// or 1 for the rootstatic size_t label_len(uint8_t *pkt_buf, size_t pkt_len, size_t off) {	uint8_t *p;	uint8_t *e = pkt_buf + pkt_len;	size_t len = 0;	for (p = pkt_buf + off; p < e; p++) {		if (*p == 0) {			return len + 1;		} else if ((*p & 0xC0) == 0xC0) {			return len + 2;		} else {			len += *p + 1;			p += *p;		}	}	return len;}// creates a label// free() after useuint8_t *create_label(const char *txt) {	int len;	uint8_t *s;	assert(txt != NULL);	len = strlen(txt);	if (len > 63)		return NULL;	s = malloc(len + 2);	s[0] = len;	strncpy((char *) s + 1, txt, len);	s[len + 1] = '\0';	return s;}// creates a uncompressed name label given a DNS name like "apple.b.com"// free() after useuint8_t *create_nlabel(const char *name) {	char *label;	char *p, *e, *lenpos;	int len = 0;	assert(name != NULL);	len = strlen(name);	label = malloc(len + 1 + 1);	if (label == NULL)		return NULL;	strncpy((char *) label + 1, name, len);	label[len + 1] = '\0';	p = label;	e = p + len;	lenpos = p;	while (p < e) {		char *dot = memchr(p + 1, '.', e - p - 1);		*lenpos = 0;		if (dot == NULL)			dot = e + 1;		*lenpos = dot - p - 1;		p = dot;		lenpos = dot;	}	return (uint8_t *) label;}// copies a label from the buffer into a newly-allocated string// free() after usestatic uint8_t *copy_label(uint8_t *pkt_buf, size_t pkt_len, size_t off) {	int len;	if (off > pkt_len)		return NULL;		len = pkt_buf[off] + 1;	if (off + len > pkt_len) {		DEBUG_PRINTF("label length exceeds packet buffer\n");		return NULL;	}	return dup_label(pkt_buf + off);}// uncompresses a name// free() after usestatic uint8_t *uncompress_nlabel(uint8_t *pkt_buf, size_t pkt_len, size_t off) {	uint8_t *p;	uint8_t *e = pkt_buf + pkt_len;	size_t len = 0;	char *str, *sp;	if (off >= pkt_len)		return NULL;	// calculate length of uncompressed label	for (p = pkt_buf + off; *p && p < e; p++) {		size_t llen = 0;		if ((*p & 0xC0) == 0xC0) {			uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);			llen = *p2 + 1;			p = p2 + llen - 1;		} else {			llen = *p + 1;			p += llen - 1;		}		len += llen;	}	str = sp = malloc(len + 1);	if (str == NULL)		return NULL;	// FIXME: must merge this with above code	for (p = pkt_buf + off; *p && p < e; p++) {		size_t llen = 0;		if ((*p & 0xC0) == 0xC0) {			uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);			llen = *p2 + 1;			strncpy(sp, (char *) p2, llen);			p = p2 + llen - 1;		} else {			llen = *p + 1;			strncpy(sp, (char *) p, llen);			p += llen - 1;		}		sp += llen;	}	*sp = '\0';	return (uint8_t *) str;}// ----- RR list & group functions -----const char *rr_get_type_name(enum rr_type type) {	switch (type) {		case RR_A:		return "A";		case RR_PTR:	return "PTR";		case RR_TXT:	return "TXT";		case RR_AAAA:	return "AAAA";		case RR_SRV:	return "SRV";		case RR_NSEC:	return "NSEC";		case RR_ANY:	return "ANY";	}	return NULL;}void rr_entry_destroy(struct rr_entry *rr) {	struct rr_data_txt *txt_rec;	assert(rr);	// check rr_type and free data elements	switch (rr->type) {		case RR_PTR:			if (rr->data.PTR.name)				free(rr->data.PTR.name);			// don't free entry			break;		case RR_TXT:			txt_rec = &rr->data.TXT;			while (txt_rec) {				struct rr_data_txt *next = txt_rec->next;				if (txt_rec->txt)					free(txt_rec->txt);				// only free() if it wasn't part of the struct				if (txt_rec != &rr->data.TXT)					free(txt_rec);				txt_rec = next;			}			break;		case RR_SRV:			if (rr->data.SRV.target)				free(rr->data.SRV.target);			break;		case RR_AAAA:			if (rr->data.AAAA.addr)				free(rr->data.AAAA.addr);        	break;		default:			// nothing to free			break;	}	free(rr->name);	free(rr);}// destroys an RR list (and optionally, items)void rr_list_destroy(struct rr_list *rr, char destroy_items) {	struct rr_list *rr_next;	for (; rr; rr = rr_next) {		rr_next = rr->next;		if (destroy_items)			rr_entry_destroy(rr->e);		free(rr);	}}int rr_list_count(struct rr_list *rr) {	int i = 0;	for (; rr; i++, rr = rr->next);	return i;}struct rr_entry *rr_entry_remove(struct rr_group *group, struct rr_entry *entry, enum rr_type type) {	struct rr_group *g;	for (g = group; g; g = g->next) {		struct rr_list *lrr = g->rr, *prr= NULL;		for (; lrr; lrr = lrr->next) {			if (lrr->e->type == type) {				switch (type) {				case RR_PTR:					if (lrr->e->data.PTR.entry == entry) {						struct rr_entry *e = lrr->e;						if (prr == NULL) {							g->rr = lrr->next;						} else {							prr->next = lrr->next;						}						free(lrr);						return e;					}					break;				default:					break;				}			}			prr = lrr;		}	}	return NULL;}struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) {	struct rr_list *le = *rr_head, *pe = NULL;	for (; le; le = le->next) {		if (le->e == rr) {			if (pe == NULL) {				*rr_head = le->next;				free(le);				return rr;			} else {				pe->next = le->next;				free(le);				return rr;			}		}		pe = le;	}	return NULL;}void rr_group_clean(struct rr_group **head) {	struct rr_group *le = *head, *pe = NULL;	while (le) {		if (le->rr == NULL) {			free(le->name);			if (pe == NULL) {				*head = le->next;				 free(le);				 le = *head;			 } else {				pe->next = le->next;				free(le);				le = pe->next;			 }		} else {			pe = le;			le = le->next;        }	}}// appends an rr_entry to an RR list// if the RR is already in the list, it will not be added// RRs are compared by memory location - not its contents// return value of 0 means item not addedint rr_list_append(struct rr_list **rr_head, struct rr_entry *rr) {	struct rr_list *node = malloc(sizeof(struct rr_list));	node->e = rr;	node->next = NULL;	if (*rr_head == NULL) {		*rr_head = node;	} else {		struct rr_list *e = *rr_head, *taile = NULL;		for (; e; e = e->next) {			// already in list - don't add			if (e->e == rr) {				free(node);				return 0;			}			if (e->next == NULL)				taile = e;		}		taile->next = node;	}	return 1;}#define FILL_RR_ENTRY(rr, _name, _type)	\	rr->name = _name;			\	rr->type = _type;			\	rr->ttl  = DEFAULT_TTL;		\	rr->cache_flush = 1;		\	rr->rr_class  = 1;struct rr_entry *rr_create_a(uint8_t *name, struct in_addr addr) {	DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);	FILL_RR_ENTRY(rr, name, RR_A);	rr->data.A.addr = addr.s_addr;	rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10	return rr;}struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr) {	DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);	FILL_RR_ENTRY(rr, name, RR_AAAA);	rr->data.AAAA.addr = addr;	rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10	return rr;}struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target) {	DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);	FILL_RR_ENTRY(rr, name, RR_SRV);	rr->data.SRV.port = port;	rr->data.SRV.target = target;	rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10	return rr;}struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr) {	DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);	FILL_RR_ENTRY(rr, name, RR_PTR);	rr->cache_flush = 0;	// PTRs shouldn't have their cache flush bit set	rr->data.PTR.entry = d_rr;	rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10	return rr;}struct rr_entry *rr_create(uint8_t *name, enum rr_type type) {	DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);	FILL_RR_ENTRY(rr, name, type);	return rr;}void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type) {	assert(rr_nsec->type == RR_NSEC);	assert((type / 8) < sizeof(rr_nsec->data.NSEC.bitmap));	rr_nsec->data.NSEC.bitmap[ type / 8 ] = 1 << (7 - (type % 8));}void rr_add_txt(struct rr_entry *rr_txt, const char *txt) {	struct rr_data_txt *txt_rec;	assert(rr_txt->type == RR_TXT);	txt_rec = &rr_txt->data.TXT;	// is current data filled?	if (txt_rec->txt == NULL) {		txt_rec->txt = create_label(txt);		return;	}	// find the last node	for (; txt_rec->next; txt_rec = txt_rec->next);	// create a new empty node	txt_rec->next = malloc(sizeof(struct rr_data_txt));	txt_rec = txt_rec->next;	txt_rec->txt = create_label(txt);	txt_rec->next = NULL;}// adds a record to an rr_groupvoid rr_group_add(struct rr_group **group, struct rr_entry *rr) {	struct rr_group *g;	assert(rr != NULL);	if (*group) {		g = rr_group_find(*group, rr->name);		if (g) {			rr_list_append(&g->rr, rr);			return;		}	}	MALLOC_ZERO_STRUCT(g, rr_group);	g->name = dup_nlabel(rr->name);	rr_list_append(&g->rr, rr);	// prepend to list	g->next = *group;	*group = g;}// finds a rr_group matching the given namestruct rr_group *rr_group_find(struct rr_group* g, uint8_t *name) {	for (; g; g = g->next) {		if (cmp_nlabel(g->name, name) == 0)			return g;	}	return NULL;}struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type) {	struct rr_list *rr = rr_list;	for (; rr; rr = rr->next) {		if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0) 			return rr->e;	}	return NULL;}// looks for a matching entry in rr_list// if entry is a PTR, we need to check if the PTR target also matchesstruct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry) {	struct rr_list *rr = rr_list;	for (; rr; rr = rr->next) {		if (rr->e->type == entry->type && cmp_nlabel(rr->e->name, entry->name) == 0) {			if (entry->type != RR_PTR) {				return rr->e;			} else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry), MDNS_RR_GET_PTR_NAME(rr->e)) == 0) {				// if it's a PTR, we need to make sure PTR target also matches				return rr->e;			}		}	}	return NULL;}void rr_group_destroy(struct rr_group *group) {	struct rr_group *g = group;	while (g) {		struct rr_group *nextg = g->next;		free(g->name);		rr_list_destroy(g->rr, 1);		free(g);		g = nextg;	}}uint8_t *mdns_write_u16(uint8_t *ptr, const uint16_t v) {	*ptr++ = (uint8_t) (v >> 8) & 0xFF;	*ptr++ = (uint8_t) (v >> 0) & 0xFF;	return ptr;}uint8_t *mdns_write_u32(uint8_t *ptr, const uint32_t v) {	*ptr++ = (uint8_t) (v >> 24) & 0xFF;	*ptr++ = (uint8_t) (v >> 16) & 0xFF;	*ptr++ = (uint8_t) (v >>  8) & 0xFF;	*ptr++ = (uint8_t) (v >>  0) & 0xFF;	return ptr;}uint16_t mdns_read_u16(const uint8_t *ptr) {	return  ((ptr[0] & 0xFF) << 8) | 			((ptr[1] & 0xFF) << 0);}uint32_t mdns_read_u32(const uint8_t *ptr) {	return  ((ptr[0] & 0xFF) << 24) | 			((ptr[1] & 0xFF) << 16) | 			((ptr[2] & 0xFF) <<  8) | 			((ptr[3] & 0xFF) <<  0);}// initialize the packet for reply// clears the packet of list structures but not its list itemsvoid mdns_init_reply(struct mdns_pkt *pkt, uint16_t id) {	// broadcast by default	pkt->unicast = 0;	// copy transaction ID	pkt->id = id;	// response flags	pkt->flags = MDNS_FLAG_RESP | MDNS_FLAG_AA;	rr_list_destroy(pkt->rr_qn,   0);	rr_list_destroy(pkt->rr_ans,  0);	rr_list_destroy(pkt->rr_auth, 0);	rr_list_destroy(pkt->rr_add,  0);	pkt->rr_qn    = NULL;	pkt->rr_ans   = NULL;	pkt->rr_auth  = NULL;	pkt->rr_add   = NULL;	pkt->num_qn = 0;	pkt->num_ans_rr = 0;	pkt->num_auth_rr = 0;	pkt->num_add_rr = 0;}// destroys an mdns_pkt struct, including its contentsvoid mdns_pkt_destroy(struct mdns_pkt *p) {	rr_list_destroy(p->rr_qn, 1);	rr_list_destroy(p->rr_ans, 1);	rr_list_destroy(p->rr_auth, 1);	rr_list_destroy(p->rr_add, 1);	free(p);}// parse the MDNS questions section// stores the parsed data in the given mdns_pkt structstatic size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off, 		struct mdns_pkt *pkt) {	const uint8_t *p = pkt_buf + off;	struct rr_entry *rr;	uint8_t *name;   	assert(pkt != NULL);	rr = malloc(sizeof(struct rr_entry)); 	memset(rr, 0, sizeof(struct rr_entry));	name = uncompress_nlabel(pkt_buf, pkt_len, off);	p += label_len(pkt_buf, pkt_len, off);	rr->name = name;	rr->type = mdns_read_u16(p);	p += sizeof(uint16_t);	rr->unicast_query = (*p & 0x80) == 0x80;	rr->rr_class = mdns_read_u16(p) & ~0x80;	p += sizeof(uint16_t);	rr_list_append(&pkt->rr_qn, rr);		return p - (pkt_buf + off);}// parse the MDNS RR section// stores the parsed data in the given mdns_pkt structstatic size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, 		struct mdns_pkt *pkt) {	const uint8_t *p = pkt_buf + off;	const uint8_t *e = pkt_buf + pkt_len;	struct rr_entry *rr;	uint8_t *name;	size_t rr_data_len = 0;	struct rr_data_txt *txt_rec;	int parse_error = 0;	assert(pkt != NULL);	if (off > pkt_len)		return 0;	rr = malloc(sizeof(struct rr_entry)); 	memset(rr, 0, sizeof(struct rr_entry));	name = uncompress_nlabel(pkt_buf, pkt_len, off);	p += label_len(pkt_buf, pkt_len, off);	rr->name = name;	rr->type = mdns_read_u16(p);	p += sizeof(uint16_t);	rr->cache_flush = (*p & 0x80) == 0x80;	rr->rr_class = mdns_read_u16(p) & ~0x80;	p += sizeof(uint16_t);	rr->ttl = mdns_read_u32(p);	p += sizeof(uint32_t);	// RR data	rr_data_len = mdns_read_u16(p);	p += sizeof(uint16_t);	if (p + rr_data_len > e) {		DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %zu > %zu\n", rr_data_len, e - p);		rr_entry_destroy(rr);		return 0;	}	e = p + rr_data_len;	// see if we can parse the RR data	switch (rr->type) {		case RR_A:			if (rr_data_len < sizeof(uint32_t)) {				DEBUG_PRINTF("invalid rr_data_len=%zu for A record\n", rr_data_len);				parse_error = 1;				break;			}			rr->data.A.addr = ntohl(mdns_read_u32(p)); /* addr already in net order */			p += sizeof(uint32_t);			break;		case RR_AAAA: {			int i;			if (rr_data_len < sizeof(struct in6_addr)) {				DEBUG_PRINTF("invalid rr_data_len=%zu for AAAA record\n", rr_data_len);				parse_error = 1;				break;			}			rr->data.AAAA.addr = malloc(sizeof(struct in6_addr));			for (i = 0; i < sizeof(struct in6_addr); i++)				rr->data.AAAA.addr->s6_addr[i] = p[i];			p += sizeof(struct in6_addr);			break;			}		case RR_PTR:			rr->data.PTR.name = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf);			if (rr->data.PTR.name == NULL) {				DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n");				parse_error = 1;				break;			}			p += rr_data_len;			break;		case RR_TXT:			txt_rec = &rr->data.TXT;			// not supposed to happen, but we should handle it			if (rr_data_len == 0) {				DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n");				txt_rec->txt = create_label("");				break;			}			while (1) {				txt_rec->txt = copy_label(pkt_buf, pkt_len, p - pkt_buf);				if (txt_rec->txt == NULL) {					DEBUG_PRINTF("unable to copy label for TXT record\n");					parse_error = 1;					break;				}				p += txt_rec->txt[0] + 1;				if (p >= e)					break;				// allocate another record				txt_rec->next = malloc(sizeof(struct rr_data_txt));				txt_rec = txt_rec->next;				txt_rec->next = NULL;			}			break;		default:			// skip to end of RR data			p = e;	}	// if there was a parse error, destroy partial rr_entry	if (parse_error) {		rr_entry_destroy(rr);		return 0;	}	rr_list_append(&pkt->rr_ans, rr);		return p - (pkt_buf + off);}// parse a MDNS packet into an mdns_pkt structstruct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len) {	uint8_t *p = pkt_buf;	size_t off;	struct mdns_pkt *pkt;	int i;	if (pkt_len < 12) 		return NULL;	MALLOC_ZERO_STRUCT(pkt, mdns_pkt);	// parse header	pkt->id 			= mdns_read_u16(p); p += sizeof(uint16_t);	pkt->flags 			= mdns_read_u16(p); p += sizeof(uint16_t);	pkt->num_qn 		= mdns_read_u16(p); p += sizeof(uint16_t);	pkt->num_ans_rr 	= mdns_read_u16(p); p += sizeof(uint16_t);	pkt->num_auth_rr 	= mdns_read_u16(p); p += sizeof(uint16_t);	pkt->num_add_rr 	= mdns_read_u16(p); p += sizeof(uint16_t);	off = p - pkt_buf;	// parse questions	for (i = 0; i < pkt->num_qn; i++) {		size_t l = mdns_parse_qn(pkt_buf, pkt_len, off, pkt);		if (! l) {			DEBUG_PRINTF("error parsing question #%d\n", i);			mdns_pkt_destroy(pkt);			return NULL;		}		off += l;	}	// parse answer RRs	for (i = 0; i < pkt->num_ans_rr; i++) {		size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt);		if (! l) {			DEBUG_PRINTF("error parsing answer #%d\n", i);			mdns_pkt_destroy(pkt);			return NULL;		}		off += l;	}	// TODO: parse the authority and additional RR sections	return pkt;}// encodes a name (label) into a packet using the name compression scheme// encoded names will be added to the compression list for subsequent usestatic size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off,		const uint8_t *name, struct name_comp *comp) {	struct name_comp *c, *c_tail = NULL;	uint8_t *p = pkt_buf + off;	size_t len = 0;	if (name) {		while (*name) {			int segment_len;			// cache the name for subsequent compression			DECL_STRUCT(new_c, name_comp);			// find match for compression			for (c = comp; c; c = c->next) {				if (cmp_nlabel(name, c->label) == 0) {					mdns_write_u16(p, 0xC000 | (c->pos & ~0xC000));					return len + sizeof(uint16_t);				}				if (c->next == NULL)					c_tail = c;			}			// copy this segment			segment_len = *name + 1;			strncpy((char *) p, (char *) name, segment_len);			MALLOC_ZERO_STRUCT(new_c, name_comp);			new_c->label = (uint8_t *) name;			new_c->pos = p - pkt_buf;			c_tail->next = new_c;			// advance to next name segment			p += segment_len;			len += segment_len;			name += segment_len;		}	}	*p = '\0';	// root "label"	len += 1;	return len;}// encodes an RR entry at the given offset// returns the size of the entire RR entrystatic size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, 		struct rr_entry *rr, struct name_comp *comp) {	uint8_t *p = pkt_buf + off, *p_data;	size_t l;	struct rr_data_txt *txt_rec;	uint8_t *label;	int i;	assert(off < pkt_len);	// name	l = mdns_encode_name(pkt_buf, pkt_len, off, rr->name, comp);	assert(l != 0);	p += l;	// type	p = mdns_write_u16(p, rr->type);	// class & cache flush	p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15));	// TTL	p = mdns_write_u32(p, rr->ttl);		// data length (filled in later)	p += sizeof(uint16_t);	// start of data marker	p_data = p;	switch (rr->type) {		case RR_A:			/* htonl() needed coz addr already in net order */			p = mdns_write_u32(p, htonl(rr->data.A.addr));			break;		case RR_AAAA:			for (i = 0; i < sizeof(struct in6_addr); i++)				*p++ = rr->data.AAAA.addr->s6_addr[i];			break;		case RR_PTR:			label = rr->data.PTR.name ? 					rr->data.PTR.name : 					rr->data.PTR.entry->name;			p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp);			break;		case RR_TXT:			txt_rec = &rr->data.TXT;			for (; txt_rec; txt_rec = txt_rec->next) {				int len = txt_rec->txt[0] + 1;				strncpy((char *) p, (char *) txt_rec->txt, len);				p += len;			}			break;		case RR_SRV:			p = mdns_write_u16(p, rr->data.SRV.priority);						p = mdns_write_u16(p, rr->data.SRV.weight);			p = mdns_write_u16(p, rr->data.SRV.port);			p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, 					rr->data.SRV.target, comp);			break;		case RR_NSEC:			p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, 					rr->name, comp);			*p++ = 0;	// bitmap window/block number			*p++ = sizeof(rr->data.NSEC.bitmap);	// bitmap length			for (i = 0; i < sizeof(rr->data.NSEC.bitmap); i++) 				*p++ = rr->data.NSEC.bitmap[i];			break;		default:			DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr->type);	}	// calculate data length based on p	l = p - p_data;	// fill in the length	mdns_write_u16(p - l - sizeof(uint16_t), l);	return p - pkt_buf - off;}// encodes a MDNS packet from the given mdns_pkt struct into a buffer// returns the size of the entire MDNS packetsize_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len) {	struct name_comp *comp;	uint8_t *p = pkt_buf;	//uint8_t *e = pkt_buf + pkt_len;	size_t off;	int i;	struct rr_list *rr_set[3];	assert(answer != NULL);	assert(pkt_len >= 12);	if (p == NULL)		return -1;	// this is an Answer - number of qns should be zero	assert(answer->num_qn == 0);	p = mdns_write_u16(p, answer->id);	p = mdns_write_u16(p, answer->flags);	p = mdns_write_u16(p, answer->num_qn);	p = mdns_write_u16(p, answer->num_ans_rr);	p = mdns_write_u16(p, answer->num_auth_rr);	p = mdns_write_u16(p, answer->num_add_rr);	off = p - pkt_buf;	// allocate list for name compression	comp = malloc(sizeof(struct name_comp));	if (comp == NULL)		return -1;	memset(comp, 0, sizeof(struct name_comp));	// dummy entry	comp->label = (uint8_t *) "";	comp->pos = 0;	// skip encoding of qn	rr_set[0] =	answer->rr_ans;	rr_set[1] = answer->rr_auth;	rr_set[2] =	answer->rr_add;	// encode answer, authority and additional RRs	for (i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) {		struct rr_list *rr = rr_set[i];		for (; rr; rr = rr->next) {			size_t l = mdns_encode_rr(pkt_buf, pkt_len, off, rr->e, comp);			off += l;			if (off >= pkt_len) {				DEBUG_PRINTF("packet buffer too small\n");				return -1;			}		}	}	// free name compression list	while (comp) {		struct name_comp *c = comp->next;		free(comp);		comp = c;	}	return off;}
 |