| 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; |
| +} |