Compare commits

...

1 Commits

Author SHA1 Message Date
df aa7053ec1f Import HummyPkg version 2020-07-18 13:49:36 +01:00
7 changed files with 242 additions and 484 deletions

View File

@ -2,17 +2,12 @@
# Makefile for tinysvcmdns
#
CC=mipsel-linux-gcc
CFLAGS += -Wall -pedantic -std=gnu99
#CFLAGS += -g
CFLAGS += -O2 -DNDEBUG
#CFLAGS += -g
LDLIBS = -lpthread
ifneq ($(CROSS_COMPILE),)
CC = gcc
CC := $(CROSS_COMPILE)$(CC)
AR := $(CROSS_COMPILE)$(AR)
endif
BIN=testmdnsd
LIBTINYSVCMDNS_OBJS = mdns.o mdnsd.o

View File

@ -1,14 +1,3 @@
NOTICE
=======
This project is un-maintained, and has been since 2013.
There are known vulnerabilities that have been disclosed, and quite possibly
others that have yet to be disclosed. Refer to the issue tracker.
You are advised to NOT use this library for any new projects / products.
tinysvcmdns
============
tinysvcmdns is a tiny MDNS responder implementation for publishing services.
@ -57,7 +46,6 @@ Copyright (C) 2011 Darell Tan
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

254
mdns.c
View File

