#ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS 1 #endif #include #include #include #ifdef _WIN32 #include #include #include #define sleep(x) Sleep(x * 1000) #else #include #include #include #include #endif // Alias some things to simulate recieving data to fuzz library #if defined(MDNS_FUZZING) #define recvfrom(sock, buffer, capacity, flags, src_addr, addrlen) ((mdns_ssize_t)capacity) #define printf #endif #include "mdns.h" #if defined(MDNS_FUZZING) #undef recvfrom #endif static char addrbuffer[64]; static char entrybuffer[256]; static char namebuffer[256]; static char sendbuffer[1024]; static mdns_record_txt_t txtbuffer[128]; static struct sockaddr_in service_address_ipv4; static struct sockaddr_in6 service_address_ipv6; static int has_ipv4; static int has_ipv6; volatile sig_atomic_t running = 1; // Data for our service including the mDNS records typedef struct { mdns_string_t service; mdns_string_t hostname; mdns_string_t service_instance; mdns_string_t hostname_qualified; struct sockaddr_in address_ipv4; struct sockaddr_in6 address_ipv6; int port; mdns_record_t record_ptr; mdns_record_t record_srv; mdns_record_t record_a; mdns_record_t record_aaaa; mdns_record_t txt_record[2]; } service_t; typedef struct { int port; char ip[32]; char domain[32]; }server_info_t; static mdns_string_t ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr, size_t addrlen) { char host[NI_MAXHOST] = {0}; char service[NI_MAXSERV] = {0}; int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST); int len = 0; if (ret == 0) { if (addr->sin_port != 0) len = snprintf(buffer, capacity, "%s:%s", host, service); else len = snprintf(buffer, capacity, "%s", host); } if (len >= (int)capacity) len = (int)capacity - 1; mdns_string_t str; str.str = buffer; str.length = len; return str; } static mdns_string_t ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr, size_t addrlen) { char host[NI_MAXHOST] = {0}; char service[NI_MAXSERV] = {0}; int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST); int len = 0; if (ret == 0) { if (addr->sin6_port != 0) len = snprintf(buffer, capacity, "[%s]:%s", host, service); else len = snprintf(buffer, capacity, "%s", host); } if (len >= (int)capacity) len = (int)capacity - 1; mdns_string_t str; str.str = buffer; str.length = len; return str; } static mdns_string_t ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr, size_t addrlen) { if (addr->sa_family == AF_INET6) return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr, addrlen); return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr, addrlen); } // Callback handling parsing answers to queries sent static int query_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, size_t name_length, size_t record_offset, size_t record_length, void* user_data) { (void)sizeof(sock); (void)sizeof(query_id); (void)sizeof(name_length); (void)sizeof(user_data); server_info_t *server_info = (server_info_t *)user_data; mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from, addrlen); const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" : ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); mdns_string_t entrystr = mdns_string_extract(data, size, &name_offset, entrybuffer, sizeof(entrybuffer)); if (rtype == MDNS_RECORDTYPE_PTR) { mdns_string_t namestr = mdns_record_parse_ptr(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); printf("%.*s : %s %.*s PTR %.*s rclass 0x%x ttl %u length %d\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(namestr), rclass, ttl, (int)record_length); } else if (rtype == MDNS_RECORDTYPE_SRV) { mdns_record_srv_t srv = mdns_record_parse_srv(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); server_info->port = srv.port; memcpy(server_info->domain, srv.name.str, srv.name.length - 1); printf("%.*s : %s %.*s SRV %.*s priority %d weight %d port %d\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); } else if (rtype == MDNS_RECORDTYPE_A) { struct sockaddr_in addr; mdns_record_parse_a(data, size, record_offset, record_length, &addr); mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); memcpy(server_info->ip, addrstr.str, addrstr.length); printf("%.*s : %s %.*s A %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr)); } else if (rtype == MDNS_RECORDTYPE_AAAA) { struct sockaddr_in6 addr; mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr); mdns_string_t addrstr = ipv6_address_to_string(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); printf("%.*s : %s %.*s AAAA %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr)); } else if (rtype == MDNS_RECORDTYPE_TXT) { size_t parsed = mdns_record_parse_txt(data, size, record_offset, record_length, txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t)); for (size_t itxt = 0; itxt < parsed; ++itxt) { if (txtbuffer[itxt].value.length) { printf("%.*s : %s %.*s TXT %.*s = %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(txtbuffer[itxt].key), MDNS_STRING_FORMAT(txtbuffer[itxt].value)); } else { printf("%.*s : %s %.*s TXT %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(txtbuffer[itxt].key)); } } } else { printf("%.*s : %s %.*s type %u rclass 0x%x ttl %u length %d\n", MDNS_STRING_FORMAT(fromaddrstr), entrytype, MDNS_STRING_FORMAT(entrystr), rtype, rclass, ttl, (int)record_length); } return 0; } // Callback handling questions incoming on service sockets static int service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, size_t name_length, size_t record_offset, size_t record_length, void* user_data) { (void)sizeof(ttl); if (entry != MDNS_ENTRYTYPE_QUESTION) return 0; const char dns_sd[] = "_services._dns-sd._udp.local."; const service_t* service = (const service_t*)user_data; mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from, addrlen); size_t offset = name_offset; mdns_string_t name = mdns_string_extract(data, size, &offset, namebuffer, sizeof(namebuffer)); const char* record_name = 0; if (rtype == MDNS_RECORDTYPE_PTR) record_name = "PTR"; else if (rtype == MDNS_RECORDTYPE_SRV) record_name = "SRV"; else if (rtype == MDNS_RECORDTYPE_A) record_name = "A"; else if (rtype == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; else if (rtype == MDNS_RECORDTYPE_TXT) record_name = "TXT"; else if (rtype == MDNS_RECORDTYPE_ANY) record_name = "ANY"; else return 0; //printf("Query ---3--- %s %.*s\n", record_name, MDNS_STRING_FORMAT(name)); if ((name.length == (sizeof(dns_sd) - 1)) && (strncmp(name.str, dns_sd, sizeof(dns_sd) - 1) == 0)) { if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) { // The PTR query was for the DNS-SD domain, send answer with a PTR record for the // service name we advertise, typically on the "<_service-name>._tcp.local." format // Answer PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." mdns_record_t answer = { .name = name, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = service->service}; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); printf(" --> answer %.*s (%s)\n", MDNS_STRING_FORMAT(answer.data.ptr.name), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, rtype, name.str, name.length, answer, 0, 0, 0, 0); } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, 0, 0); } } } else if ((name.length == service->service.length) && (strncmp(name.str, service->service.str, name.length) == 0)) { if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) { // The PTR query was for our service (usually "<_service-name._tcp.local"), answer a PTR // record reverse mapping the queried service name to our service instance name // (typically on the ".<_service-name>._tcp.local." format), and add // additional records containing the SRV record mapping the service instance name to our // qualified hostname (typically ".local.") and port, as well as any IPv4/IPv6 // address for the hostname as A/AAAA records, and two test TXT records // Answer PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." mdns_record_t answer = service->record_ptr; mdns_record_t additional[5] = {0}; size_t additional_count = 0; // SRV record mapping ".<_service-name>._tcp.local." to // ".local." with port. Set weight & priority to 0. additional[additional_count++] = service->record_srv; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses if (service->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service->record_a; if (service->address_ipv6.sin6_family == AF_INET6) additional[additional_count++] = service->record_aaaa; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library additional[additional_count++] = service->txt_record[0]; additional[additional_count++] = service->txt_record[1]; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); printf(" --> answer %.*s (%s)\n", MDNS_STRING_FORMAT(service->record_ptr.data.ptr.name), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, rtype, name.str, name.length, answer, 0, 0, additional, additional_count); } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } } } else if ((name.length == service->service_instance.length) && (strncmp(name.str, service->service_instance.str, name.length) == 0)) { if ((rtype == MDNS_RECORDTYPE_SRV) || (rtype == MDNS_RECORDTYPE_ANY)) { // The SRV query was for our service instance (usually // ".<_service-name._tcp.local"), answer a SRV record mapping the service // instance name to our qualified hostname (typically ".local.") and port, as // well as any IPv4/IPv6 address for the hostname as A/AAAA records, and two test TXT // records // Answer PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." mdns_record_t answer = service->record_srv; mdns_record_t additional[5] = {0}; size_t additional_count = 0; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses if (service->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service->record_a; if (service->address_ipv6.sin6_family == AF_INET6) additional[additional_count++] = service->record_aaaa; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library additional[additional_count++] = service->txt_record[0]; additional[additional_count++] = service->txt_record[1]; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); printf(" --> answer %.*s port %d (%s)\n", MDNS_STRING_FORMAT(service->record_srv.data.srv.name), service->port, (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, rtype, name.str, name.length, answer, 0, 0, additional, additional_count); } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } } } else if ((name.length == service->hostname_qualified.length) && (strncmp(name.str, service->hostname_qualified.str, name.length) == 0)) { if (((rtype == MDNS_RECORDTYPE_A) || (rtype == MDNS_RECORDTYPE_ANY)) && (service->address_ipv4.sin_family == AF_INET)) { // The A query was for our qualified hostname (typically ".local.") and we // have an IPv4 address, answer with an A record mappiing the hostname to an IPv4 // address, as well as any IPv6 address for the hostname, and two test TXT records // Answer A records mapping ".local." to IPv4 address mdns_record_t answer = service->record_a; mdns_record_t additional[5] = {0}; size_t additional_count = 0; // AAAA record mapping ".local." to IPv6 addresses if (service->address_ipv6.sin6_family == AF_INET6) additional[additional_count++] = service->record_aaaa; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library additional[additional_count++] = service->txt_record[0]; additional[additional_count++] = service->txt_record[1]; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); mdns_string_t addrstr = ip_address_to_string( addrbuffer, sizeof(addrbuffer), (struct sockaddr*)&service->record_a.data.a.addr, sizeof(service->record_a.data.a.addr)); printf(" --> answer %.*s IPv4 %.*s (%s)\n", MDNS_STRING_FORMAT(service->record_a.name), MDNS_STRING_FORMAT(addrstr), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, rtype, name.str, name.length, answer, 0, 0, additional, additional_count); } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } } else if (((rtype == MDNS_RECORDTYPE_AAAA) || (rtype == MDNS_RECORDTYPE_ANY)) && (service->address_ipv6.sin6_family == AF_INET6)) { // The AAAA query was for our qualified hostname (typically ".local.") and we // have an IPv6 address, answer with an AAAA record mappiing the hostname to an IPv6 // address, as well as any IPv4 address for the hostname, and two test TXT records // Answer AAAA records mapping ".local." to IPv6 address mdns_record_t answer = service->record_aaaa; mdns_record_t additional[5] = {0}; size_t additional_count = 0; // A record mapping ".local." to IPv4 addresses if (service->address_ipv4.sin_family == AF_INET) additional[additional_count++] = service->record_a; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library additional[additional_count++] = service->txt_record[0]; additional[additional_count++] = service->txt_record[1]; // Send the answer, unicast or multicast depending on flag in query uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE); mdns_string_t addrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), (struct sockaddr*)&service->record_aaaa.data.aaaa.addr, sizeof(service->record_aaaa.data.aaaa.addr)); printf(" --> answer %.*s IPv6 %.*s (%s)\n", MDNS_STRING_FORMAT(service->record_aaaa.name), MDNS_STRING_FORMAT(addrstr), (unicast ? "unicast" : "multicast")); if (unicast) { mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer), query_id, rtype, name.str, name.length, answer, 0, 0, additional, additional_count); } else { mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional, additional_count); } } } return 0; } // Callback handling questions and answers dump static int dump_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, size_t name_length, size_t record_offset, size_t record_length, void* user_data) { mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from, addrlen); size_t offset = name_offset; mdns_string_t name = mdns_string_extract(data, size, &offset, namebuffer, sizeof(namebuffer)); const char* record_name = 0; if (rtype == MDNS_RECORDTYPE_PTR) record_name = "PTR"; else if (rtype == MDNS_RECORDTYPE_SRV) record_name = "SRV"; else if (rtype == MDNS_RECORDTYPE_A) record_name = "A"; else if (rtype == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; else if (rtype == MDNS_RECORDTYPE_TXT) record_name = "TXT"; else if (rtype == MDNS_RECORDTYPE_ANY) record_name = "ANY"; else record_name = ""; const char* entry_type = "Question"; if (entry == MDNS_ENTRYTYPE_ANSWER) entry_type = "Answer"; else if (entry == MDNS_ENTRYTYPE_AUTHORITY) entry_type = "Authority"; else if (entry == MDNS_ENTRYTYPE_ADDITIONAL) entry_type = "Additional"; printf("%.*s: %s %s %.*s rclass 0x%x ttl %u\n", MDNS_STRING_FORMAT(fromaddrstr), entry_type, record_name, MDNS_STRING_FORMAT(name), (unsigned int)rclass, ttl); return 0; } // Open sockets for sending one-shot multicast queries from an ephemeral port static int open_client_sockets(int* sockets, int max_sockets, int port) { // When sending, each socket can only send to one network interface // Thus we need to open one socket for each interface and address family int num_sockets = 0; #ifdef _WIN32 IP_ADAPTER_ADDRESSES* adapter_address = 0; ULONG address_size = 8000; unsigned int ret; unsigned int num_retries = 4; do { adapter_address = (IP_ADAPTER_ADDRESSES*)malloc(address_size); ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, 0, adapter_address, &address_size); if (ret == ERROR_BUFFER_OVERFLOW) { free(adapter_address); adapter_address = 0; address_size *= 2; } else { break; } } while (num_retries-- > 0); if (!adapter_address || (ret != NO_ERROR)) { free(adapter_address); printf("Failed to get network adapter addresses\n"); return num_sockets; } int first_ipv4 = 1; int first_ipv6 = 1; for (PIP_ADAPTER_ADDRESSES adapter = adapter_address; adapter; adapter = adapter->Next) { if (adapter->TunnelType == TUNNEL_TYPE_TEREDO) continue; if (adapter->OperStatus != IfOperStatusUp) continue; for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) { if (unicast->Address.lpSockaddr->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*)unicast->Address.lpSockaddr; if ((saddr->sin_addr.S_un.S_un_b.s_b1 != 127) || (saddr->sin_addr.S_un.S_un_b.s_b2 != 0) || (saddr->sin_addr.S_un.S_un_b.s_b3 != 0) || (saddr->sin_addr.S_un.S_un_b.s_b4 != 1)) { int log_addr = 0; if (first_ipv4) { service_address_ipv4 = *saddr; first_ipv4 = 0; log_addr = 1; } has_ipv4 = 1; if (num_sockets < max_sockets) { saddr->sin_port = htons((unsigned short)port); int sock = mdns_socket_open_ipv4(saddr); if (sock >= 0) { sockets[num_sockets++] = sock; log_addr = 1; } else { log_addr = 0; } } if (log_addr) { char buffer[128]; mdns_string_t addr = ipv4_address_to_string(buffer, sizeof(buffer), saddr, sizeof(struct sockaddr_in)); printf("---Local IPv4 address: %.*s\n", MDNS_STRING_FORMAT(addr)); } } } else if (unicast->Address.lpSockaddr->sa_family == AF_INET6) { struct sockaddr_in6* saddr = (struct sockaddr_in6*)unicast->Address.lpSockaddr; // Ignore link-local addresses if (saddr->sin6_scope_id) continue; static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static const unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}; if ((unicast->DadState == NldsPreferred) && memcmp(saddr->sin6_addr.s6_addr, localhost, 16) && memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) { int log_addr = 0; if (first_ipv6) { service_address_ipv6 = *saddr; first_ipv6 = 0; log_addr = 1; } has_ipv6 = 1; if (num_sockets < max_sockets) { saddr->sin6_port = htons((unsigned short)port); int sock = mdns_socket_open_ipv6(saddr); if (sock >= 0) { sockets[num_sockets++] = sock; log_addr = 1; } else { log_addr = 0; } } if (log_addr) { char buffer[128]; mdns_string_t addr = ipv6_address_to_string(buffer, sizeof(buffer), saddr, sizeof(struct sockaddr_in6)); printf("Local IPv6 address: %.*s\n", MDNS_STRING_FORMAT(addr)); } } } } } free(adapter_address); #else struct ifaddrs* ifaddr = 0; struct ifaddrs* ifa = 0; if (getifaddrs(&ifaddr) < 0) printf("Unable to get interface addresses\n"); int first_ipv4 = 1; int first_ipv6 = 1; for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST)) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) || (ifa->ifa_flags & IFF_POINTOPOINT)) continue; if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*)ifa->ifa_addr; if (saddr->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { int log_addr = 0; if (first_ipv4) { service_address_ipv4 = *saddr; first_ipv4 = 0; log_addr = 1; } has_ipv4 = 1; if (num_sockets < max_sockets) { saddr->sin_port = htons(port); int sock = mdns_socket_open_ipv4(saddr); if (sock >= 0) { sockets[num_sockets++] = sock; log_addr = 1; } else { log_addr = 0; } } if (log_addr) { char buffer[128]; mdns_string_t addr = ipv4_address_to_string(buffer, sizeof(buffer), saddr, sizeof(struct sockaddr_in)); printf("Local IPv4 address: %.*s\n", MDNS_STRING_FORMAT(addr)); } } } else if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6* saddr = (struct sockaddr_in6*)ifa->ifa_addr; // Ignore link-local addresses if (saddr->sin6_scope_id) continue; static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static const unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}; if (memcmp(saddr->sin6_addr.s6_addr, localhost, 16) && memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) { int log_addr = 0; if (first_ipv6) { service_address_ipv6 = *saddr; first_ipv6 = 0; log_addr = 1; } has_ipv6 = 1; if (num_sockets < max_sockets) { saddr->sin6_port = htons(port); int sock = mdns_socket_open_ipv6(saddr); if (sock >= 0) { sockets[num_sockets++] = sock; log_addr = 1; } else { log_addr = 0; } } if (log_addr) { char buffer[128]; mdns_string_t addr = ipv6_address_to_string(buffer, sizeof(buffer), saddr, sizeof(struct sockaddr_in6)); printf("Local IPv6 address: %.*s\n", MDNS_STRING_FORMAT(addr)); } } } } freeifaddrs(ifaddr); #endif return num_sockets; } /* // Open sockets to listen to incoming mDNS queries on port 5353 static int open_service_sockets(int* sockets, int max_sockets) { // When recieving, each socket can recieve data from all network interfaces // Thus we only need to open one socket for each address family int num_sockets = 0; // Call the client socket function to enumerate and get local addresses, // but not open the actual sockets open_client_sockets(0, 0, 0); if (num_sockets < max_sockets) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(struct sockaddr_in)); sock_addr.sin_family = AF_INET; #ifdef _WIN32 sock_addr.sin_addr = in4addr_any; #else sock_addr.sin_addr.s_addr = INADDR_ANY; #endif sock_addr.sin_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr.sin_len = sizeof(struct sockaddr_in); #endif int sock = mdns_socket_open_ipv4(&sock_addr); if (sock >= 0) { sockets[num_sockets++] = sock; printf("IPv4 socket opened successfully on port %d\n", MDNS_PORT); } else { printf("Failed to open IPv4 socket on port %d\n", MDNS_PORT); } } if (num_sockets < max_sockets) { struct sockaddr_in6 sock_addr; memset(&sock_addr, 0, sizeof(struct sockaddr_in6)); sock_addr.sin6_family = AF_INET6; sock_addr.sin6_addr = in6addr_any; sock_addr.sin6_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr.sin6_len = sizeof(struct sockaddr_in6); #endif int sock = mdns_socket_open_ipv6(&sock_addr); if (sock >= 0){ sockets[num_sockets++] = sock; printf("IPv6 socket opened successfully on port %d\n", MDNS_PORT); } else { printf("Failed to open IPv6 socket on port %d\n", MDNS_PORT); } } return num_sockets; } */ static int open_service_sockets(int* sockets, int max_sockets, const char* local_ip) { int num_sockets = 0; if (local_ip) { // 如果指定了 local_ip,则绑定到该地址 struct sockaddr_in sock_addr_ipv4; struct sockaddr_in6 sock_addr_ipv6; memset(&sock_addr_ipv4, 0, sizeof(sock_addr_ipv4)); memset(&sock_addr_ipv6, 0, sizeof(sock_addr_ipv6)); if (inet_pton(AF_INET, local_ip, &sock_addr_ipv4.sin_addr)) { sock_addr_ipv4.sin_family = AF_INET; sock_addr_ipv4.sin_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr_ipv4.sin_len = sizeof(struct sockaddr_in); #endif int sock = mdns_socket_open_ipv4(&sock_addr_ipv4); if (sock >= 0) { sockets[num_sockets++] = sock; printf("Bound to specific IPv4 address: %s\n", local_ip); } else { printf("Failed to bind to IPv4 address: %s\n", local_ip); } } else if (inet_pton(AF_INET6, local_ip, &sock_addr_ipv6.sin6_addr)) { sock_addr_ipv6.sin6_family = AF_INET6; sock_addr_ipv6.sin6_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr_ipv6.sin6_len = sizeof(struct sockaddr_in6); #endif int sock = mdns_socket_open_ipv6(&sock_addr_ipv6); if (sock >= 0) { sockets[num_sockets++] = sock; printf("Bound to specific IPv6 address: %s\n", local_ip); } else { printf("Failed to bind to IPv6 address: %s\n", local_ip); } } else { printf("Invalid local IP address: %s\n", local_ip); } } if (num_sockets == 0) { // 如果未指定 local_ip,则绑定到所有接口 if (num_sockets < max_sockets) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; #ifdef _WIN32 sock_addr.sin_addr = in4addr_any; #else sock_addr.sin_addr.s_addr = INADDR_ANY; #endif sock_addr.sin_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr.sin_len = sizeof(struct sockaddr_in); #endif int sock = mdns_socket_open_ipv4(&sock_addr); if (sock >= 0) { sockets[num_sockets++] = sock; printf("IPv4 socket opened successfully on port %d\n", MDNS_PORT); } else { printf("Failed to open IPv4 socket on port %d\n", MDNS_PORT); } } if (num_sockets < max_sockets) { struct sockaddr_in6 sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin6_family = AF_INET6; sock_addr.sin6_addr = in6addr_any; sock_addr.sin6_port = htons(MDNS_PORT); #ifdef __APPLE__ sock_addr.sin6_len = sizeof(struct sockaddr_in6); #endif int sock = mdns_socket_open_ipv6(&sock_addr); if (sock >= 0) { sockets[num_sockets++] = sock; printf("IPv6 socket opened successfully on port %d\n", MDNS_PORT); } else { printf("Failed to open IPv6 socket on port %d\n", MDNS_PORT); } } } return num_sockets; } // Send a DNS-SD query static int send_dns_sd(void) { int sockets[32]; int num_sockets = open_client_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), 0); if (num_sockets <= 0) { printf("Failed to open any client sockets\n"); return -1; } printf("Opened %d socket%s for DNS-SD\n", num_sockets, num_sockets > 1 ? "s" : ""); printf("Sending DNS-SD discovery\n"); for (int isock = 0; isock < num_sockets; ++isock) { if (mdns_discovery_send(sockets[isock])) printf("Failed to send DNS-DS discovery: %s\n", strerror(errno)); } size_t capacity = 2048; void* buffer = malloc(capacity); void* user_data = 0; size_t records; // This is a simple implementation that loops for 5 seconds or as long as we get replies int res; printf("Reading DNS-SD replies\n"); do { struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; int nfds = 0; fd_set readfs; FD_ZERO(&readfs); for (int isock = 0; isock < num_sockets; ++isock) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } records = 0; res = select(nfds, &readfs, 0, 0, &timeout); if (res > 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { records += mdns_discovery_recv(sockets[isock], buffer, capacity, query_callback, user_data); } } } } while (res > 0); free(buffer); for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); printf("Closed socket%s\n", num_sockets ? "s" : ""); return 0; } // Send a mDNS query static int send_mdns_query(mdns_query_t* query, size_t count, void* user_data) { int sockets[32]; int query_id[32]; int num_sockets = open_client_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), 0); if (num_sockets <= 0) { printf("Failed to open any client sockets\n"); return -1; } printf("Opened %d socket%s for mDNS query\n", num_sockets, num_sockets ? "s" : ""); size_t capacity = 2048; void* buffer = malloc(capacity); //void* user_data = 0; printf("Sending mDNS query"); for (size_t iq = 0; iq < count; ++iq) { const char* record_name = "PTR"; if (query[iq].type == MDNS_RECORDTYPE_SRV) record_name = "SRV"; else if (query[iq].type == MDNS_RECORDTYPE_A) record_name = "A"; else if (query[iq].type == MDNS_RECORDTYPE_AAAA) record_name = "AAAA"; else query[iq].type = MDNS_RECORDTYPE_PTR; printf(" : %s %s", query[iq].name, record_name); } printf("\n"); for (int isock = 0; isock < num_sockets; ++isock) { query_id[isock] = mdns_multiquery_send(sockets[isock], query, count, buffer, capacity, 0); if (query_id[isock] < 0) printf("Failed to send mDNS query: %s\n", strerror(errno)); } // This is a simple implementation that loops for 5 seconds or as long as we get replies int res; printf("Reading mDNS query replies\n"); int records = 0; do { struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; int nfds = 0; fd_set readfs; FD_ZERO(&readfs); for (int isock = 0; isock < num_sockets; ++isock) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } res = select(nfds, &readfs, 0, 0, &timeout); if (res > 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { size_t rec = mdns_query_recv(sockets[isock], buffer, capacity, query_callback, user_data, query_id[isock]); if (rec > 0) records += rec; } FD_SET(sockets[isock], &readfs); } } } while (res > 0); printf("Read %d records\n", records); free(buffer); for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); printf("Closed socket%s\n", num_sockets ? "s" : ""); return 0; } // Provide a mDNS service, answering incoming DNS-SD and mDNS queries /* int service_mdns(const char* hostname, const char* service_name, int service_port, const char* local_ip) { int sockets[32]; //int num_sockets = open_service_sockets(sockets, sizeof(sockets) / sizeof(sockets[0])); int num_sockets = open_service_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), local_ip); if (num_sockets <= 0) { printf("Failed to open any client sockets\n"); return -1; } printf("Opened %d socket%s for mDNS service\n", num_sockets, num_sockets ? "s" : ""); size_t service_name_length = strlen(service_name); if (!service_name_length) { printf("Invalid service name\n"); return -1; } char* service_name_buffer = malloc(service_name_length + 2); memcpy(service_name_buffer, service_name, service_name_length); if (service_name_buffer[service_name_length - 1] != '.') service_name_buffer[service_name_length++] = '.'; service_name_buffer[service_name_length] = 0; service_name = service_name_buffer; printf("Service mDNS: %s:%d\n", service_name, service_port); printf("Hostname: %s\n", hostname); size_t capacity = 2048; void* buffer = malloc(capacity); mdns_string_t service_string = (mdns_string_t){service_name, strlen(service_name)}; mdns_string_t hostname_string = (mdns_string_t){hostname, strlen(hostname)}; // Build the service instance ".<_service-name>._tcp.local." string char service_instance_buffer[256] = {0}; snprintf(service_instance_buffer, sizeof(service_instance_buffer) - 1, "%.*s.%.*s", MDNS_STRING_FORMAT(hostname_string), MDNS_STRING_FORMAT(service_string)); mdns_string_t service_instance_string = (mdns_string_t){service_instance_buffer, strlen(service_instance_buffer)}; // Build the ".local." string char qualified_hostname_buffer[256] = {0}; snprintf(qualified_hostname_buffer, sizeof(qualified_hostname_buffer) - 1, "%.*s.local.", MDNS_STRING_FORMAT(hostname_string)); mdns_string_t hostname_qualified_string = (mdns_string_t){qualified_hostname_buffer, strlen(qualified_hostname_buffer)}; service_t service = {0}; service.service = service_string; service.hostname = hostname_string; service.service_instance = service_instance_string; service.hostname_qualified = hostname_qualified_string; service.address_ipv4 = service_address_ipv4; service.address_ipv6 = service_address_ipv6; service.port = service_port; // Setup our mDNS records // PTR record reverse mapping "<_service-name>._tcp.local." to // ".<_service-name>._tcp.local." service.record_ptr = (mdns_record_t){.name = service.service, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = service.service_instance, .rclass = 0, .ttl = 0}; // SRV record mapping ".<_service-name>._tcp.local." to // ".local." with port. Set weight & priority to 0. service.record_srv = (mdns_record_t){.name = service.service_instance, .type = MDNS_RECORDTYPE_SRV, .data.srv.name = service.hostname_qualified, .data.srv.port = service.port, .data.srv.priority = 0, .data.srv.weight = 0, .rclass = 0, .ttl = 0}; // A/AAAA records mapping ".local." to IPv4/IPv6 addresses service.record_a = (mdns_record_t){.name = service.hostname_qualified, .type = MDNS_RECORDTYPE_A, .data.a.addr = service.address_ipv4, .rclass = 0, .ttl = 0}; service.record_aaaa = (mdns_record_t){.name = service.hostname_qualified, .type = MDNS_RECORDTYPE_AAAA, .data.aaaa.addr = service.address_ipv6, .rclass = 0, .ttl = 0}; // Add two test TXT records for our service instance name, will be coalesced into // one record with both key-value pair strings by the library service.txt_record[0] = (mdns_record_t){.name = service.service_instance, .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("test")}, .data.txt.value = {MDNS_STRING_CONST("1")}, .rclass = 0, .ttl = 0}; service.txt_record[1] = (mdns_record_t){.name = service.service_instance, .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("other")}, .data.txt.value = {MDNS_STRING_CONST("value")}, .rclass = 0, .ttl = 0}; // Send an announcement on startup of service { printf("Sending announce\n"); mdns_record_t additional[5] = {0}; size_t additional_count = 0; additional[additional_count++] = service.record_srv; if (service.address_ipv4.sin_family == AF_INET) additional[additional_count++] = service.record_a; if (service.address_ipv6.sin6_family == AF_INET6) additional[additional_count++] = service.record_aaaa; additional[additional_count++] = service.txt_record[0]; additional[additional_count++] = service.txt_record[1]; for (int isock = 0; isock < num_sockets; ++isock) mdns_announce_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0, additional, additional_count); } // This is a crude implementation that checks for incoming queries while (running) { int nfds = 0; fd_set readfs; FD_ZERO(&readfs); for (int isock = 0; isock < num_sockets; ++isock) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 100000; if (select(nfds, &readfs, 0, 0, &timeout) >= 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { mdns_socket_listen(sockets[isock], buffer, capacity, service_callback, &service); } FD_SET(sockets[isock], &readfs); } } else { break; } } // Send a goodbye on end of service { printf("Sending goodbye\n"); mdns_record_t additional[5] = {0}; size_t additional_count = 0; additional[additional_count++] = service.record_srv; if (service.address_ipv4.sin_family == AF_INET) additional[additional_count++] = service.record_a; if (service.address_ipv6.sin6_family == AF_INET6) additional[additional_count++] = service.record_aaaa; additional[additional_count++] = service.txt_record[0]; additional[additional_count++] = service.txt_record[1]; for (int isock = 0; isock < num_sockets; ++isock) mdns_goodbye_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0, additional, additional_count); } free(buffer); free(service_name_buffer); for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); printf("Closed socket%s\n", num_sockets ? "s" : ""); return 0; }*/ int service_mdns(const char* hostname, const char* service_name, int service_port, const char* local_ip) { int sockets[32]; int num_sockets = open_service_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), local_ip); if (num_sockets <= 0) { printf("Failed to open any client sockets\n"); return -1; } printf("Opened %d socket%s for mDNS service\n", num_sockets, num_sockets ? "s" : ""); size_t service_name_length = strlen(service_name); if (!service_name_length) { printf("Invalid service name\n"); return -1; } char* service_name_buffer = malloc(service_name_length + 2); memcpy(service_name_buffer, service_name, service_name_length); if (service_name_buffer[service_name_length - 1] != '.') service_name_buffer[service_name_length++] = '.'; service_name_buffer[service_name_length] = 0; service_name = service_name_buffer; printf("Service mDNS: %s:%d\n", service_name, service_port); printf("Hostname: %s\n", hostname); size_t capacity = 2048; void* buffer = malloc(capacity); mdns_string_t service_string = (mdns_string_t){service_name, strlen(service_name)}; mdns_string_t hostname_string = (mdns_string_t){hostname, strlen(hostname)}; char service_instance_buffer[256] = {0}; snprintf(service_instance_buffer, sizeof(service_instance_buffer) - 1, "%.*s.%.*s", MDNS_STRING_FORMAT(hostname_string), MDNS_STRING_FORMAT(service_string)); mdns_string_t service_instance_string = (mdns_string_t){service_instance_buffer, strlen(service_instance_buffer)}; char qualified_hostname_buffer[256] = {0}; snprintf(qualified_hostname_buffer, sizeof(qualified_hostname_buffer) - 1, "%.*s.local.", MDNS_STRING_FORMAT(hostname_string)); mdns_string_t hostname_qualified_string = (mdns_string_t){qualified_hostname_buffer, strlen(qualified_hostname_buffer)}; service_t service = {0}; service.service = service_string; service.hostname = hostname_string; service.service_instance = service_instance_string; service.hostname_qualified = hostname_qualified_string; service.port = service_port; if (local_ip) { struct sockaddr_in addr_ipv4; memset(&addr_ipv4, 0, sizeof(addr_ipv4)); addr_ipv4.sin_family = AF_INET; if (inet_pton(AF_INET, local_ip, &addr_ipv4.sin_addr) <= 0) { printf("Invalid local IP address: %s\n", local_ip); free(service_name_buffer); free(buffer); return -1; } service.address_ipv4 = addr_ipv4; } else { service.address_ipv4 = service_address_ipv4; } // Setup mDNS records service.record_ptr = (mdns_record_t){ .name = service.service, .type = MDNS_RECORDTYPE_PTR, .data.ptr.name = service.service_instance, .rclass = 0, .ttl = 120 }; service.record_srv = (mdns_record_t){ .name = service.service_instance, .type = MDNS_RECORDTYPE_SRV, .data.srv.name = service.hostname_qualified, .data.srv.port = service.port, .data.srv.priority = 0, .data.srv.weight = 0, .rclass = 0, .ttl = 120 }; service.record_a = (mdns_record_t){ .name = service.hostname_qualified, .type = MDNS_RECORDTYPE_A, .data.a.addr = service.address_ipv4, .rclass = 0, .ttl = 120 }; service.record_aaaa = (mdns_record_t){ .name = service.hostname_qualified, .type = MDNS_RECORDTYPE_AAAA, .data.aaaa.addr = service.address_ipv6, .rclass = 0, .ttl = 120 }; service.txt_record[0] = (mdns_record_t){ .name = service.service_instance, .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("test")}, .data.txt.value = {MDNS_STRING_CONST("1")}, .rclass = 0, .ttl = 120 }; service.txt_record[1] = (mdns_record_t){ .name = service.service_instance, .type = MDNS_RECORDTYPE_TXT, .data.txt.key = {MDNS_STRING_CONST("other")}, .data.txt.value = {MDNS_STRING_CONST("value")}, .rclass = 0, .ttl = 120 }; printf("Sending announce\n"); mdns_record_t additional[5] = {0}; size_t additional_count = 0; additional[additional_count++] = service.record_srv; additional[additional_count++] = service.record_a; additional[additional_count++] = service.txt_record[0]; additional[additional_count++] = service.txt_record[1]; for (int isock = 0; isock < num_sockets; ++isock) mdns_announce_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0, additional, additional_count); // Service loop while (running) { int nfds = 0; fd_set readfs; FD_ZERO(&readfs); for (int isock = 0; isock < num_sockets; ++isock) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 100000; if (select(nfds, &readfs, 0, 0, &timeout) >= 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { mdns_socket_listen(sockets[isock], buffer, capacity, service_callback, &service); } FD_SET(sockets[isock], &readfs); } } else { break; } } printf("Sending goodbye\n"); additional_count = 0; additional[additional_count++] = service.record_srv; additional[additional_count++] = service.record_a; additional[additional_count++] = service.txt_record[0]; additional[additional_count++] = service.txt_record[1]; for (int isock = 0; isock < num_sockets; ++isock) mdns_goodbye_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0, additional, additional_count); free(buffer); free(service_name_buffer); for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); printf("Closed socket%s\n", num_sockets > 1 ? "s" : ""); return 0; } // Dump all incoming mDNS queries and answers static int dump_mdns(void) { int sockets[32]; int num_sockets = open_service_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), "192.168.1.170"); if (num_sockets <= 0) { printf("Failed to open any client sockets\n"); return -1; } printf("Opened %d socket%s for mDNS dump\n", num_sockets, num_sockets ? "s" : ""); size_t capacity = 2048; void* buffer = malloc(capacity); // This is a crude implementation that checks for incoming queries and answers while (running) { int nfds = 0; fd_set readfs; FD_ZERO(&readfs); for (int isock = 0; isock < num_sockets; ++isock) { if (sockets[isock] >= nfds) nfds = sockets[isock] + 1; FD_SET(sockets[isock], &readfs); } struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 100000; if (select(nfds, &readfs, 0, 0, &timeout) >= 0) { for (int isock = 0; isock < num_sockets; ++isock) { if (FD_ISSET(sockets[isock], &readfs)) { mdns_socket_listen(sockets[isock], buffer, capacity, dump_callback, 0); } FD_SET(sockets[isock], &readfs); } } else { break; } } free(buffer); for (int isock = 0; isock < num_sockets; ++isock) mdns_socket_close(sockets[isock]); printf("Closed socket%s\n", num_sockets ? "s" : ""); return 0; } #ifdef MDNS_FUZZING #undef printf // Fuzzing by piping random data into the recieve functions static void fuzz_mdns(void) { #define MAX_FUZZ_SIZE 4096 #define MAX_PASSES (1024 * 1024 * 1024) static uint8_t fuzz_mdns_services_query[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd', 0x04, '_', 'u', 'd', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00}; uint8_t* buffer = malloc(MAX_FUZZ_SIZE); uint8_t* strbuffer = malloc(MAX_FUZZ_SIZE); for (int ipass = 0; ipass < MAX_PASSES; ++ipass) { size_t size = rand() % MAX_FUZZ_SIZE; for (size_t i = 0; i < size; ++i) buffer[i] = rand() & 0xFF; if (ipass % 4) { // Crafted fuzzing, make sure header is reasonable memcpy(buffer, fuzz_mdns_services_query, sizeof(fuzz_mdns_services_query)); uint16_t* header = (uint16_t*)buffer; header[0] = 0; header[1] = htons(0x8400); for (int ival = 2; ival < 6; ++ival) header[ival] = rand() & 0xFF; } mdns_discovery_recv(0, (void*)buffer, size, query_callback, 0); mdns_socket_listen(0, (void*)buffer, size, service_callback, 0); if (ipass % 4) { // Crafted fuzzing, make sure header is reasonable (1 question claimed). // Earlier passes will have done completely random data uint16_t* header = (uint16_t*)buffer; header[2] = htons(1); } mdns_query_recv(0, (void*)buffer, size, query_callback, 0, 0); // Fuzzing by piping random data into the parse functions size_t offset = size ? (rand() % size) : 0; size_t length = size ? (rand() % (size - offset)) : 0; mdns_record_parse_ptr(buffer, size, offset, length, strbuffer, MAX_FUZZ_SIZE); offset = size ? (rand() % size) : 0; length = size ? (rand() % (size - offset)) : 0; mdns_record_parse_srv(buffer, size, offset, length, strbuffer, MAX_FUZZ_SIZE); struct sockaddr_in addr_ipv4; offset = size ? (rand() % size) : 0; length = size ? (rand() % (size - offset)) : 0; mdns_record_parse_a(buffer, size, offset, length, &addr_ipv4); struct sockaddr_in6 addr_ipv6; offset = size ? (rand() % size) : 0; length = size ? (rand() % (size - offset)) : 0; mdns_record_parse_aaaa(buffer, size, offset, length, &addr_ipv6); offset = size ? (rand() % size) : 0; length = size ? (rand() % (size - offset)) : 0; mdns_record_parse_txt(buffer, size, offset, length, (mdns_record_txt_t*)strbuffer, MAX_FUZZ_SIZE); if (ipass && !(ipass % 10000)) printf("Completed fuzzing pass %d\n", ipass); } free(buffer); free(strbuffer); } #endif #ifdef _WIN32 BOOL console_handler(DWORD signal) { if (signal == CTRL_C_EVENT) { running = 0; } return TRUE; } #else void signal_handler(int signal) { running = 0; } #endif /* int main(int argc, const char* const* argv) { int mode = 0; const char* service = "_test-mdns._tcp.local."; const char* hostname = "dummy-host"; mdns_query_t query[16]; size_t query_count = 0; int service_port = 42424; server_info_t server_info; memset(&server_info, 0, sizeof(server_info_t)); #ifdef _WIN32 WORD versionWanted = MAKEWORD(1, 1); WSADATA wsaData; if (WSAStartup(versionWanted, &wsaData)) { printf("Failed to initialize WinSock\n"); return -1; } char hostname_buffer[256]; DWORD hostname_size = (DWORD)sizeof(hostname_buffer); if (GetComputerNameA(hostname_buffer, &hostname_size)) hostname = hostname_buffer; SetConsoleCtrlHandler(console_handler, TRUE); #else char hostname_buffer[256]; size_t hostname_size = sizeof(hostname_buffer); if (gethostname(hostname_buffer, hostname_size) == 0) hostname = hostname_buffer; signal(SIGINT, signal_handler); #endif for (int iarg = 0; iarg < argc; ++iarg) { if (strcmp(argv[iarg], "--discovery") == 0) { mode = 0; } else if (strcmp(argv[iarg], "--query") == 0) { // Each query is either a service name, or a pair of record type and a service name // For example: // mdns --query _foo._tcp.local. // mdns --query SRV myhost._foo._tcp.local. // mdns --query A myhost._tcp.local. _service._tcp.local. mode = 1; ++iarg; while ((iarg < argc) && (query_count < 16)) { query[query_count].name = argv[iarg++]; query[query_count].type = MDNS_RECORDTYPE_PTR; if (iarg < argc) { mdns_record_type_t record_type = 0; if (strcmp(query[query_count].name, "PTR") == 0) record_type = MDNS_RECORDTYPE_PTR; else if (strcmp(query[query_count].name, "SRV") == 0) record_type = MDNS_RECORDTYPE_SRV; else if (strcmp(query[query_count].name, "A") == 0) record_type = MDNS_RECORDTYPE_A; else if (strcmp(query[query_count].name, "AAAA") == 0) record_type = MDNS_RECORDTYPE_AAAA; if (record_type != 0) { query[query_count].type = record_type; query[query_count].name = argv[iarg++]; } } query[query_count].length = strlen(query[query_count].name); ++query_count; } } else if (strcmp(argv[iarg], "--service") == 0) { mode = 2; ++iarg; if (iarg < argc) service = argv[iarg]; } else if (strcmp(argv[iarg], "--dump") == 0) { mode = 3; } else if (strcmp(argv[iarg], "--hostname") == 0) { ++iarg; if (iarg < argc) hostname = argv[iarg]; } else if (strcmp(argv[iarg], "--port") == 0) { ++iarg; if (iarg < argc) service_port = atoi(argv[iarg]); } } #ifdef MDNS_FUZZING fuzz_mdns(); #else int ret; if (mode == 0) ret = send_dns_sd(); else if (mode == 1) ret = send_mdns_query(query, query_count, &server_info); else if (mode == 2) ret = service_mdns(hostname, service, service_port); else if (mode == 3) ret = dump_mdns(); #endif #ifdef _WIN32 WSACleanup(); #endif printf("ip %s, port %d, domain %s\n", server_info.ip, server_info.port, server_info.domain); return 0; } */