curl-fuzzer C++ reference
Loading...
Searching...
No Matches
resolver_getaddrinfo_shim.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3 *
4 * SPDX-License-Identifier: curl
5 */
6
9
10#include <arpa/inet.h>
11#include <netdb.h>
12#include <netinet/in.h>
13#include <sys/socket.h>
14
15#include <cerrno>
16#include <chrono>
17#include <cstdint>
18#include <cstdlib>
19#include <cstring>
20#include <thread>
21
23
24namespace {
25
26constexpr char kPositiveHost[] = "resolver-positive.test";
27constexpr char kPartialHost[] = "resolver-partial.test";
28constexpr char kNegativeHost[] = "resolver-negative.test";
29constexpr char kLinkLocalHost[] = "resolver-link-local.test";
30constexpr char kQueuedCancelHost[] = "resolver-queued-cancel.test";
31
32unsigned short ParsePort(const char* service) {
33 if (service == nullptr || *service == '\0') {
34 return 0;
35 }
36 char* end = nullptr;
37 errno = 0;
38 const unsigned long value = std::strtoul(service, &end, 10);
39 if (errno != 0 || end == service || *end != '\0' || value > UINT16_MAX) {
40 return 0;
41 }
42 return static_cast<unsigned short>(value);
43}
44
45addrinfo* AllocateAddress(int family, const addrinfo* hints, unsigned short port, const void* address) {
46 auto* node = static_cast<addrinfo*>(std::calloc(1, sizeof(addrinfo)));
47 if (node == nullptr) {
48 return nullptr;
49 }
50
51 const std::size_t address_size = family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
52 auto* socket_address = static_cast<sockaddr*>(std::calloc(1, address_size));
53 if (socket_address == nullptr) {
54 std::free(node);
55 return nullptr;
56 }
57
58 node->ai_flags = hints == nullptr ? 0 : hints->ai_flags;
59 node->ai_family = family;
60 node->ai_socktype = hints == nullptr || hints->ai_socktype == 0 ? SOCK_STREAM : hints->ai_socktype;
61 node->ai_protocol = hints == nullptr ? 0 : hints->ai_protocol;
62 node->ai_addrlen = static_cast<socklen_t>(address_size);
63 node->ai_addr = socket_address;
64
65 if (family == AF_INET) {
66 auto* ipv4 = reinterpret_cast<sockaddr_in*>(socket_address);
67 ipv4->sin_family = AF_INET;
68 ipv4->sin_port = htons(port);
69 std::memcpy(&ipv4->sin_addr, address, sizeof(ipv4->sin_addr));
70 } else {
71 auto* ipv6 = reinterpret_cast<sockaddr_in6*>(socket_address);
72 ipv6->sin6_family = AF_INET6;
73 ipv6->sin6_port = htons(port);
74 std::memcpy(&ipv6->sin6_addr, address, sizeof(ipv6->sin6_addr));
75 }
76 return node;
77}
78
79bool AppendAddress(addrinfo** head, addrinfo** tail, int family, const addrinfo* hints, unsigned short port,
80 const char* address) {
81 in_addr ipv4{};
82 in6_addr ipv6{};
83 const void* bytes = nullptr;
84 if (family == AF_INET) {
85 if (inet_pton(AF_INET, address, &ipv4) != 1) {
86 return false;
87 }
88 bytes = &ipv4;
89 } else {
90 if (inet_pton(AF_INET6, address, &ipv6) != 1) {
91 return false;
92 }
93 bytes = &ipv6;
94 }
95
96 addrinfo* node = AllocateAddress(family, hints, port, bytes);
97 if (node == nullptr) {
98 return false;
99 }
100 if (*tail == nullptr) {
101 *head = node;
102 } else {
103 (*tail)->ai_next = node;
104 }
105 *tail = node;
106 return true;
107}
108
109void FreeAddressList(addrinfo* address) noexcept {
110 while (address != nullptr) {
111 addrinfo* next = address->ai_next;
112 std::free(address->ai_addr);
113 std::free(address->ai_canonname);
114 std::free(address);
115 address = next;
116 }
117}
118
119} // namespace
120
129extern "C" int getaddrinfo(const char* nodename, const char* service, const addrinfo* hints, addrinfo** result) {
130 if (result == nullptr) {
131 return EAI_FAIL;
132 }
133 *result = nullptr;
134 if (nodename == nullptr) {
135 return EAI_NONAME;
136 }
137
138 const bool negative = std::strcmp(nodename, kNegativeHost) == 0;
139
140 const bool positive = std::strcmp(nodename, kPositiveHost) == 0;
141 const bool partial = std::strcmp(nodename, kPartialHost) == 0;
142 const bool link_local = std::strcmp(nodename, kLinkLocalHost) == 0;
143 const bool queued_cancel = std::strcmp(nodename, kQueuedCancelHost) == 0;
144 if (!positive && !negative && !partial && !link_local && !queued_cancel) {
145 return EAI_NONAME;
146 }
147
148 // This hostname is selected only by the queued-cancellation resolver mode.
149 // The main thread waits until the first handle's first family is stopped
150 // here, then adds and starts a duplicate handle. With the public resolver
151 // pool capped at one worker, no later family can be dequeued before the
152 // duplicate's pending resolver work is cancelled.
153 if (queued_cancel) {
155 }
156
157 // Hold every owned answer for one bounded interval so curl's first public
158 // poll observes live resolver work. Unlike a scheduler race or debug-only
159 // environment hook, this is part of the target-local backend contract.
160 std::this_thread::sleep_for(std::chrono::milliseconds(1));
161 if (negative) {
162 return EAI_NONAME;
163 }
164
165 const int family = hints == nullptr ? AF_UNSPEC : hints->ai_family;
166 if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6) {
167 return EAI_FAMILY;
168 }
169
170 const unsigned short port = ParsePort(service);
171 if (partial && family == AF_INET) {
172 // Keep the A answer behind the AAAA answer by a small, fixed interval.
173 // curl can then consume the first asynchronous family through its live
174 // resolver accessor before the complete result moves into the DNS cache.
175 std::this_thread::sleep_for(std::chrono::milliseconds(4));
176 }
177 addrinfo* head = nullptr;
178 addrinfo* tail = nullptr;
179 bool ok = true;
180 if (positive || partial || queued_cancel) {
181 if (family == AF_UNSPEC || family == AF_INET6) {
182 ok = AppendAddress(&head, &tail, AF_INET6, hints, port, "::1") &&
183 AppendAddress(&head, &tail, AF_INET6, hints, port, "::2");
184 }
185 if (ok && (family == AF_UNSPEC || family == AF_INET)) {
186 ok = AppendAddress(&head, &tail, AF_INET, hints, port, "127.0.0.1") &&
187 AppendAddress(&head, &tail, AF_INET, hints, port, "127.0.0.2");
188 }
189 } else if (family == AF_INET6) {
190 ok = AppendAddress(&head, &tail, AF_INET6, hints, port, "fe80::1");
191 } else if (family == AF_UNSPEC) {
192 ok = AppendAddress(&head, &tail, AF_INET6, hints, port, "fe80::1") &&
193 AppendAddress(&head, &tail, AF_INET, hints, port, "127.0.0.1");
194 } else {
195 ok = AppendAddress(&head, &tail, AF_INET, hints, port, "127.0.0.1");
196 }
197
198 if (!ok || head == nullptr) {
199 FreeAddressList(head);
200 return EAI_MEMORY;
201 }
202 *result = head;
203 return 0;
204}
205
208extern "C" void freeaddrinfo(addrinfo* address) noexcept { FreeAddressList(address); }
Bounded rendezvous for the resolver queued-cancellation scenario.
void WaitAtResolverCancelGate()
Block only the first lookup issued while ScopedResolverCancelGate is live.
int getaddrinfo(const char *nodename, const char *service, const addrinfo *hints, addrinfo **result)
Replace libc name service only in curl_fuzzer_proto_resolver.
void freeaddrinfo(addrinfo *address) noexcept
Match the ownership contract of the target-local getaddrinfo definition.