@ -32,13 +32,7 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#ifdef _WIN32
#include <winsock.h>
#include <in6addr.h>
#else
#include <netinet/in.h>
#endif
#define DEFAULT_TTL 120
@ -53,11 +47,13 @@ struct name_comp {
// ----- label functions -----
// compares 2 names
static inline int cmp_nlabel(const uint8_t *L1, const uint8_t *L2) {
return strcmp((char *) L1, (char *) L2);
}
// duplicates a name
inline uint8_t *dup_nlabel(const uint8_t *n) {
if (n == NULL)
return NULL;
assert(n[0] <= 63); // prevent mis-use
return (uint8_t *) strdup((char *) n);
}
@ -93,33 +89,20 @@ uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) {
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;
assert(name != NULL);
label = labelp = malloc(buf_len);
label = labelp = malloc(256);
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;
strncpy(labelp, (char *) p + 1, *p);
labelp += *p;
*labelp = '.';
labelp++;
buf_len -= label_len + 1;
p += label_len;
p += *p;
}
// avoid writing NULL past end of buffer
if (buf_len == 0)
labelp--;
*labelp = '\0';
return label;
@ -153,9 +136,7 @@ uint8_t *create_label(const char *txt) {
int len;
uint8_t *s;
if (txt == NULL)
return NULL;
assert(txt != NULL);
len = strlen(txt);
if (len > 63)
return NULL;
@ -270,19 +251,6 @@ static uint8_t *uncompress_nlabel(uint8_t *pkt_buf, size_t pkt_len, size_t off)
// ----- 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);
@ -402,13 +370,6 @@ struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr) {
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;
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);
@ -420,7 +381,6 @@ struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target) {
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;
return rr;
}
@ -496,29 +456,12 @@ 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_list *rr = rr_list;
for (; rr; rr = rr->next) {
if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0)
if (rr->e->type == type)
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 matches
struct 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;
@ -531,32 +474,6 @@ void rr_group_destroy(struct rr_group *group) {
}
}
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 items
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id) {
@ -604,32 +521,22 @@ 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;
memset(rr, 0, sizeof(struct rr_entry));
name = uncompress_nlabel(pkt_buf, pkt_len, off);
if (name == NULL)
goto err;
p += label_len(pkt_buf, pkt_len, off);
rr->name = name;
rr->type = mdns_read_u16(p);
rr->type = ntohs( * (uint16_t *) p );
p += sizeof(uint16_t);
rr->unicast_query = (*p & 0x80) == 0x80;
rr->rr_class = mdns_read_u16(p) & ~0x80;
rr->rr_class = ntohs( * (uint16_t *) p) & ~0x80;
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
@ -642,7 +549,6 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
uint8_t *name;
size_t rr_data_len = 0;
struct rr_data_txt *txt_rec;
int parse_error = 0;
assert(pkt != NULL);
@ -650,91 +556,46 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
return 0;
rr = malloc(sizeof(struct rr_entry));
if (rr == NULL)
goto err;
memset(rr, 0, sizeof(struct rr_entry));
name = uncompress_nlabel(pkt_buf, pkt_len, off);
if (name == NULL)
goto err;
p += label_len(pkt_buf, pkt_len, off);
rr->name = name;
rr->type = mdns_read_u16(p);
rr->type = ntohs( * (uint16_t *) p );
p += sizeof(uint16_t);
rr->cache_flush = (*p & 0x80) == 0x80;
rr->rr_class = mdns_read_u16(p) & ~0x80;
rr->rr_class = ntohs( * (uint16_t *) p) & ~0x80;
p += sizeof(uint16_t);
rr->ttl = mdns_read_u32(p);
rr->ttl = ntohl( * (uint32_t *) p );
p += sizeof(uint32_t);
// RR data
rr_data_len = mdns_read_u16(p);
rr_data_len = ntohs( * (uint16_t *) p );
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);
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=%lu 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 */
rr->data.A.addr = ntohl( * (uint32_t *) p );
p += sizeof(uint32_t);
break;
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);
parse_error = 1;
break;
}
rr->data.AAAA.addr = 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);
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;
if (!txt_rec->txt)
break;
}
p += txt_rec->txt[0] + 1;
if (p >= e)
@ -752,24 +613,14 @@ static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
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);
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;
uint16_t *p = (uint16_t *) pkt_buf;
size_t off;
struct mdns_pkt *pkt;
int i;
@ -780,14 +631,14 @@ struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len) {
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);
pkt->id = ntohs(*p); p++;
pkt->flags = ntohs(*p); p++;
pkt->num_qn = ntohs(*p); p++;
pkt->num_ans_rr = ntohs(*p); p++;
pkt->num_auth_rr = ntohs(*p); p++;
pkt->num_add_rr = ntohs(*p); p++;
off = p - pkt_buf;
off = (uint8_t *) p - pkt_buf;
// parse questions
for (i = 0; i < pkt->num_qn; i++) {
@ -831,7 +682,7 @@ static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off,
// 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));
*(uint16_t *) p = htons(0xC000 | (c->pos & ~0xC000));
return len + sizeof(uint16_t);
}
@ -881,13 +732,16 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
p += l;
// type
p = mdns_write_u16(p, rr->type);
*(uint16_t *) p = htons(rr->type);
p += sizeof(uint16_t);
// class & cache flush
p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15));
*(uint16_t *) p = htons((rr->rr_class & ~0x8000) | (rr->cache_flush << 15));
p += sizeof(uint16_t);
// TTL
p = mdns_write_u32(p, rr->ttl);
*(uint32_t *) p = htonl(rr->ttl);
p += sizeof(uint32_t);
// data length (filled in later)
p += sizeof(uint16_t);
@ -897,13 +751,8 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
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];
*(uint32_t *) p = (rr->data.A.addr);
p += sizeof(uint32_t);
break;
case RR_PTR:
@ -923,11 +772,14 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
break;
case RR_SRV:
p = mdns_write_u16(p, rr->data.SRV.priority);
*(uint16_t *) p = htons(rr->data.SRV.priority);
p += sizeof(uint16_t);
p = mdns_write_u16(p, rr->data.SRV.weight);
*(uint16_t *) p = htons(rr->data.SRV.weight);
p += sizeof(uint16_t);
p = mdns_write_u16(p, rr->data.SRV.port);
*(uint16_t *) p = htons(rr->data.SRV.port);
p += sizeof(uint16_t);
p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf,
rr->data.SRV.target, comp);
@ -954,7 +806,7 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
l = p - p_data;
// fill in the length
mdns_write_u16(p - l - sizeof(uint16_t), l);
*(uint16_t *) (p - l - sizeof(uint16_t)) = htons(l);
return p - pkt_buf - off;
}
@ -963,7 +815,7 @@ static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
// 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) {
struct name_comp *comp;
uint8_t *p = pkt_buf;
uint16_t *p = (uint16_t *) pkt_buf;
//uint8_t *e = pkt_buf + pkt_len;
size_t off;
int i;
@ -977,14 +829,14 @@ size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len
// 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);
*p++ = htons(answer->id);
*p++ = htons(answer->flags);
*p++ = htons(answer->num_qn);
*p++ = htons(answer->num_ans_rr);
*p++ = htons(answer->num_auth_rr);
*p++ = htons(answer->num_add_rr);
off = p - pkt_buf;
off = (uint8_t *) p - pkt_buf;
// allocate list for name compression
comp = malloc(sizeof(struct name_comp));

