Line | Count | Source |
1 | | /* Copyright 2026 Google LLC |
2 | | Licensed under the Apache License, Version 2.0 (the "License"); |
3 | | you may not use this file except in compliance with the License. |
4 | | You may obtain a copy of the License at |
5 | | http://www.apache.org/licenses/LICENSE-2.0 |
6 | | Unless required by applicable law or agreed to in writing, software |
7 | | distributed under the License is distributed on an "AS IS" BASIS, |
8 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
9 | | See the License for the specific language governing permissions and |
10 | | limitations under the License. |
11 | | */ |
12 | | |
13 | | #include <stddef.h> |
14 | | #include <stdint.h> |
15 | | #include <stdlib.h> |
16 | | #include <string.h> |
17 | | #include <sys/socket.h> |
18 | | #include <netinet/in.h> |
19 | | #include <arpa/inet.h> |
20 | | #include <unistd.h> |
21 | | #include <fcntl.h> |
22 | | #include <sys/stat.h> |
23 | | |
24 | | #include <fuzzer/FuzzedDataProvider.h> |
25 | | |
26 | | extern "C" { |
27 | | #include "event2/event.h" |
28 | | #include "event2/dns.h" |
29 | | #include "event2/util.h" |
30 | | #include "event2/dns_struct.h" |
31 | | } |
32 | | |
33 | 0 | static void dns_callback(int result, char type, int count, int ttl, void *addresses, void *arg) { |
34 | 0 | } |
35 | | |
36 | 0 | static void dns_server_cb(struct evdns_server_request *req, void *data) { |
37 | 0 | evdns_server_request_respond(req, 0); |
38 | 0 | } |
39 | | |
40 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
41 | | if (size < 10) return 0; |
42 | | |
43 | | FuzzedDataProvider data_provider(data, size); |
44 | | uint8_t mode = data_provider.ConsumeIntegral<uint8_t>(); |
45 | | |
46 | | struct event_base *base = event_base_new(); |
47 | | if (!base) return 0; |
48 | | |
49 | | if (mode % 3 == 0) { |
50 | | /* Config parsing (from dns_config_fuzzer.cc) */ |
51 | | uint32_t flags = data_provider.ConsumeIntegral<uint32_t>(); |
52 | | std::string s1 = data_provider.ConsumeRandomLengthString(); |
53 | | std::string s2 = data_provider.ConsumeRandomLengthString(); |
54 | | |
55 | | struct evdns_base *dns = evdns_base_new(base, flags % 65537); |
56 | | if (dns) { |
57 | | char resolvFilename[50]; |
58 | | sprintf(resolvFilename, "/tmp/resolv.%d", getpid()); |
59 | | FILE *fp = fopen(resolvFilename, "wb"); |
60 | | if (fp) { |
61 | | fwrite(s1.c_str(), s1.size(), 1, fp); |
62 | | fclose(fp); |
63 | | evdns_base_resolv_conf_parse(dns, flags % 17, resolvFilename); |
64 | | unlink(resolvFilename); |
65 | | } |
66 | | |
67 | | char hostsFilename[50]; |
68 | | sprintf(hostsFilename, "/tmp/hosts.%d", getpid()); |
69 | | fp = fopen(hostsFilename, "wb"); |
70 | | if (fp) { |
71 | | fwrite(s2.c_str(), s2.size(), 1, fp); |
72 | | fclose(fp); |
73 | | evdns_base_load_hosts(dns, hostsFilename); |
74 | | unlink(hostsFilename); |
75 | | } |
76 | | evdns_base_search_ndots_set(dns, flags); |
77 | | evdns_base_search_clear(dns); |
78 | | evdns_base_clear_host_addresses(dns); |
79 | | evdns_base_free(dns, 0); |
80 | | } |
81 | | } else if (mode % 3 == 1) { |
82 | | /* Server-side request parsing (from dns_message_fuzzer.cc) */ |
83 | | int fds[2]; |
84 | | if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0) { |
85 | | struct evdns_server_port *port = |
86 | | evdns_add_server_port_with_base(base, fds[0], 0, dns_server_cb, nullptr); |
87 | | if (port) { |
88 | | std::vector<uint8_t> packet = data_provider.ConsumeRemainingBytes<uint8_t>(); |
89 | | if (packet.size() > 0) { |
90 | | send(fds[1], packet.data(), packet.size(), 0); |
91 | | event_base_loop(base, EVLOOP_NONBLOCK); |
92 | | } |
93 | | evdns_close_server_port(port); |
94 | | } |
95 | | close(fds[1]); |
96 | | } |
97 | | } else { |
98 | | /* Client-side response parsing (from evdns_client_fuzzer.cc) */ |
99 | | struct evdns_base *dns_base = evdns_base_new(base, 0); |
100 | | if (dns_base) { |
101 | | int sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
102 | | if (sockfd >= 0) { |
103 | | evutil_make_socket_nonblocking(sockfd); |
104 | | struct sockaddr_in sin; |
105 | | memset(&sin, 0, sizeof(sin)); |
106 | | sin.sin_family = AF_INET; |
107 | | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
108 | | sin.sin_port = 0; |
109 | | if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) == 0) { |
110 | | socklen_t slen = sizeof(sin); |
111 | | getsockname(sockfd, (struct sockaddr *)&sin, &slen); |
112 | | char addr_port[64]; |
113 | | snprintf(addr_port, sizeof(addr_port), "127.0.0.1:%d", ntohs(sin.sin_port)); |
114 | | evdns_base_nameserver_ip_add(dns_base, addr_port); |
115 | | |
116 | | uint8_t type = data_provider.ConsumeIntegral<uint8_t>() % 4; |
117 | | const char *name = "example.com"; |
118 | | switch (type) { |
119 | | case 0: evdns_base_resolve_ipv4(dns_base, name, 0, dns_callback, NULL); break; |
120 | | case 1: evdns_base_resolve_ipv6(dns_base, name, 0, dns_callback, NULL); break; |
121 | | case 2: { |
122 | | struct in_addr addr; |
123 | | addr.s_addr = 0x01020304; |
124 | | evdns_base_resolve_reverse(dns_base, &addr, 0, dns_callback, NULL); |
125 | | } break; |
126 | | case 3: evdns_base_resolve_reverse_ipv6(dns_base, (struct in6_addr *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 0, dns_callback, NULL); break; |
127 | | } |
128 | | event_base_loop(base, EVLOOP_NONBLOCK); |
129 | | |
130 | | struct sockaddr_in client_addr; |
131 | | slen = sizeof(client_addr); |
132 | | char buf[1500]; |
133 | | int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &slen); |
134 | | if (n > 0) { |
135 | | std::vector<uint8_t> resp = data_provider.ConsumeRemainingBytes<uint8_t>(); |
136 | | if (resp.size() > 0) { |
137 | | sendto(sockfd, resp.data(), resp.size(), 0, (struct sockaddr *)&client_addr, slen); |
138 | | event_base_loop(base, EVLOOP_NONBLOCK); |
139 | | } |
140 | | } |
141 | | } |
142 | | close(sockfd); |
143 | | } |
144 | | evdns_base_free(dns_base, 0); |
145 | | } |
146 | | } |
147 | | |
148 | | event_base_free(base); |
149 | | return 0; |
150 | | } |