Initial working responder version reabsorbing TizenRT changes.
Consequently: - fixes #9 Arbitrary memory read while parsing malicious mDNS queries (mdns.c); - fixes #10 Denial of Service vulnerability (infinite loop) while parsing malicious mDNS queries (mdns.c); - fixes #11 Heap-based buffer overread (off-by-one) (mdns.c); - fixes #12 Heap-based buffer overread while parsing mDNS RR section (off-by-one) (mdns.c).
This commit is contained in:
14
Makefile
14
Makefile
@@ -3,8 +3,9 @@
|
||||
#
|
||||
|
||||
CFLAGS += -Wall -pedantic -std=gnu99
|
||||
#CFLAGS += -g
|
||||
CFLAGS += -O2 -DNDEBUG
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O2
|
||||
#CFLAGS += -DNDEBUG
|
||||
LDLIBS = -lpthread
|
||||
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
@@ -28,11 +29,16 @@ clean:
|
||||
-$(RM) $(BIN)
|
||||
-$(RM) libtinysvcmdns.a
|
||||
|
||||
mdns: mdns.o
|
||||
mdns.o: mdns.h
|
||||
|
||||
mdnsd: mdns.o mdnsd.o
|
||||
mdnsd.o: mdns.h mdnsd.h
|
||||
|
||||
testmdnsd.o: mdnsd.h
|
||||
|
||||
testmdnsd: testmdnsd.o libtinysvcmdns.a
|
||||
|
||||
libtinysvcmdns.a: $(patsubst %, libtinysvcmdns.a(%), $(LIBTINYSVCMDNS_OBJS))
|
||||
|
||||
mdnsd: testmdnsd
|
||||
strip -o $@ $<
|
||||
|
||||
|
||||
592
mdns.c
592
mdns.c
@@ -40,9 +40,13 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
/* see RFC 6762 Section 10 */
|
||||
#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
|
||||
#define DEFAULT_TTL 4500
|
||||
|
||||
#define DEFAULT_TTL 120
|
||||
|
||||
/* DETECT_CYCLE checks malformed packet you should handle the value carefully */
|
||||
/* https://www.kb.cert.org/vuls/id/23495 */
|
||||
#define DETECT_CYCLE 255
|
||||
|
||||
struct name_comp {
|
||||
uint8_t *label; // label
|
||||
@@ -59,7 +63,7 @@ inline uint8_t *dup_nlabel(const uint8_t *n) {
|
||||
return NULL;
|
||||
|
||||
assert(n[0] <= 63); // prevent mis-use
|
||||
return (uint8_t *) strdup((char *) n);
|
||||
return (uint8_t *)MDNS_STRDUP((char *)n);
|
||||
}
|
||||
|
||||
// duplicates a label
|
||||
@@ -67,7 +71,7 @@ uint8_t *dup_label(const uint8_t *label) {
|
||||
int len = *label + 1;
|
||||
if (len > 63)
|
||||
return NULL;
|
||||
uint8_t *newlabel = malloc(len + 1);
|
||||
uint8_t *newlabel = MDNS_MALLOC(len + 1);
|
||||
strncpy((char *) newlabel, (char *) label, len);
|
||||
newlabel[len] = '\0';
|
||||
return newlabel;
|
||||
@@ -82,7 +86,7 @@ uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) {
|
||||
len1 = strlen((char *) n1);
|
||||
len2 = strlen((char *) n2);
|
||||
|
||||
s = malloc(len1 + len2 + 1);
|
||||
s = MDNS_MALLOC(len1 + len2 + 1);
|
||||
strncpy((char *) s, (char *) n1, len1);
|
||||
strncpy((char *) s+len1, (char *) n2, len2);
|
||||
s[len1 + len2] = '\0';
|
||||
@@ -92,15 +96,14 @@ uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) {
|
||||
// returns a human-readable name label in dotted form
|
||||
char *nlabel_to_str(const uint8_t *name) {
|
||||
char *label, *labelp;
|
||||
const uint8_t *p;
|
||||
size_t buf_len = 256;
|
||||
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
label = labelp = malloc(buf_len);
|
||||
label = labelp = MDNS_MALLOC(buf_len);
|
||||
|
||||
for (p = name; *p; p++) {
|
||||
for (const uint8_t *p = name; *p; p++) {
|
||||
uint8_t label_len = *p;
|
||||
if (buf_len <= label_len)
|
||||
break;
|
||||
@@ -160,7 +163,7 @@ uint8_t *create_label(const char *txt) {
|
||||
if (len > 63)
|
||||
return NULL;
|
||||
|
||||
s = malloc(len + 2);
|
||||
s = MDNS_MALLOC(len + 2);
|
||||
s[0] = len;
|
||||
strncpy((char *) s + 1, txt, len);
|
||||
s[len + 1] = '\0';
|
||||
@@ -169,35 +172,29 @@ uint8_t *create_label(const char *txt) {
|
||||
}
|
||||
|
||||
// creates a uncompressed name label given a DNS name like "apple.b.com"
|
||||
// free() after use
|
||||
// MDNS_FREE() after use
|
||||
uint8_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);
|
||||
label = MDNS_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) {
|
||||
*lenpos = 0;
|
||||
char* p = label;
|
||||
for (char* e = p + len; p < e; ) {
|
||||
*p = 0;
|
||||
char *dot = memchr(p + 1, '.', e - p - 1);
|
||||
if (dot == NULL)
|
||||
dot = e + 1;
|
||||
*lenpos = dot - p - 1;
|
||||
|
||||
*p = dot - p - 1;
|
||||
p = dot;
|
||||
lenpos = dot;
|
||||
}
|
||||
|
||||
return (uint8_t *) label;
|
||||
@@ -227,29 +224,43 @@ static uint8_t *uncompress_nlabel(uint8_t *pkt_buf, size_t pkt_len, size_t off)
|
||||
uint8_t *e = pkt_buf + pkt_len;
|
||||
size_t len = 0;
|
||||
char *str, *sp;
|
||||
uint16_t cycle = 0;
|
||||
if (off >= pkt_len)
|
||||
return NULL;
|
||||
|
||||
// calculate length of uncompressed label
|
||||
for (p = pkt_buf + off; *p && p < e; p++) {
|
||||
for (p = pkt_buf + off; p + 1 < e && *p; p++) {
|
||||
size_t llen = 0;
|
||||
if (cycle++ >= DETECT_CYCLE) {
|
||||
DEBUG_PRINTF("malformed packet: DoS (loop in compressed labels)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((*p & 0xC0) == 0xC0) {
|
||||
uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
|
||||
if (p2 >= pkt_buf + pkt_len) {
|
||||
DEBUG_PRINTF("malformed packet: heap-buffer-overflow (p2 >= pkt_buf+pkt_len)\n");
|
||||
return NULL;
|
||||
}
|
||||
llen = *p2 + 1;
|
||||
p = p2 + llen - 1;
|
||||
} else {
|
||||
llen = *p + 1;
|
||||
p += llen - 1;
|
||||
if (p + llen > pkt_buf + pkt_len) {
|
||||
DEBUG_PRINTF("malformed packet: heap-buffer-overflow (p + llen > pkt_buf + pkt_len)\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
len += llen;
|
||||
}
|
||||
|
||||
str = sp = malloc(len + 1);
|
||||
str = sp = MDNS_MALLOC(len + 1);
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
// FIXME: must merge this with above code
|
||||
for (p = pkt_buf + off; *p && p < e; p++) {
|
||||
for (p = pkt_buf + off; p + 1 < e && *p; p++) {
|
||||
size_t llen = 0;
|
||||
if ((*p & 0xC0) == 0xC0) {
|
||||
uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
|
||||
@@ -291,8 +302,8 @@ void rr_entry_destroy(struct rr_entry *rr) {
|
||||
switch (rr->type) {
|
||||
case RR_PTR:
|
||||
if (rr->data.PTR.name)
|
||||
free(rr->data.PTR.name);
|
||||
// don't free entry
|
||||
MDNS_FREE(rr->data.PTR.name);
|
||||
// don't free entry
|
||||
break;
|
||||
|
||||
case RR_TXT:
|
||||
@@ -300,11 +311,11 @@ void rr_entry_destroy(struct rr_entry *rr) {
|
||||
while (txt_rec) {
|
||||
struct rr_data_txt *next = txt_rec->next;
|
||||
if (txt_rec->txt)
|
||||
free(txt_rec->txt);
|
||||
MDNS_FREE(txt_rec->txt);
|
||||
|
||||
// only free() if it wasn't part of the struct
|
||||
if (txt_rec != &rr->data.TXT)
|
||||
free(txt_rec);
|
||||
MDNS_FREE(txt_rec);
|
||||
|
||||
txt_rec = next;
|
||||
}
|
||||
@@ -312,7 +323,12 @@ void rr_entry_destroy(struct rr_entry *rr) {
|
||||
|
||||
case RR_SRV:
|
||||
if (rr->data.SRV.target)
|
||||
free(rr->data.SRV.target);
|
||||
MDNS_FREE(rr->data.SRV.target);
|
||||
break;
|
||||
|
||||
case RR_AAAA:
|
||||
if (rr->data.AAAA.addr)
|
||||
MDNS_FREE(rr->data.AAAA.addr);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -320,8 +336,8 @@ void rr_entry_destroy(struct rr_entry *rr) {
|
||||
break;
|
||||
}
|
||||
|
||||
free(rr->name);
|
||||
free(rr);
|
||||
MDNS_FREE(rr->name);
|
||||
MDNS_FREE(rr);
|
||||
}
|
||||
|
||||
// destroys an RR list (and optionally, items)
|
||||
@@ -332,7 +348,7 @@ void rr_list_destroy(struct rr_list *rr, char destroy_items) {
|
||||
rr_next = rr->next;
|
||||
if (destroy_items)
|
||||
rr_entry_destroy(rr->e);
|
||||
free(rr);
|
||||
MDNS_FREE(rr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,11 +364,11 @@ struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) {
|
||||
if (le->e == rr) {
|
||||
if (pe == NULL) {
|
||||
*rr_head = le->next;
|
||||
free(le);
|
||||
MDNS_FREE(le);
|
||||
return rr;
|
||||
} else {
|
||||
pe->next = le->next;
|
||||
free(le);
|
||||
MDNS_FREE(le);
|
||||
return rr;
|
||||
}
|
||||
}
|
||||
@@ -377,27 +393,42 @@ int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr) {
|
||||
for (; e; e = e->next) {
|
||||
// already in list - don't add
|
||||
if (e->e == rr) {
|
||||
free(node);
|
||||
MDNS_FREE(node);
|
||||
return 0;
|
||||
}
|
||||
if (e->next == NULL)
|
||||
taile = e;
|
||||
}
|
||||
taile->next = node;
|
||||
if (taile)
|
||||
taile->next = node;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define FILL_QN_ENTRY(rr, _name, _type, _unicast_query) \
|
||||
rr->name = _name; \
|
||||
rr->type = _type; \
|
||||
rr->unicast_query = _unicast_query; \
|
||||
rr->rr_class = 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;
|
||||
rr->name = _name; \
|
||||
rr->type = _type; \
|
||||
rr->ttl = DEFAULT_TTL; \
|
||||
rr->cache_flush = 1; \
|
||||
rr->rr_class = 1;
|
||||
|
||||
struct rr_entry *qn_create(uint8_t *name, enum rr_type type, int unicast_query)
|
||||
{
|
||||
DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
|
||||
FILL_QN_ENTRY(rr, name, type, unicast_query);
|
||||
return rr;
|
||||
}
|
||||
|
||||
struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr) {
|
||||
DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
|
||||
FILL_RR_ENTRY(rr, name, RR_A);
|
||||
rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
|
||||
rr->data.A.addr = addr;
|
||||
return rr;
|
||||
}
|
||||
@@ -405,6 +436,7 @@ struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr) {
|
||||
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->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
|
||||
rr->data.AAAA.addr = addr;
|
||||
return rr;
|
||||
}
|
||||
@@ -414,6 +446,7 @@ struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -422,6 +455,7 @@ struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -431,8 +465,62 @@ struct rr_entry *rr_create(uint8_t *name, enum rr_type type) {
|
||||
return rr;
|
||||
}
|
||||
|
||||
struct rr_entry *rr_duplicate(struct rr_entry *rr_src)
|
||||
{
|
||||
if (rr_src == NULL)
|
||||
return NULL;
|
||||
|
||||
DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
|
||||
memcpy(rr, rr_src, sizeof(struct rr_entry));
|
||||
if (rr_src->name)
|
||||
rr->name = dup_nlabel(rr_src->name);
|
||||
|
||||
switch (rr_src->type) {
|
||||
case RR_PTR:
|
||||
if (rr_src->data.PTR.name)
|
||||
rr->data.PTR.name = dup_nlabel(rr_src->data.PTR.name);
|
||||
break;
|
||||
|
||||
case RR_SRV:
|
||||
if (rr_src->data.SRV.target)
|
||||
rr->data.SRV.target = dup_nlabel(rr_src->data.SRV.target);
|
||||
break;
|
||||
|
||||
case RR_TXT: {
|
||||
struct rr_data_txt *txt_rec_src = &rr_src->data.TXT;;
|
||||
struct rr_data_txt *txt_rec = &rr->data.TXT;
|
||||
if (txt_rec_src->txt)
|
||||
txt_rec->txt = dup_nlabel(txt_rec_src->txt);
|
||||
txt_rec_src = txt_rec_src->next;
|
||||
|
||||
while (txt_rec_src) {
|
||||
txt_rec->next = (struct rr_data_txt *)MDNS_MALLOC(sizeof(struct rr_data_txt));
|
||||
memset(txt_rec->next, 0, sizeof(struct rr_data_txt));
|
||||
txt_rec = txt_rec->next;
|
||||
if (txt_rec_src->txt)
|
||||
txt_rec->txt = dup_nlabel(txt_rec_src->txt);
|
||||
txt_rec_src = txt_rec_src->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RR_AAAA:
|
||||
if (rr_src->data.AAAA.addr) {
|
||||
rr->data.AAAA.addr = MDNS_MALLOC(sizeof(struct in6_addr));
|
||||
memcpy(rr->data.AAAA.addr, rr_src->data.AAAA.addr, sizeof(struct in6_addr));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to allocate memory
|
||||
break;
|
||||
}
|
||||
|
||||
return rr;
|
||||
}
|
||||
|
||||
void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type) {
|
||||
assert(rr_nsec->type = RR_NSEC);
|
||||
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));
|
||||
@@ -454,7 +542,7 @@ void rr_add_txt(struct rr_entry *rr_txt, const char *txt) {
|
||||
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->next = MDNS_MALLOC(sizeof(struct rr_data_txt));
|
||||
|
||||
txt_rec = txt_rec->next;
|
||||
txt_rec->txt = create_label(txt);
|
||||
@@ -484,6 +572,42 @@ void rr_group_add(struct rr_group **group, struct rr_entry *rr) {
|
||||
*group = g;
|
||||
}
|
||||
|
||||
// deletes a record from an rr_group
|
||||
void rr_group_del(struct rr_group **group, struct rr_entry *rr)
|
||||
{
|
||||
struct rr_group *g;
|
||||
struct rr_entry *e;
|
||||
|
||||
assert(rr != NULL);
|
||||
|
||||
if (*group) {
|
||||
g = rr_group_find(*group, rr->name);
|
||||
if (g) {
|
||||
e = rr_list_remove(&g->rr, rr);
|
||||
if (e) {
|
||||
rr_entry_destroy(e);
|
||||
}
|
||||
|
||||
if (g->rr == NULL) {
|
||||
if (*group == g) {
|
||||
*group = g->next;
|
||||
} else {
|
||||
struct rr_group *grp = *group;
|
||||
for (; grp; grp = grp->next) {
|
||||
if (grp->next == g) {
|
||||
grp->next = g->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MDNS_FREE(g->name);
|
||||
MDNS_FREE(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finds a rr_group matching the given name
|
||||
struct rr_group *rr_group_find(struct rr_group* g, uint8_t *name) {
|
||||
for (; g; g = g->next) {
|
||||
@@ -557,6 +681,32 @@ uint32_t mdns_read_u32(const uint8_t *ptr) {
|
||||
((ptr[3] & 0xFF) << 0);
|
||||
}
|
||||
|
||||
// initialize the packet for query
|
||||
// clears the packet of list structures but not its list items
|
||||
void mdns_init_query(struct mdns_pkt *pkt, uint16_t id)
|
||||
{
|
||||
// copy transaction ID
|
||||
pkt->id = id;
|
||||
|
||||
// query flags
|
||||
pkt->flags = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// initialize the packet for reply
|
||||
// clears the packet of list structures but not its list items
|
||||
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id) {
|
||||
@@ -589,7 +739,7 @@ void mdns_pkt_destroy(struct mdns_pkt *p) {
|
||||
rr_list_destroy(p->rr_auth, 1);
|
||||
rr_list_destroy(p->rr_add, 1);
|
||||
|
||||
free(p);
|
||||
MDNS_FREE(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -603,39 +753,51 @@ static size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
|
||||
assert(pkt != NULL);
|
||||
|
||||
rr = malloc(sizeof(struct rr_entry));
|
||||
if (rr == NULL)
|
||||
goto err;
|
||||
if (off > pkt_len) {
|
||||
DEBUG_PRINTF("Offset %zu beyond packet of length %zu\n", off, pkt_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rr = MDNS_MALLOC(sizeof(struct rr_entry));
|
||||
if (rr == NULL) {
|
||||
DEBUG_PRINTF("Failed to allocate rr_entry\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(rr, 0, sizeof(struct rr_entry));
|
||||
|
||||
name = uncompress_nlabel(pkt_buf, pkt_len, off);
|
||||
if (name == NULL)
|
||||
goto err;
|
||||
if (!name) {
|
||||
DEBUG_PRINTF("malformed packet bad name\n");
|
||||
MDNS_FREE(rr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += label_len(pkt_buf, pkt_len, off);
|
||||
rr->name = name;
|
||||
if (p + 4 > pkt_buf + pkt_len) {
|
||||
DEBUG_PRINTF("malformed packet buff overflow\n");
|
||||
MDNS_FREE(rr);
|
||||
MDNS_FREE(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rr->type = mdns_read_u16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->unicast_query = (*p & 0x80) == 0x80;
|
||||
rr->rr_class = mdns_read_u16(p) & ~0x80;
|
||||
rr->rr_class = mdns_read_u16(p) & ~0x8000;
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr_list_append(&pkt->rr_qn, rr);
|
||||
|
||||
return p - (pkt_buf + off);
|
||||
|
||||
err:
|
||||
free(rr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// parse the MDNS RR section
|
||||
// stores the parsed data in the given mdns_pkt struct
|
||||
static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
struct mdns_pkt *pkt) {
|
||||
struct mdns_pkt *pkt, struct rr_list **rr_l) {
|
||||
const uint8_t *p = pkt_buf + off;
|
||||
const uint8_t *e = pkt_buf + pkt_len;
|
||||
struct rr_entry *rr;
|
||||
@@ -649,24 +811,35 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
if (off > pkt_len)
|
||||
return 0;
|
||||
|
||||
rr = malloc(sizeof(struct rr_entry));
|
||||
if (rr == NULL)
|
||||
goto err;
|
||||
rr = MDNS_MALLOC(sizeof(struct rr_entry));
|
||||
|
||||
memset(rr, 0, sizeof(struct rr_entry));
|
||||
if (rr == NULL) {
|
||||
DEBUG_PRINTF("Failed to allocate rr_entry\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = uncompress_nlabel(pkt_buf, pkt_len, off);
|
||||
if (name == NULL)
|
||||
goto err;
|
||||
if (!name) {
|
||||
DEBUG_PRINTF("malformed packet bad name\n");
|
||||
MDNS_FREE(rr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += label_len(pkt_buf, pkt_len, off);
|
||||
rr->name = name;
|
||||
if (p + 10 - pkt_buf > pkt_len) {
|
||||
DEBUG_PRINTF("malformed packet buff overflow\n");
|
||||
MDNS_FREE(rr);
|
||||
MDNS_FREE(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rr->type = mdns_read_u16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->cache_flush = (*p & 0x80) == 0x80;
|
||||
rr->rr_class = mdns_read_u16(p) & ~0x80;
|
||||
rr->rr_class = mdns_read_u16(p) & ~0x8000;
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->ttl = mdns_read_u32(p);
|
||||
@@ -677,7 +850,7 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
if (p + rr_data_len > e) {
|
||||
DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len, e - p);
|
||||
DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %zu > %zu\n", rr_data_len, e - p);
|
||||
rr_entry_destroy(rr);
|
||||
return 0;
|
||||
}
|
||||
@@ -688,7 +861,7 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
switch (rr->type) {
|
||||
case RR_A:
|
||||
if (rr_data_len < sizeof(uint32_t)) {
|
||||
DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len);
|
||||
DEBUG_PRINTF("invalid rr_data_len=%zu for A record\n", rr_data_len);
|
||||
parse_error = 1;
|
||||
break;
|
||||
}
|
||||
@@ -698,11 +871,11 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
|
||||
case RR_AAAA:
|
||||
if (rr_data_len < sizeof(struct in6_addr)) {
|
||||
DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len);
|
||||
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));
|
||||
rr->data.AAAA.addr = MDNS_MALLOC(sizeof(struct in6_addr));
|
||||
for (int i = 0; i < sizeof(struct in6_addr); i++)
|
||||
rr->data.AAAA.addr->s6_addr[i] = p[i];
|
||||
p += sizeof(struct in6_addr);
|
||||
@@ -718,6 +891,25 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
p += rr_data_len;
|
||||
break;
|
||||
|
||||
case RR_SRV:
|
||||
rr->data.SRV.priority = mdns_read_u16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->data.SRV.weight = mdns_read_u16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->data.SRV.port = mdns_read_u16(p);
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
rr->data.SRV.target = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf);
|
||||
if (rr->data.SRV.target == NULL) {
|
||||
DEBUG_PRINTF("unable to parse/uncompress label for SRV target\n");
|
||||
parse_error = 1;
|
||||
break;
|
||||
}
|
||||
p += (rr_data_len - (3 * sizeof(uint16_t)));
|
||||
break;
|
||||
|
||||
case RR_TXT:
|
||||
txt_rec = &rr->data.TXT;
|
||||
|
||||
@@ -741,7 +933,7 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
break;
|
||||
|
||||
// allocate another record
|
||||
txt_rec->next = malloc(sizeof(struct rr_data_txt));
|
||||
txt_rec->next = MDNS_MALLOC(sizeof(struct rr_data_txt));
|
||||
txt_rec = txt_rec->next;
|
||||
txt_rec->next = NULL;
|
||||
}
|
||||
@@ -751,6 +943,8 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
// skip to end of RR data
|
||||
p = e;
|
||||
}
|
||||
// update time
|
||||
rr->update_time = time(NULL);
|
||||
|
||||
// if there was a parse error, destroy partial rr_entry
|
||||
if (parse_error) {
|
||||
@@ -758,24 +952,21 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
return 0;
|
||||
}
|
||||
|
||||
rr_list_append(&pkt->rr_ans, rr);
|
||||
rr_list_append(rr_l, rr);
|
||||
|
||||
return p - (pkt_buf + off);
|
||||
|
||||
err:
|
||||
free(rr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// parse a MDNS packet into an mdns_pkt struct
|
||||
struct 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)
|
||||
if (pkt_len < 12) {
|
||||
DEBUG_PRINTF("malformed packet: pkt size is less than mininum mdns packet\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MALLOC_ZERO_STRUCT(pkt, mdns_pkt);
|
||||
|
||||
@@ -787,35 +978,56 @@ struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len) {
|
||||
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;
|
||||
size_t off = p - pkt_buf;
|
||||
|
||||
// parse questions
|
||||
for (i = 0; i < pkt->num_qn; i++) {
|
||||
for (int 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;
|
||||
goto error_with_parsing;
|
||||
}
|
||||
|
||||
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) {
|
||||
for (int i = 0; i < pkt->num_ans_rr; i++) {
|
||||
size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt, &pkt->rr_ans);
|
||||
if (!l) {
|
||||
DEBUG_PRINTF("error parsing answer #%d\n", i);
|
||||
mdns_pkt_destroy(pkt);
|
||||
return NULL;
|
||||
goto error_with_parsing;
|
||||
}
|
||||
|
||||
off += l;
|
||||
}
|
||||
|
||||
// TODO: parse the authority and additional RR sections
|
||||
// parse authority RRs
|
||||
for (int i = 0; i < pkt->num_auth_rr; i++) {
|
||||
size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt, &pkt->rr_auth);
|
||||
if (!l) {
|
||||
DEBUG_PRINTF("error parsing authority rr #%d\n", i);
|
||||
goto error_with_parsing;
|
||||
}
|
||||
|
||||
off += l;
|
||||
}
|
||||
|
||||
// parse additional RRs
|
||||
for (int i = 0; i < pkt->num_add_rr; i++) {
|
||||
size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt, &pkt->rr_add);
|
||||
if (!l) {
|
||||
DEBUG_PRINTF("error parsing additional rr #%d\n", i);
|
||||
goto error_with_parsing;
|
||||
}
|
||||
|
||||
off += l;
|
||||
}
|
||||
return pkt;
|
||||
|
||||
error_with_parsing:
|
||||
mdns_pkt_destroy(pkt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// encodes a name (label) into a packet using the name compression scheme
|
||||
@@ -848,7 +1060,8 @@ static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
|
||||
new_c->label = (uint8_t *) name;
|
||||
new_c->pos = p - pkt_buf;
|
||||
c_tail->next = new_c;
|
||||
if (c_tail)
|
||||
c_tail->next = new_c;
|
||||
|
||||
// advance to next name segment
|
||||
p += segment_len;
|
||||
@@ -863,6 +1076,29 @@ static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
return len;
|
||||
}
|
||||
|
||||
// encode a QN entry at the given offset
|
||||
// returns the size of the QN entry
|
||||
static size_t mdns_encode_qn(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;
|
||||
size_t l;
|
||||
|
||||
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 & unicast query
|
||||
p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->unicast_query << 15));
|
||||
|
||||
return p - pkt_buf - off;
|
||||
}
|
||||
|
||||
// encodes an RR entry at the given offset
|
||||
// returns the size of the entire RR entry
|
||||
static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
@@ -908,8 +1144,8 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
|
||||
case RR_PTR:
|
||||
label = rr->data.PTR.name ?
|
||||
rr->data.PTR.name :
|
||||
rr->data.PTR.entry->name;
|
||||
rr->data.PTR.name :
|
||||
rr->data.PTR.entry->name;
|
||||
p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp);
|
||||
break;
|
||||
|
||||
@@ -961,28 +1197,30 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
|
||||
|
||||
// encodes a MDNS packet from the given mdns_pkt struct into a buffer
|
||||
// returns the size of the entire MDNS packet
|
||||
size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len) {
|
||||
size_t mdns_encode_pkt(struct mdns_pkt *encoded_pkt, 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;
|
||||
|
||||
assert(answer != NULL);
|
||||
struct rr_list *rr;
|
||||
int result;
|
||||
|
||||
assert(encoded_pkt != NULL);
|
||||
assert(pkt_len >= 12);
|
||||
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
|
||||
#if 0 /* disabled - now it *can* have qns */
|
||||
// this is an Answer - number of qns should be zero
|
||||
assert(answer->num_qn == 0);
|
||||
#endif
|
||||
|
||||
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);
|
||||
p = mdns_write_u16(p, encoded_pkt->id);
|
||||
p = mdns_write_u16(p, encoded_pkt->flags);
|
||||
p = mdns_write_u16(p, encoded_pkt->num_qn);
|
||||
p = mdns_write_u16(p, encoded_pkt->num_ans_rr);
|
||||
p = mdns_write_u16(p, encoded_pkt->num_auth_rr);
|
||||
p = mdns_write_u16(p, encoded_pkt->num_add_rr);
|
||||
|
||||
off = p - pkt_buf;
|
||||
|
||||
@@ -996,36 +1234,178 @@ size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len
|
||||
comp->label = (uint8_t *) "";
|
||||
comp->pos = 0;
|
||||
|
||||
// skip encoding of qn
|
||||
// encode of qn
|
||||
for (rr = encoded_pkt->rr_qn; rr; rr = rr->next) {
|
||||
size_t l = mdns_encode_qn(pkt_buf, pkt_len, off, rr->e, comp);
|
||||
off += l;
|
||||
|
||||
if (off >= pkt_len) {
|
||||
DEBUG_PRINTF("packet buffer too small\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
struct rr_list *rr_set[] = {
|
||||
answer->rr_ans,
|
||||
answer->rr_auth,
|
||||
answer->rr_add
|
||||
encoded_pkt->rr_ans,
|
||||
encoded_pkt->rr_auth,
|
||||
encoded_pkt->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) {
|
||||
for (int i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) {
|
||||
for (rr = rr_set[i]; 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;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* result is success */
|
||||
result = 0;
|
||||
|
||||
done:
|
||||
// free name compression list
|
||||
while (comp) {
|
||||
struct name_comp *c = comp->next;
|
||||
free(comp);
|
||||
MDNS_FREE(comp);
|
||||
comp = c;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
return -1;
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
#if (MDNS_DEBUG_PRINTF == 1) && (MDNS_MEMORY_DEBUG == 1)
|
||||
struct mdns_meminfo_node {
|
||||
char *func;
|
||||
int line;
|
||||
void *addr;
|
||||
unsigned int size;
|
||||
struct mdns_meminfo_node *next;
|
||||
};
|
||||
|
||||
static struct mdns_meminfo_node *g_mdns_meminfo_head = NULL;
|
||||
|
||||
static void add_meminfo_node(const char *func, int line, void *addr, unsigned int size)
|
||||
{
|
||||
struct mdns_meminfo_node *new_node = (struct mdns_meminfo_node *)malloc(sizeof(struct mdns_meminfo_node));
|
||||
if (new_node == NULL) {
|
||||
DEBUG_PRINTF("ERROR: cannot allocate memory for mdns_meminfo_node\n");
|
||||
return;
|
||||
}
|
||||
|
||||
new_node->func = strdup(func);
|
||||
new_node->line = line;
|
||||
new_node->addr = addr;
|
||||
new_node->size = size;
|
||||
new_node->next = NULL;
|
||||
|
||||
if (g_mdns_meminfo_head) {
|
||||
struct mdns_meminfo_node *pnode = g_mdns_meminfo_head;
|
||||
while (pnode->next) {
|
||||
pnode = pnode->next;
|
||||
}
|
||||
|
||||
pnode->next = new_node;
|
||||
} else {
|
||||
g_mdns_meminfo_head = new_node;
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_meminfo_node(const char *func, int line, void *addr)
|
||||
{
|
||||
int b_found = 0;
|
||||
struct mdns_meminfo_node *cur_node = g_mdns_meminfo_head;
|
||||
struct mdns_meminfo_node *prev_node = NULL;
|
||||
|
||||
while (cur_node) {
|
||||
if (cur_node->addr == addr) {
|
||||
b_found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
prev_node = cur_node;
|
||||
cur_node = cur_node->next;
|
||||
}
|
||||
|
||||
if (b_found) {
|
||||
if (cur_node == g_mdns_meminfo_head) {
|
||||
g_mdns_meminfo_head = cur_node->next;
|
||||
} else {
|
||||
prev_node->next = cur_node->next;
|
||||
}
|
||||
|
||||
free(cur_node->func);
|
||||
free(cur_node);
|
||||
} else {
|
||||
DEBUG_PRINTF("Something Wrong!!! There is no address to remove. (address=%p, %s(%d))\n", addr, func, line);
|
||||
}
|
||||
}
|
||||
|
||||
void *mdns_malloc(const char *func_name, int line, unsigned int size)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if (ptr) {
|
||||
add_meminfo_node(func_name, line, ptr, size);
|
||||
} else {
|
||||
DEBUG_PRINTF("ERROR: cannot allocate memory (size=%d, %s(%d))\n", size, func_name, line);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void mdns_free(const char *func_name, int line, void *ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
remove_meminfo_node(func_name, line, ptr);
|
||||
free(ptr);
|
||||
} else {
|
||||
DEBUG_PRINTF("ERROR: cannot release memory (ptr=%p, %s(%d))\n", ptr, func_name, line);
|
||||
}
|
||||
}
|
||||
|
||||
char *mdns_strdup(const char *func_name, int line, const char *str)
|
||||
{
|
||||
char *ptr = strdup(str);
|
||||
int size = strlen(str) + 1;
|
||||
if (ptr) {
|
||||
add_meminfo_node(func_name, line, ptr, size);
|
||||
} else {
|
||||
DEBUG_PRINTF("ERROR: cannot allocate string buffer memory (size=%d, %s(%d))\n", size, func_name, line);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void mdns_show_meminfo(void)
|
||||
{
|
||||
DEBUG_PRINTF("===========================================================\n");
|
||||
DEBUG_PRINTF(" MDNS Memory Information\n");
|
||||
DEBUG_PRINTF("===========================================================\n");
|
||||
if (g_mdns_meminfo_head) {
|
||||
int total_size = 0;
|
||||
struct mdns_meminfo_node *cur_node = g_mdns_meminfo_head;
|
||||
while (cur_node) {
|
||||
DEBUG_PRINTF(" [address=0x%08X] [size=%d] %s(%d)\n", (int)cur_node->addr, cur_node->size, cur_node->func, cur_node->line);
|
||||
total_size += cur_node->size;
|
||||
cur_node = cur_node->next;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("\n");
|
||||
DEBUG_PRINTF(" TOTAL SIZE = %d\n", total_size);
|
||||
}
|
||||
|
||||
else {
|
||||
DEBUG_PRINTF(" There is Nothing...\n");
|
||||
}
|
||||
DEBUG_PRINTF("===========================================================\n\n");
|
||||
}
|
||||
|
||||
#endif /* MDNS_DEBUG_PRINTF==1 && MDNS_MEMORY_DEBUG==1 */
|
||||
|
||||
42
mdns.h
42
mdns.h
@@ -32,6 +32,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h>
|
||||
@@ -39,14 +40,42 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifndef MDNS_DEBUG_PRINTF
|
||||
#define MDNS_DEBUG_PRINTF 0
|
||||
#endif
|
||||
|
||||
#if MDNS_DEBUG_PRINTF==1
|
||||
#define MDNS_RR_DEBUG 0
|
||||
#define MDNS_MEMORY_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if MDNS_DEBUG_PRINTF==1 && MDNS_MEMORY_DEBUG==1
|
||||
void *mdns_malloc(const char *func_name, int line, unsigned int size);
|
||||
void mdns_free(const char *func_name, int line, void *ptr);
|
||||
char *mdns_strdup(const char *func_name, int line, const char *str);
|
||||
void mdns_show_meminfo(void);
|
||||
|
||||
#define MDNS_MALLOC(size) mdns_malloc(__FUNCTION__, __LINE__, size)
|
||||
#define MDNS_FREE(ptr) mdns_free(__FUNCTION__, __LINE__, ptr)
|
||||
#define MDNS_STRDUP(str) mdns_strdup(__FUNCTION__, __LINE__, str)
|
||||
|
||||
#else
|
||||
#define MDNS_MALLOC(size) malloc(size)
|
||||
#define MDNS_FREE(ptr) free(ptr)
|
||||
#define MDNS_STRDUP(size) strdup(size)
|
||||
#endif
|
||||
#define MALLOC_ZERO_STRUCT(x, type) \
|
||||
x = malloc(sizeof(struct type)); \
|
||||
memset(x, 0, sizeof(struct type));
|
||||
x = MDNS_MALLOC(sizeof(struct type)); \
|
||||
memset(x, 0, sizeof(struct type))
|
||||
|
||||
#define DECL_MALLOC_ZERO_STRUCT(x, type) \
|
||||
struct type * MALLOC_ZERO_STRUCT(x, type)
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DECL_ZERO_STRUCT(_x, _type) \
|
||||
struct _type _x; \
|
||||
memset(&(_x), 0, sizeof(struct _type))
|
||||
|
||||
#if MDNS_DEBUG_PRINTF==1
|
||||
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINTF(...) ((void) 0)
|
||||
@@ -118,6 +147,7 @@ struct rr_entry {
|
||||
struct rr_data_a A;
|
||||
struct rr_data_aaaa AAAA;
|
||||
} data;
|
||||
time_t update_time;
|
||||
};
|
||||
|
||||
struct rr_list {
|
||||
@@ -162,8 +192,9 @@ struct mdns_pkt {
|
||||
|
||||
struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len);
|
||||
|
||||
void mdns_init_query(struct mdns_pkt *pkt, uint16_t id);
|
||||
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id);
|
||||
size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len);
|
||||
size_t mdns_encode_pkt(struct mdns_pkt *encoded_pkt, uint8_t *pkt_buf, size_t pkt_len);
|
||||
|
||||
void mdns_pkt_destroy(struct mdns_pkt *p);
|
||||
void rr_group_destroy(struct rr_group *group);
|
||||
@@ -171,17 +202,20 @@ struct rr_group *rr_group_find(struct rr_group *g, uint8_t *name);
|
||||
struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type);
|
||||
struct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry);
|
||||
void rr_group_add(struct rr_group **group, struct rr_entry *rr);
|
||||
void rr_group_del(struct rr_group **group, struct rr_entry *rr);
|
||||
|
||||
int rr_list_count(struct rr_list *rr);
|
||||
int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr);
|
||||
struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr);
|
||||
void rr_list_destroy(struct rr_list *rr, char destroy_items);
|
||||
|
||||
struct rr_entry *qn_create(uint8_t *name, enum rr_type type, int unicast_query);
|
||||
struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr);
|
||||
struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target);
|
||||
struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr);
|
||||
struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr);
|
||||
struct rr_entry *rr_create(uint8_t *name, enum rr_type type);
|
||||
struct rr_entry *rr_duplicate(struct rr_entry *rr_src);
|
||||
void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type);
|
||||
void rr_add_txt(struct rr_entry *rr_txt, const char *txt);
|
||||
|
||||
|
||||
113
mdnsd.h
113
mdnsd.h
@@ -25,6 +25,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/* APIs for Multicast DNS */
|
||||
|
||||
#ifndef __MDNSD_H__
|
||||
#define __MDNSD_H__
|
||||
@@ -34,12 +35,76 @@
|
||||
struct mdnsd;
|
||||
struct mdns_service;
|
||||
|
||||
// starts a MDNS responder instance
|
||||
// returns NULL if unsuccessful
|
||||
struct mdnsd *mdnsd_start();
|
||||
#define MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT 10
|
||||
#define MAX_SERVICE_DISCOVERY_TIME_MS (60 * 1000)
|
||||
|
||||
// stops the given MDNS responder instance
|
||||
void mdnsd_stop(struct mdnsd *s);
|
||||
#define MDNS_HOSTNAME_RESOLVER_TIMEOUT_MSEC (3 * 1000)
|
||||
#define MDNS_HOSTNAME_RESOLVER_MAX_TRY_COUNT 5
|
||||
#define MDNS_HOSTNAME_RESOLVER_WAIT_TIME_MSEC 250
|
||||
|
||||
#define MDNS_SERVICE_DISCOVERY_MAX_TRY_COUNT 5
|
||||
#define MDNS_SERVICE_DISCOVERY_WAIT_TIME_MSEC 250
|
||||
|
||||
/* Structure of MDNS service information */
|
||||
struct mdns_service_info {
|
||||
char *type;
|
||||
char *instance_name;
|
||||
char *hostname;
|
||||
unsigned int ipaddr; /* ipv4 */
|
||||
unsigned int port;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
||||
|
||||
/**
|
||||
* starts the MDNS daemon.
|
||||
*
|
||||
* [in] desired_hostname the desired host name as string type; if same name is in the network, an alternative name will be set as hostname.
|
||||
* [in] netif_name network interface name as string type
|
||||
* On success, 0 is returned. On failure, a negative value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_start(const char *desired_hostname, const char *netif_name);
|
||||
|
||||
/**
|
||||
* mdnsd_stop() stops the MDNS daemon.
|
||||
*
|
||||
* On success, 0 is returned. On failure, a negative value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_stop(void);
|
||||
|
||||
/**
|
||||
* mdnsd_get_hostname() gets the current host name as MDNS type.
|
||||
*
|
||||
* [out] hostname_result 32-bytes string buffer for the host name result
|
||||
* On success, 0 is returned. On failure, a negative value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_get_hostname(char *hostname_result);
|
||||
|
||||
/**
|
||||
* mdnsd_register_service() register a service to expose through mDNS-SD.
|
||||
*
|
||||
* [in] instance instance name to expose
|
||||
* [in] type type of service. e.g. _http._tcp.local
|
||||
* [in] port port to which the service is reachable
|
||||
* [in] hostname if NULL, use the hostname configured when starting the daemon,
|
||||
* or use this parameter otherwise
|
||||
* [in] txt text records to add to the service announcement. Can be NULL.
|
||||
* On success, 0 is returned. On failure, a negative errno value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_register_service(const char *instance, const char *type,
|
||||
uint16_t port, const char *hostname, const char *txt[]);
|
||||
|
||||
// sets the hostname for the given MDNS responder instance
|
||||
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip);
|
||||
@@ -48,11 +113,41 @@ void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip);
|
||||
void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr);
|
||||
|
||||
// registers a service with the MDNS responder instance
|
||||
struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
|
||||
const char *type, uint16_t port, const char *hostname, const char *txt[]);
|
||||
//struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
|
||||
// const char *type, uint16_t port, const char *hostname, const char *txt[]);
|
||||
|
||||
// destroys the mdns_service struct returned by mdnsd_register_svc()
|
||||
void mdns_service_destroy(struct mdns_service *srv);
|
||||
//void mdns_service_destroy(struct mdns_service *srv);
|
||||
|
||||
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
||||
|
||||
/**
|
||||
* mdnsd_resolve_hostname() gets ip address with the given hostname.
|
||||
*
|
||||
* [in] hostname host name as string type
|
||||
* [out] ipaddr the pointer of ip address result
|
||||
* On success, 0 is returned. On failure, a negative value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_resolve_hostname(char *hostname, int *ipaddr);
|
||||
|
||||
/**
|
||||
* mdnsd_discover_service() discovers services with the given service type string
|
||||
*
|
||||
* [in] service_type mdns service type string
|
||||
* [in] discover_time_msec time in milliseconds for discovering service
|
||||
* [out] service_list the array of service list
|
||||
* [out] num_of_services number of services
|
||||
* On success, 0 is returned. On failure, a negative value is returned.
|
||||
*
|
||||
*/
|
||||
int mdnsd_discover_service(char *service_type, int discover_time_msec, struct mdns_service_info **service_list, int *num_of_services);
|
||||
|
||||
|
||||
#endif/*!__MDNSD_H__*/
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*!__MDNSD_H__ */
|
||||
|
||||
|
||||
310
testmdnsd.c
310
testmdnsd.c
@@ -25,6 +25,13 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -36,16 +43,15 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mdns.h"
|
||||
#include "mdnsd.h"
|
||||
|
||||
#define HOSTFILE "/tmp/hosts"
|
||||
|
||||
#if 0
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
void
|
||||
background()
|
||||
{
|
||||
@@ -75,55 +81,287 @@ background()
|
||||
|
||||
setsid();
|
||||
}
|
||||
#endif
|
||||
|
||||
void freehostents( struct hostent * h) {
|
||||
for (struct hostent *p = h; p->h_name; ++p) {
|
||||
free(p->h_name);
|
||||
free(p->h_addr_list);
|
||||
}
|
||||
free(h);
|
||||
}
|
||||
|
||||
struct hostent * add_host(struct hostent * hosts, const char * hostname, struct in_addr ip) {
|
||||
struct hostent * p;
|
||||
|
||||
int newname = 1;
|
||||
for (p=hosts;p->h_name;++p) {
|
||||
newname = 1;
|
||||
if (p->h_length == 0 ||
|
||||
0 == (newname = strcasecmp(p->h_name, hostname)))
|
||||
break;
|
||||
}
|
||||
if (p->h_name) {
|
||||
if (0 != newname) {
|
||||
free(p->h_name);
|
||||
p->h_name = strdup(hostname);
|
||||
}
|
||||
} else {
|
||||
p->h_name = strdup(hostname);
|
||||
(p+1)->h_name = 0;
|
||||
}
|
||||
p->h_aliases = 0;
|
||||
p->h_addrtype = AF_INET;
|
||||
p->h_length = sizeof(in_addr_t);
|
||||
p->h_addr_list = (char **) realloc(p->h_addr_list, sizeof(struct in_addr)+2*sizeof(struct in_addr*));
|
||||
p->h_addr_list[0] = (char *)(p->h_addr_list + 2);
|
||||
*(struct in_addr *)(p->h_addr_list[0]) = ip;
|
||||
p->h_addr_list[1] = NULL;
|
||||
return ++p;
|
||||
}
|
||||
|
||||
struct hostent * realloc_hosts(struct hostent * p, size_t n) {
|
||||
struct hostent * new = (struct hostent *) realloc(p,n*sizeof(struct hostent));
|
||||
if (!new) {
|
||||
fprintf(stderr, "Failed to allocate hostent: %s\n", strerror(errno));
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
struct hostent * read_hosts(unsigned int extra) {
|
||||
FILE * hosts = fopen( HOSTFILE, "r");
|
||||
if (!hosts) {
|
||||
fprintf(stderr, "Failed to open hosts file: %s\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
size_t nhost = extra;
|
||||
while (0 <= fscanf(hosts, "%*[^\n]") && !ferror(hosts)) {
|
||||
if (0 <= fscanf(hosts,"%*c"))
|
||||
++nhost;
|
||||
}
|
||||
if (ferror(hosts) || -1 == fseek(hosts, 0L, SEEK_SET)) {
|
||||
fprintf(stderr, "Failed to read hosts file: %s\n", strerror(errno));
|
||||
fclose(hosts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hostent * hostents = (struct hostent *) realloc_hosts(NULL,++nhost);
|
||||
if (!hostents) {
|
||||
fclose(hosts);
|
||||
return NULL;
|
||||
} else {
|
||||
memset(hostents,0,nhost*sizeof(struct hostent));
|
||||
}
|
||||
|
||||
char ip[256];
|
||||
char name[256];
|
||||
char fmt[32];
|
||||
|
||||
/* a format for [the beginning of] a /etc/hosts line */
|
||||
int n = 0;
|
||||
if (snprintf( fmt, sizeof fmt - 1, "%%%zus %%%zus%%n%n", sizeof ip - 1, sizeof name - 1, &n) != n) {
|
||||
fprintf(stderr, "Format length %d inadequate\n", n);
|
||||
}
|
||||
/* and for further hostnames */
|
||||
char * nfmt = strchr(fmt,' ')+1;
|
||||
char * line = NULL;
|
||||
size_t linesz = 0;
|
||||
size_t nleft = nhost;
|
||||
hostents->h_name = 0;
|
||||
for (struct hostent * p = hostents;;) {
|
||||
int ret = getline( &line, &linesz, hosts);
|
||||
int pos = 0;
|
||||
if (ret == -1) {
|
||||
if (!feof(hosts)) {
|
||||
fprintf(stderr, "Failure reading hosts file: %s\n", strerror(errno));
|
||||
freehostents(hostents);
|
||||
hostents = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* skip initial space and any comment line */
|
||||
ret = sscanf( line, " %n", &pos );
|
||||
if (0 <= ret) {
|
||||
int n = 0;
|
||||
ret = sscanf( line+pos, "#%*[^\n]%n", &n);
|
||||
if (n > 0) continue;
|
||||
}
|
||||
|
||||
if (0 <= ret) {
|
||||
int n = 0;
|
||||
ret = sscanf( line+pos, fmt, ip, name, &n);
|
||||
// printf( "Read: %.255s\t%.32s\t%d\n", name, ip, ret);
|
||||
pos += n;
|
||||
}
|
||||
if (ret == 2) {
|
||||
struct in_addr it;
|
||||
it.s_addr = inet_addr(ip);
|
||||
do {
|
||||
struct hostent * pp = add_host(p, name, it);
|
||||
char * dot = strrchr(p->h_name, '.');
|
||||
if (dot && 0 == strcasecmp(dot, ".local"))
|
||||
/* flag as expendable */
|
||||
p->h_length = 0;
|
||||
p = pp;
|
||||
|
||||
/* running out of space? try for more */
|
||||
if (--nleft < extra + 1) {
|
||||
size_t n = 2*nhost;
|
||||
struct hostent * new = (struct hostent *) realloc_hosts(hostents,n);
|
||||
if (!new) {
|
||||
free(line);
|
||||
fclose(hosts);
|
||||
free(hostents);
|
||||
return NULL;
|
||||
}
|
||||
/* zero the new space and adjust counts and pointers */
|
||||
memset(new+nhost,0,(n-nhost)*sizeof(struct hostent));
|
||||
nleft += n - nhost;
|
||||
nhost = n;
|
||||
p += new - hostents;
|
||||
hostents = new;
|
||||
}
|
||||
|
||||
/* allow for ip name [name ...] */
|
||||
int n = 0;
|
||||
ret = sscanf( line+pos, " %n", &n);
|
||||
pos += n;
|
||||
/* skip comment */
|
||||
if (0 <= ret) {
|
||||
n = 0;
|
||||
ret = sscanf( line+pos, "#%*[^\n]%n", &n);
|
||||
if (n > 0) break;
|
||||
}
|
||||
if (0 <= ret) {
|
||||
n = 0;
|
||||
ret = sscanf( line+pos, nfmt, name, &n);
|
||||
pos += n;
|
||||
}
|
||||
} while (ret == 1);
|
||||
p->h_name = 0;
|
||||
} else if (ret >= 0) {
|
||||
fprintf(stderr, "Unexpected format in hosts file; line was\n%s\n", line);
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
fclose(hosts);
|
||||
return hostents;
|
||||
}
|
||||
|
||||
int write_hosts(const struct hostent * hostents) {
|
||||
int ret = -1;
|
||||
FILE * hosts = fopen( HOSTFILE, "w");
|
||||
if (!hosts) {
|
||||
fprintf(stderr, "Failed to open hosts file: %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
for (const struct hostent * p = hostents;p->h_name;++p) {
|
||||
//printf( "writing %.255s\n", p->h_name);
|
||||
if (p->h_length > 0) {
|
||||
for (struct in_addr ** pa = (struct in_addr **)(p->h_addr_list); *pa; ++pa) {
|
||||
ret = fprintf( hosts, "%s\t%s\n", inet_ntoa(**pa), p->h_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(hosts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dump_hosts(const struct hostent * hostents) {
|
||||
for (const struct hostent * p = hostents;p->h_name;++p) {
|
||||
printf( "Host: %.255s\t%.32s\t%d\n", p->h_name,
|
||||
inet_ntoa(*(struct in_addr *)(p->h_addr_list[0])),
|
||||
p->h_length);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char hostname[0x100], ifname[0x100];
|
||||
struct hostent *hp;
|
||||
char hostname[0x100], ifname[0x120], fullname[0x108];
|
||||
|
||||
struct in_addr saddr;
|
||||
int notrunning = 1;
|
||||
|
||||
if (gethostname(hostname, sizeof(hostname) - 1) == -1)
|
||||
{
|
||||
printf("Couldn't retrieve humax hostname.\n");
|
||||
fprintf(stderr, "Couldn't retrieve humax hostname.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(hp = gethostbyname(hostname)))
|
||||
{
|
||||
printf("Couldn't retrieve humax IP address.\n");
|
||||
return 1;
|
||||
int n = 0;
|
||||
if (snprintf(fullname, sizeof fullname - 1, "%s.local%n", hostname, &n) != n ||
|
||||
(n = 0, sprintf(ifname, "Humax Fox T2 (%s) Web Interface%n", hostname, &n) != n)) {
|
||||
fprintf( stderr, "Format length %d inadeqate\n", n);
|
||||
}
|
||||
memmove((char *)&saddr.s_addr, (char *)hp->h_addr,
|
||||
sizeof(saddr.s_addr));
|
||||
printf("Hostname: %s (%s)\n", hostname, inet_ntoa(saddr));
|
||||
|
||||
struct mdnsd *svr = mdnsd_start();
|
||||
if (svr == NULL) {
|
||||
printf("mdnsd_start() error\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Started MDNS Service\n");
|
||||
|
||||
mdnsd_set_hostname(svr, hostname, saddr.s_addr);
|
||||
|
||||
sprintf(ifname, "Humax Fox T2 (%s) Web Interface", hostname);
|
||||
|
||||
const char *txt[] = {
|
||||
"path=/",
|
||||
NULL
|
||||
};
|
||||
struct mdns_service *svc = mdnsd_register_svc(
|
||||
svr, ifname, "_http._tcp.local", 80,
|
||||
NULL, txt);
|
||||
//mdns_service_destroy(svc);
|
||||
|
||||
printf("Registered name.\n");
|
||||
for (saddr.s_addr = 0;;sleep(61)) {
|
||||
struct hostent *hp = gethostbyname(hostname);
|
||||
char * ip_addr;
|
||||
|
||||
if (!hp || hp->h_length != sizeof(saddr.s_addr))
|
||||
{
|
||||
fprintf(stderr, "Couldn't retrieve humax IP address.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (saddr.s_addr != *(in_addr_t *)(hp->h_addr_list[0])) {
|
||||
if ((notrunning == 0) && (0 > mdnsd_stop())) {
|
||||
fprintf(stderr, "mdnsd_stop() error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
saddr.s_addr = *(in_addr_t *)(hp->h_addr_list[0]);
|
||||
|
||||
for (;;)
|
||||
sleep(3600);
|
||||
ip_addr = inet_ntoa(saddr);
|
||||
|
||||
// mdnsd_stop(svr);
|
||||
printf("Hostname: %s (%s)\n", hostname, ip_addr);
|
||||
|
||||
if (mdnsd_start(fullname, ip_addr) < 0) {
|
||||
fprintf(stderr, "mdnsd_start() error\n");
|
||||
return 1;
|
||||
}
|
||||
notrunning = 0;
|
||||
|
||||
if (0 <= mdnsd_register_service(
|
||||
ifname, "_http._tcp.local", 80,
|
||||
hostname, txt))
|
||||
printf("Registered name.\n");
|
||||
}
|
||||
|
||||
int numserv = 0;
|
||||
struct mdns_service_info * svcinfo;
|
||||
|
||||
if (0 <= mdnsd_discover_service("_http._tcp.local", 5000, &svcinfo, &numserv)) {
|
||||
|
||||
struct hostent * hosts = read_hosts(numserv);
|
||||
if (!hosts) {
|
||||
fprintf(stderr,"null hosts\n");
|
||||
return 1;
|
||||
}
|
||||
//dump_hosts(hosts);
|
||||
struct hostent * p = hosts;
|
||||
for (int i = 0; i < numserv; ++i) {
|
||||
struct in_addr it;
|
||||
it.s_addr = svcinfo[i].ipaddr;
|
||||
printf("Host %d: %s (%s)\n", i, svcinfo[i].hostname, inet_ntoa(it));
|
||||
p = add_host(p, svcinfo[i].hostname, it);
|
||||
}
|
||||
p->h_name = 0;
|
||||
//dump_hosts(hosts);
|
||||
write_hosts(hosts);
|
||||
freehostents(hosts);
|
||||
}
|
||||
}
|
||||
|
||||
if ((notrunning == 0) && (0 > mdnsd_stop())) {
|
||||
fprintf(stderr,"mdnsd_stop() error\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user