24
mdns.h
View File

@ -31,13 +31,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#define MALLOC_ZERO_STRUCT(x, type) \
x = malloc(sizeof(struct type)); \
@ -82,10 +75,6 @@ struct rr_data_a {
uint32_t addr;
};
struct rr_data_aaaa {
struct in6_addr *addr;
};
struct rr_entry {
uint8_t *name;
@ -116,7 +105,6 @@ struct rr_entry {
struct rr_data_txt TXT;
struct rr_data_ptr PTR;
struct rr_data_a A;
struct rr_data_aaaa AAAA;
} data;
};
@ -143,9 +131,6 @@ struct rr_group {
#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F)
#define MDNS_FLAG_GET_OPCODE(x) ((x >> 11) & 0x0F)
// gets the PTR target name, either from "name" member or "entry" member
#define MDNS_RR_GET_PTR_NAME(rr) (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name)
struct mdns_pkt {
uint16_t id; // transaction ID
uint16_t flags;
@ -169,7 +154,6 @@ void mdns_pkt_destroy(struct mdns_pkt *p);
void rr_group_destroy(struct rr_group *group);
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);
int rr_list_count(struct rr_list *rr);
@ -179,14 +163,11 @@ void rr_list_destroy(struct rr_list *rr, char destroy_items);
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);
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);
const char *rr_get_type_name(enum rr_type type);
uint8_t *create_label(const char *txt);
uint8_t *create_nlabel(const char *name);
char *nlabel_to_str(const uint8_t *name);
@ -194,9 +175,4 @@ uint8_t *dup_label(const uint8_t *label);
uint8_t *dup_nlabel(const uint8_t *n);
uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2);
// compares 2 names
static inline int cmp_nlabel(const uint8_t *L1, const uint8_t *L2) {
return strcmp((char *) L1, (char *) L2);
}
#endif /*!__MDNS_H__*/

277
mdnsd.c
View File

