blob: f3f64c1d35f030b3c182a199043032c3fc5f3540 [file] [log] [blame]
diff --git a/avahi-core/core.h b/avahi-core/core.h
index 3fba55e..380bf60 100644
--- a/avahi-core/core.h
+++ b/avahi-core/core.h
@@ -79,6 +79,14 @@ AvahiServer *avahi_server_new(
void* userdata, /**< An opaque pointer which is passed to the callback function */
int *error);
+/** Allocate a new mDNS responder object. - Test only */
+AvahiServer *avahi_server_unreg_running(
+ const AvahiPoll *poll_api,
+ const AvahiServerConfig *sc,
+ AvahiServerCallback callback,
+ void* userdata,
+ int *error);
+
/** Free an mDNS responder object */
void avahi_server_free(AvahiServer* s);
diff --git a/avahi-core/server.c b/avahi-core/server.c
index a2580e3..316b0ac 100644
--- a/avahi-core/server.c
+++ b/avahi-core/server.c
@@ -882,7 +882,7 @@ static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const
assert(a);
/* If it isn't the MDNS port it can't be generated by us */
- if (port != AVAHI_MDNS_PORT)
+ if (port != get_mdns_port())
return 0;
return avahi_interface_has_address(s->monitor, iface, a);
@@ -936,7 +936,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddres
* AR section completely here, so far. Until the day we add
* EDNS0 support. */
- if (port != AVAHI_MDNS_PORT) {
+ if (port != get_mdns_port()) {
/* Legacy Unicast */
if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
@@ -963,7 +963,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddres
} else {
char t[AVAHI_ADDRESS_STR_MAX];
- if (port != AVAHI_MDNS_PORT) {
+ if (port != get_mdns_port()) {
avahi_log_debug("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
return;
}
@@ -1365,7 +1365,8 @@ static int setup_sockets(AvahiServer *s) {
return 0;
}
-AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
+
+AvahiServer * avahi_server_unregistered(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void *userdata, int *error) {
AvahiServer *s;
int e;
@@ -1455,6 +1456,19 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
s->monitor = avahi_interface_monitor_new(s);
avahi_interface_monitor_sync(s->monitor);
+ return s;
+}
+
+//Function skips the registering part of the code to save time for fuzzing
+AvahiServer * avahi_server_unreg_running(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void *userdata, int *error) {
+ AvahiServer *s = avahi_server_unregistered(poll_api, sc, callback, userdata, error);
+ server_set_state(s, AVAHI_SERVER_RUNNING);
+ return s;
+}
+
+AvahiServer * avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void *userdata, int *error) {
+ AvahiServer *s = avahi_server_unregistered(poll_api, sc, callback, userdata, error);
+
register_localhost(s);
register_stuff(s);
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
index 41d9133..d14fb22 100644
--- a/avahi-core/socket.c
+++ b/avahi-core/socket.c
@@ -64,12 +64,14 @@
#endif
#endif
+static int avahi_mdns_port = 5353;
+
static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
assert(ret_sa);
memset(ret_sa, 0, sizeof(struct sockaddr_in));
ret_sa->sin_family = AF_INET;
- ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
+ ret_sa->sin_port = htons(get_mdns_port());
inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
}
@@ -78,7 +80,7 @@ static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
memset(ret_sa, 0, sizeof(struct sockaddr_in6));
ret_sa->sin6_family = AF_INET6;
- ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
+ ret_sa->sin6_port = htons(get_mdns_port());
inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
}
@@ -338,7 +340,7 @@ int avahi_open_socket_ipv4(int no_reuse) {
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
- local.sin_port = htons(AVAHI_MDNS_PORT);
+ local.sin_port = htons(get_mdns_port());
if (no_reuse)
r = bind(fd, (struct sockaddr*) &local, sizeof(local));
@@ -351,6 +353,17 @@ int avahi_open_socket_ipv4(int no_reuse) {
if (ipv4_pktinfo (fd) < 0)
goto fail;
+ if (get_mdns_port() == 0) {
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(sin);
+ if (getsockname(fd, (struct sockaddr *) &sin, &len) == -1) {
+ perror("getsockname");
+ }
+ else {
+ set_mdns_port(ntohs(sin.sin_port));
+ }
+ }
+
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
@@ -408,7 +421,7 @@ int avahi_open_socket_ipv6(int no_reuse) {
memset(&local, 0, sizeof(local));
local.sin6_family = AF_INET6;
- local.sin6_port = htons(AVAHI_MDNS_PORT);
+ local.sin6_port = htons(get_mdns_port());
if (no_reuse)
r = bind(fd, (struct sockaddr*) &local, sizeof(local));
@@ -421,6 +434,17 @@ int avahi_open_socket_ipv6(int no_reuse) {
if (ipv6_pktinfo(fd) < 0)
goto fail;
+ if (get_mdns_port() == 0) { // If port was zero, it was ephemeral and now needs to change to the actual (testing only)
+ struct sockaddr_in6 sin;
+ socklen_t len = sizeof(sin);
+ if (getsockname(fd, (struct sockaddr *) &sin, &len) == -1) {
+ perror("getsockname");
+ }
+ else {
+ set_mdns_port(ntohs(sin.sin6_port));
+ }
+ }
+
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
@@ -785,6 +809,14 @@ fail:
return NULL;
}
+void set_mdns_port(int port) {
+ avahi_mdns_port = port;
+}
+
+int get_mdns_port(void) {
+ return avahi_mdns_port;
+}
+
AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
int fd,
AvahiIPv6Address *ret_src_address,
diff --git a/avahi-core/socket.h b/avahi-core/socket.h
index 92f12d7..7e2f1ba 100644
--- a/avahi-core/socket.h
+++ b/avahi-core/socket.h
@@ -24,11 +24,13 @@
#include "dns.h"
-#define AVAHI_MDNS_PORT 5353
#define AVAHI_DNS_PORT 53
#define AVAHI_IPV4_MCAST_GROUP "224.0.0.251"
#define AVAHI_IPV6_MCAST_GROUP "ff02::fb"
+void set_mdns_port (int port);
+int get_mdns_port(void);
+
int avahi_open_socket_ipv4(int no_reuse);
int avahi_open_socket_ipv6(int no_reuse);
diff --git a/configure.ac b/configure.ac
index 76f201e..b518e0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1093,6 +1093,20 @@ AC_ARG_ENABLE(tests,
AM_CONDITIONAL([ENABLE_TESTS], [test "x$ENABLE_TESTS" = "xyes"])
+#
+# Conditionally compile avahi fuzzer
+#
+AC_ARG_ENABLE(fuzzer,
+ AS_HELP_STRING([--enable-fuzzer],[Enable building of Fuzzer]),
+ [case "${enableval}" in
+ yes) ENABLE_FUZZER=yes ;;
+ no) ENABLE_FUZZER=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-fuzzer) ;;
+ esac],
+ [ENABLE_FUZZER=no])
+
+AM_CONDITIONAL([ENABLE_FUZZER], [test "x$ENABLE_FUZZER" = "xyes"])
+
#
# Optionally enable libdns_sd compatibility support
#
diff --git a/examples/Makefile.am b/examples/Makefile.am
index dbb7f00..376e856 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -31,6 +31,24 @@ core_browse_services_SOURCES = core-browse-services.c
core_browse_services_CFLAGS = $(AM_CFLAGS)
core_browse_services_LDADD = $(AM_LDADD) ../avahi-core/libavahi-core.la ../avahi-common/libavahi-common.la
+
+if ENABLE_FUZZER
+if ENABLE_TESTS
+noinst_PROGRAMS += \
+ client-publish-service \
+ client-browse-services \
+ recv_fuzzer
+else
+noinst_PROGRAMS = recv_fuzzer
+endif
+
+recv_fuzzer_SOURCES = recv_fuzzer.c
+recv_fuzzer_CFLAGS = $(AM_CFLAGS) -fsanitize=fuzzer
+recv_fuzzer_LDADD = $(AM_LDADD) ../avahi-core/libavahi-core.la ../avahi-common/libavahi-common.la $(LIBDAEMON_LIBS) $(XML_LIBS)
+recv_fuzzer_LDFLAGS = -fsanitize=fuzzer
+
+endif
+
if HAVE_DBUS
if ENABLE_TESTS
diff --git a/examples/recv_fuzzer.c b/examples/recv_fuzzer.c
new file mode 100644
index 0000000..0d626e7
--- /dev/null
+++ b/examples/recv_fuzzer.c
@@ -0,0 +1,200 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <avahi-common/alternative.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/simple-watch.h>
+
+#include <avahi-core/core.h>
+#include <avahi-core/dns-srv-rr.h>
+#include <avahi-core/log.h>
+#include <avahi-core/lookup.h>
+#include <avahi-core/publish.h>
+#include <avahi-core/socket.h>
+
+AvahiServer *server;
+const uint8_t *data;
+static size_t size = sizeof(data);
+
+static int reuseaddr(int fd) {
+ int yes = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
+ return -1;
+ }
+
+#ifdef SO_REUSEPORT
+ yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno));
+ if (errno != ENOPROTOOPT)
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+static void send_server_message_ipv4(int same_port) {
+ assert (same_port == 1 || same_port == 0);
+
+ int sock = -1;
+ unsigned char set = 1;
+ struct sockaddr_in local;
+ struct sockaddr_in destAddr;
+ memset(&local, 0, sizeof(local));
+ memset(&destAddr, 0, sizeof(destAddr));
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ unsigned short port = get_mdns_port() * same_port; //0 for random port
+ local.sin_family = AF_INET;
+ local.sin_port = htons(port);
+
+
+ destAddr.sin_port = htons(get_mdns_port());
+ destAddr.sin_family = AF_INET;
+ destAddr.sin_addr.s_addr = inet_addr(AVAHI_IPV4_MCAST_GROUP);
+
+ if (sock < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ exit(1);
+ }
+ reuseaddr(sock);
+
+ if (bind(sock, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ return;
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set)) < 0) {
+ avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
+ }
+
+ ssize_t s;
+ if ((s = sendto(sock, data, size, 0, (struct sockaddr *) &destAddr, sizeof(destAddr))) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ close(sock);
+
+}
+
+static void send_server_message_ipv6(int same_port) {
+
+ assert (same_port == 1 || same_port == 0);
+
+ int sock = -1;
+ int port = get_mdns_port() * same_port;
+
+ struct sockaddr_in6 local;
+ struct sockaddr_in6 destAddr;
+ memset(&local, 0, sizeof(local));
+ memset(&destAddr, 0, sizeof(local));
+
+ local.sin6_family = AF_INET6;
+ local.sin6_port = htons(port);
+
+ sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ destAddr.sin6_family = AF_INET6;
+ destAddr.sin6_port = htons(get_mdns_port());
+ inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &destAddr.sin6_addr);
+
+ if (sock < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ reuseaddr(sock);
+ if (bind(sock, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ return;
+ }
+
+ ssize_t s;
+ if ((s = sendto(sock, data, size, 0, (struct sockaddr *) &destAddr, sizeof(destAddr))) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ close(sock);
+}
+
+static void send_server_message(int same_port) {
+ send_server_message_ipv4(same_port); //Send IPv4 Packet
+ send_server_message_ipv6(same_port); //Send IPv6 Packet
+}
+
+static void quit_timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) {
+ AvahiSimplePoll *simple_poll = userdata;
+ avahi_simple_poll_quit(simple_poll);
+}
+
+static void server_callback(AvahiServer *s,
+ AvahiServerState state,
+ AVAHI_GCC_UNUSED AVAHI_GCC_UNUSED void *userdata) {
+ server = s;
+
+ if (state == AVAHI_SERVER_RUNNING) {
+ send_server_message(1);
+ send_server_message(0);
+ } else if (state == AVAHI_SERVER_COLLISION) {
+ /*Handle Collision of Server Interface*/
+ char *n;
+ n = avahi_alternative_host_name(avahi_server_get_host_name(s));
+ avahi_server_set_host_name(s, n);
+ avahi_free(n);
+ }
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ data = Data;
+ size = Size;
+
+ const AvahiPoll *poll_api;
+ AvahiServerConfig config;
+ AvahiSimplePoll *simple_poll;
+ int error;
+
+ simple_poll = avahi_simple_poll_new();
+ poll_api = avahi_simple_poll_get(simple_poll);
+
+ avahi_server_config_init(&config);
+ config.disallow_other_stacks = 0;
+ config.enable_reflector = 1;
+
+ server = avahi_server_unreg_running(poll_api, &config, server_callback, NULL, &error);
+
+ struct timeval tv;
+ avahi_elapse_time(&tv, 5, 0);
+ poll_api->timeout_new(poll_api, &tv, quit_timeout_callback, simple_poll);
+
+ avahi_simple_poll_loop(simple_poll);
+
+ avahi_server_config_free(&config);
+
+ if (server)
+ avahi_server_free(server);
+
+ if (simple_poll)
+ avahi_simple_poll_free(simple_poll);
+
+ return 0;
+}