@ -26,22 +26,15 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define LOG_ERR 3
#else
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <syslog.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
@ -51,20 +44,15 @@
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
/*
* Define a proper IP socket level if not already done.
* Required to compile on OS X
*/
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#endif
#include <syslog.h>
#include "mdns.h"
#include "mdnsd.h"
#define MDNS_ADDR "224.0.0.251"
#define MDNS_PORT 5353
#define LLMNR_ADDR "224.0.0.252"
#define LLMNR_PORT 5355
#define PACKET_SIZE 65536
@ -74,6 +62,7 @@
struct mdnsd {
pthread_mutex_t data_lock;
int sockfd;
int llmnrfd;
int notify_pipe[2];
int stop_flag;
@ -102,7 +91,7 @@ static void log_message(int loglevel, char *fmt_str, ...) {
fprintf(stderr, "%s\n", buf);
}
static int create_recv_sock() {
static int create_recv_sock(char *addr, int port) {
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
log_message(LOG_ERR, "recv socket(): %m");
@ -121,7 +110,7 @@ static int create_recv_sock() {
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MDNS_PORT);
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
log_message(LOG_ERR, "recv bind(): %m");
@ -131,7 +120,7 @@ static int create_recv_sock() {
struct ip_mreq mreq;
memset(&mreq, 0, sizeof(struct ip_mreq));
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
mreq.imr_multiaddr.s_addr = inet_addr(addr);
if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m");
return r;
@ -166,11 +155,28 @@ static ssize_t send_packet(int fd, const void *data, size_t len) {
return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in));
}
static ssize_t send_llmnr_packet(int fd, struct sockaddr_in *from, const void *data, size_t len) {
#if 0
static struct sockaddr_in toaddr;
memset(&toaddr, 0, sizeof(struct sockaddr_in));
toaddr.sin_family = AF_INET;
toaddr.sin_port = from->sin_port;
toaddr.sin_addr.s_addr = inet_addr(LLMNR_ADDR);
return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in));
#else
return sendto(fd, data, len, 0, (struct sockaddr *)from,
sizeof(struct sockaddr_in));
#endif
}
// populate the specified list which matches the RR name and type
// type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) {
int num_ans = 0;
struct rr_entry *rr;
// check if we have the records
pthread_mutex_lock(&svr->data_lock);
@ -180,18 +186,22 @@ static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t
return num_ans;
}
// decide which records should go into answers
struct rr_list *n = ans_grp->rr;
for (; n; n = n->next) {
// exclude NSEC for RR_ANY
if (type == RR_ANY && n->e->type == RR_NSEC)
continue;
// include all records?
if (type == RR_ANY) {
struct rr_list *n = ans_grp->rr;
for (; n; n = n->next) {
// exclude NSEC
if (n->e->type == RR_NSEC)
continue;
if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
num_ans += rr_list_append(rr_head, n->e);
}
} else {
// match record type
rr = rr_entry_find(ans_grp->rr, name, type);
if (rr)
num_ans += rr_list_append(rr_head, rr);
}
pthread_mutex_unlock(&svr->data_lock);
return num_ans;
@ -206,7 +216,10 @@ static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_
case RR_PTR:
// target host A, AAAA records
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
(ans->data.PTR.name ?
ans->data.PTR.name :
ans->data.PTR.entry->name),
RR_ANY);
break;
case RR_SRV:
@ -272,10 +285,8 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns
struct rr_list *qnl = pkt->rr_qn;
for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
struct rr_entry *qn = qnl->e;
int num_ans_added = 0;
char *namestr = nlabel_to_str(qn->name);
DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr);
DEBUG_PRINTF("qn #%d: type 0x%02x %s - ", i, qn->type, namestr);
free(namestr);
// check if it's a unicast query - we ignore those
@ -284,41 +295,16 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns
continue;
}
num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
reply->num_ans_rr += num_ans_added;
DEBUG_PRINTF("added %d answers\n", num_ans_added);
}
// remove our replies if they were already in their answers
struct rr_list *ans = NULL, *prev_ans = NULL;
for (ans = reply->rr_ans; ans; ) {
struct rr_list *next_ans = ans->next;
struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
// discard answers that have at least half of the actual TTL
if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
char *namestr = nlabel_to_str(ans->e->name);
DEBUG_PRINTF("removing answer for %s\n", namestr);
free(namestr);
// check if list item is head
if (prev_ans == NULL)
reply->rr_ans = ans->next;
else
prev_ans->next = ans->next;
free(ans);
ans = prev_ans;
// adjust answer count
reply->num_ans_rr--;
// see if it is in the answers
if (rr_entry_find(pkt->rr_ans, qn->name, qn->type)) {
DEBUG_PRINTF("our record is already in their answers\n");
continue;
}
prev_ans = ans;
ans = next_ans;
}
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
DEBUG_PRINTF("adding %d answers\n", reply->num_ans_rr);
}
// see if we can match additional records for answers
add_related_rr(svr, reply->rr_ans, reply);
@ -334,104 +320,39 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns
return 0;
}
int create_pipe(int handles[2]) {
#ifdef _WIN32
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
return -1;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(0);
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if (listen(sock, 1) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
int len = sizeof(serv_addr);
if (getsockname(sock, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
closesocket(sock);
return -1;
}
if (connect(handles[1], (struct sockaddr*)&serv_addr, len) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if ((handles[0] = accept(sock, (struct sockaddr*)&serv_addr, &len)) == INVALID_SOCKET) {
closesocket((SOCKET)handles[1]);
handles[1] = INVALID_SOCKET;
closesocket(sock);
return -1;
}
closesocket(sock);
return 0;
#else
return pipe(handles);
#endif
}
int read_pipe(int s, char* buf, int len) {
#ifdef _WIN32
int ret = recv(s, buf, len, 0);
if (ret < 0 && WSAGetLastError() == WSAECONNRESET) {
ret = 0;
}
return ret;
#else
return read(s, buf, len);
#endif
}
int write_pipe(int s, char* buf, int len) {
#ifdef _WIN32
return send(s, buf, len, 0);
#else
return write(s, buf, len);
#endif
}
int close_pipe(int s) {
#ifdef _WIN32
return closesocket(s);
#else
return close(s);
#endif
}
// main loop to receive, process and send out MDNS replies
// also handles MDNS service announces
static void main_loop(struct mdnsd *svr) {
fd_set sockfd_set;
int max_fd = svr->sockfd;
char notify_buf[2]; // buffer for reading of notify_pipe
void *pkt_buffer = malloc(PACKET_SIZE);
if (svr->notify_pipe[0] > max_fd)
max_fd = svr->notify_pipe[0];
if (svr->llmnrfd > max_fd)
max_fd = svr->llmnrfd;
struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt));
memset(mdns_reply, 0, sizeof(struct mdns_pkt));
while (! svr->stop_flag) {
struct timeval tv = {
.tv_sec = 3600,
.tv_usec = 0,
};
FD_ZERO(&sockfd_set);
FD_SET(svr->sockfd, &sockfd_set);
FD_SET(svr->llmnrfd, &sockfd_set);
FD_SET(svr->notify_pipe[0], &sockfd_set);
select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
select(max_fd + 1, &sockfd_set, NULL, NULL, &tv);
if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
// flush the notify_pipe
read_pipe(svr->notify_pipe[0], (char*)&notify_buf, 1);
} else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
read(svr->notify_pipe[0], pkt_buffer, PACKET_SIZE);
}
if (FD_ISSET(svr->sockfd, &sockfd_set)) {
struct sockaddr_in fromaddr;
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
@ -447,8 +368,28 @@ static void main_loop(struct mdnsd *svr) {
if (process_mdns_pkt(svr, mdns, mdns_reply)) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
send_packet(svr->sockfd, pkt_buffer, replylen);
} else if (mdns->num_qn == 0) {
DEBUG_PRINTF("(no questions in packet)\n\n");
}
mdns_pkt_destroy(mdns);
}
}
if (FD_ISSET(svr->llmnrfd, &sockfd_set)) {
struct sockaddr_in fromaddr;
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
ssize_t recvsize = recvfrom(svr->llmnrfd, pkt_buffer, PACKET_SIZE, 0,
(struct sockaddr *) &fromaddr, &sockaddr_size);
if (recvsize < 0) {
log_message(LOG_ERR, "recv(): %m");
}
DEBUG_PRINTF("LLMNR data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize);
struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize);
if (mdns != NULL) {
if (process_mdns_pkt(svr, mdns, mdns_reply)) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
send_llmnr_packet(svr->llmnrfd, &fromaddr, pkt_buffer, replylen);
}
mdns_pkt_destroy(mdns);
@ -505,7 +446,8 @@ static void main_loop(struct mdnsd *svr) {
free(pkt_buffer);
close_pipe(svr->sockfd);
close(svr->sockfd);
close(svr->llmnrfd);
svr->stop_flag = 2;
}
@ -514,28 +456,32 @@ static void main_loop(struct mdnsd *svr) {
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) {
struct rr_entry *a_e = NULL,
*nsec_e = NULL;
struct rr_entry *a_e, *nsec_e;
struct rr_entry *al_e, *nsecl_e;
char hostnamel[0x100];
// currently can't be called twice
// dont ask me what happens if the IP changes
assert(svr->hostname == NULL);
sprintf(hostnamel, "%s.local", hostname);
a_e = rr_create_a(create_nlabel(hostname), ip);
a_e->cache_flush = 0;
//nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
//rr_set_nsec(nsec_e, RR_A);
nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
rr_set_nsec(nsec_e, RR_A);
al_e = rr_create_a(create_nlabel(hostnamel), ip);
nsecl_e = rr_create(create_nlabel(hostnamel), RR_NSEC);
rr_set_nsec(nsecl_e, RR_A);
pthread_mutex_lock(&svr->data_lock);
svr->hostname = create_nlabel(hostname);
svr->hostname = create_nlabel(hostnamel);
rr_group_add(&svr->group, a_e);
rr_group_add(&svr->group, nsec_e);
pthread_mutex_unlock(&svr->data_lock);
}
void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
pthread_mutex_lock(&svr->data_lock);
rr_group_add(&svr->group, rr);
//rr_group_add(&svr->group, nsec_e);
rr_group_add(&svr->group, al_e);
rr_group_add(&svr->group, nsecl_e);
pthread_mutex_unlock(&svr->data_lock);
}
@ -601,7 +547,7 @@ struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_
free(inst_nlabel);
// notify server
write_pipe(svr->notify_pipe[1], ".", 1);
write(svr->notify_pipe[1], ".", 1);
return service;
}
@ -619,19 +565,26 @@ struct mdnsd *mdnsd_start() {
struct mdnsd *server = malloc(sizeof(struct mdnsd));
memset(server, 0, sizeof(struct mdnsd));
if (create_pipe(server->notify_pipe) != 0) {
if (pipe(server->notify_pipe) != 0) {
log_message(LOG_ERR, "pipe(): %m\n");
free(server);
return NULL;
}
server->sockfd = create_recv_sock();
server->sockfd = create_recv_sock(MDNS_ADDR, MDNS_PORT);
if (server->sockfd < 0) {
log_message(LOG_ERR, "unable to create recv socket");
free(server);
return NULL;
}
server->llmnrfd = create_recv_sock(LLMNR_ADDR, LLMNR_PORT);
if (server->llmnrfd < 0) {
log_message(LOG_ERR, "unable to create llmnr recv socket");
free(server);
return NULL;
}
pthread_mutex_init(&server->data_lock, NULL);
// init thread
@ -656,13 +609,13 @@ void mdnsd_stop(struct mdnsd *s) {
};
s->stop_flag = 1;
write_pipe(s->notify_pipe[1], ".", 1);
write(s->notify_pipe[1], ".", 1);
while (s->stop_flag != 2)
select(0, NULL, NULL, NULL, &tv);
close_pipe(s->notify_pipe[0]);
close_pipe(s->notify_pipe[1]);
close(s->notify_pipe[0]);
close(s->notify_pipe[1]);
pthread_mutex_destroy(&s->data_lock);
rr_group_destroy(s->group);

View File

@ -44,9 +44,6 @@ void mdnsd_stop(struct mdnsd *s);
// sets the hostname for the given MDNS responder instance
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip);
// adds an additional RR
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[]);

View File

@ -1,96 +1,93 @@
/*
* 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.
*/
#ifdef _WIN32
#include <winsock2.h>
#include <in6addr.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include "mdns.h"
#include "mdnsd.h"
int main(int argc, char *argv[]) {
// create host entries
char *hostname = "some-random-host.local";
void
background()
{
int fd;
switch ((int)fork())
{
case -1:
perror("fork");
exit(1);
case 0:
break;
default:
exit(0);
}
#ifdef TIOCNOTTY
if (ioctl(fileno(stdin), TIOCNOTTY) == -1)
perror("detach_console");
#endif
freopen("/dev/null", "r", stdin);
fd = open("/dev/null", O_WRONLY, 0666);
dup2(fd, fileno(stdout));
dup2(fd, fileno(stderr));
close(fd);
setsid();
}
int
main(int argc, char **argv)
{
char hostname[0x100], ifname[0x100];
struct hostent *hp;
struct in_addr saddr;
if (gethostname(hostname, sizeof(hostname) - 1) == -1)
{
printf("Couldn't retrieve humax hostname.\n");
return 1;
}
if (!(hp = gethostbyname(hostname)))
{
printf("Couldn't retrieve humax IP address.\n");
return 1;
}
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");
printf("mdnsd_start OK. press ENTER to add hostname & service\n");
getchar();
mdnsd_set_hostname(svr, hostname, saddr.s_addr);
mdnsd_set_hostname(svr, hostname, inet_addr("192.168.0.29"));
struct rr_entry *a2_e = NULL;
a2_e = rr_create_a(create_nlabel(hostname), inet_addr("192.168.0.31"));
mdnsd_add_rr(svr, a2_e);
struct rr_entry *aaaa_e = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo* results;
getaddrinfo(
"fe80::e2f8:47ff:fe20:28e0",
NULL,
&hints,
&results);
struct sockaddr_in6* addr = (struct sockaddr_in6*)results->ai_addr;
struct in6_addr v6addr = addr->sin6_addr;
freeaddrinfo(results);
aaaa_e = rr_create_aaaa(create_nlabel(hostname), &v6addr);
mdnsd_add_rr(svr, aaaa_e);
sprintf(ifname, "Humax Fox T2 (%s) Web Interface", hostname);
const char *txt[] = {
"path=/mywebsite",
"path=/",
NULL
};
struct mdns_service *svc = mdnsd_register_svc(svr, "My Website",
"_http._tcp.local", 8080, NULL, txt);
mdns_service_destroy(svc);
struct mdns_service *svc = mdnsd_register_svc(
svr, ifname, "_http._tcp.local", 80,
NULL, txt);
//mdns_service_destroy(svc);
printf("added service and hostname. press ENTER to exit\n");
getchar();
printf("Registered name.\n");
mdnsd_stop(svr);
for (;;)
sleep(3600);
// mdnsd_stop(svr);
return 0;
}