Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Assertions.h>
8#include <AK/ByteBuffer.h>
9#include <AK/DeprecatedString.h>
10#include <AK/ScopeGuard.h>
11#include <Kernel/Net/IPv4.h>
12#include <arpa/inet.h>
13#include <errno.h>
14#include <netdb.h>
15#include <netinet/in.h>
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19
20extern "C" {
21
22#ifdef NO_TLS
23int h_errno;
24#else
25__thread int h_errno;
26#endif
27
28static hostent __gethostbyname_buffer;
29static in_addr_t __gethostbyname_address;
30static in_addr_t* __gethostbyname_address_list_buffer[2];
31static char* __gethostbyname_alias_list_buffer[1];
32
33static hostent __gethostbyaddr_buffer;
34static in_addr_t* __gethostbyaddr_address_list_buffer[2];
35static char* __gethostbyaddr_alias_list_buffer[1];
36// IPCCompiler depends on LibC. Because of this, it cannot be compiled
37// before LibC is. However, the lookup magic can only be obtained from the
38// endpoint itself if IPCCompiler has compiled the IPC file, so this creates
39// a chicken-and-egg situation. Because of this, the LookupServer endpoint magic
40// is hardcoded here.
41// Keep the name synchronized with LookupServer/LookupServer.ipc.
42static constexpr u32 lookup_server_endpoint_magic = "LookupServer"sv.hash();
43
44// Get service entry buffers and file information for the getservent() family of functions.
45static FILE* services_file = nullptr;
46static char const* services_path = "/etc/services";
47
48static bool fill_getserv_buffers(char const* line, ssize_t read);
49static servent __getserv_buffer;
50static DeprecatedString __getserv_name_buffer;
51static DeprecatedString __getserv_protocol_buffer;
52static int __getserv_port_buffer;
53static Vector<ByteBuffer> __getserv_alias_list_buffer;
54static Vector<char*> __getserv_alias_list;
55static bool keep_service_file_open = false;
56static ssize_t service_file_offset = 0;
57
58// Get protocol entry buffers and file information for the getprotent() family of functions.
59static FILE* protocols_file = nullptr;
60static char const* protocols_path = "/etc/protocols";
61
62static bool fill_getproto_buffers(char const* line, ssize_t read);
63static protoent __getproto_buffer;
64static DeprecatedString __getproto_name_buffer;
65static Vector<ByteBuffer> __getproto_alias_list_buffer;
66static Vector<char*> __getproto_alias_list;
67static int __getproto_protocol_buffer;
68static bool keep_protocols_file_open = false;
69static ssize_t protocol_file_offset = 0;
70
71static int connect_to_lookup_server()
72{
73 int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
74 if (fd < 0) {
75 perror("socket");
76 return -1;
77 }
78
79 sockaddr_un address {
80 AF_LOCAL,
81 "/tmp/portal/lookup"
82 };
83
84 if (connect(fd, (sockaddr const*)&address, sizeof(address)) < 0) {
85 perror("connect_to_lookup_server");
86 close(fd);
87 return -1;
88 }
89 return fd;
90}
91
92static DeprecatedString gethostbyname_name_buffer;
93
94hostent* gethostbyname(char const* name)
95{
96 h_errno = 0;
97
98 auto ipv4_address = IPv4Address::from_string({ name, strlen(name) });
99
100 if (ipv4_address.has_value()) {
101 gethostbyname_name_buffer = ipv4_address.value().to_deprecated_string();
102 __gethostbyname_buffer.h_name = const_cast<char*>(gethostbyname_name_buffer.characters());
103 __gethostbyname_alias_list_buffer[0] = nullptr;
104 __gethostbyname_buffer.h_aliases = __gethostbyname_alias_list_buffer;
105 __gethostbyname_buffer.h_addrtype = AF_INET;
106 new (&__gethostbyname_address) IPv4Address(ipv4_address.value());
107 __gethostbyname_address_list_buffer[0] = &__gethostbyname_address;
108 __gethostbyname_address_list_buffer[1] = nullptr;
109 __gethostbyname_buffer.h_addr_list = (char**)__gethostbyname_address_list_buffer;
110 __gethostbyname_buffer.h_length = 4;
111
112 return &__gethostbyname_buffer;
113 }
114
115 int fd = connect_to_lookup_server();
116 if (fd < 0) {
117 h_errno = TRY_AGAIN;
118 return nullptr;
119 }
120
121 auto close_fd_on_exit = ScopeGuard([fd] {
122 close(fd);
123 });
124
125 auto name_length = strlen(name);
126 VERIFY(name_length <= NumericLimits<i32>::max());
127
128 struct [[gnu::packed]] {
129 u32 message_size;
130 u32 endpoint_magic;
131 i32 message_id;
132 u32 name_length;
133 } request_header = {
134 (u32)(sizeof(request_header) - sizeof(request_header.message_size) + name_length),
135 lookup_server_endpoint_magic,
136 1,
137 static_cast<u32>(name_length),
138 };
139 if (auto nsent = write(fd, &request_header, sizeof(request_header)); nsent < 0) {
140 h_errno = TRY_AGAIN;
141 return nullptr;
142 } else if (nsent != sizeof(request_header)) {
143 h_errno = NO_RECOVERY;
144 return nullptr;
145 }
146
147 if (auto nsent = write(fd, name, name_length); nsent < 0) {
148 h_errno = TRY_AGAIN;
149 return nullptr;
150 } else if (static_cast<size_t>(nsent) != name_length) {
151 h_errno = NO_RECOVERY;
152 return nullptr;
153 }
154
155 struct [[gnu::packed]] {
156 u32 message_size;
157 u32 endpoint_magic;
158 i32 message_id;
159 i32 code;
160 u32 addresses_count;
161 } response_header;
162
163 if (auto nreceived = read(fd, &response_header, sizeof(response_header)); nreceived < 0) {
164 h_errno = TRY_AGAIN;
165 return nullptr;
166 } else if (nreceived != sizeof(response_header)) {
167 h_errno = NO_RECOVERY;
168 return nullptr;
169 }
170 if (response_header.endpoint_magic != lookup_server_endpoint_magic || response_header.message_id != 2) {
171 h_errno = NO_RECOVERY;
172 return nullptr;
173 }
174 if (response_header.code != 0) {
175 h_errno = NO_RECOVERY;
176 return nullptr;
177 }
178 if (response_header.addresses_count == 0) {
179 h_errno = HOST_NOT_FOUND;
180 return nullptr;
181 }
182 i32 response_length;
183 if (auto nreceived = read(fd, &response_length, sizeof(response_length)); nreceived < 0) {
184 h_errno = TRY_AGAIN;
185 return nullptr;
186 } else if (nreceived != sizeof(response_length)
187 || response_length != sizeof(__gethostbyname_address)) {
188 h_errno = NO_RECOVERY;
189 return nullptr;
190 }
191
192 if (auto nreceived = read(fd, &__gethostbyname_address, response_length); nreceived < 0) {
193 h_errno = TRY_AGAIN;
194 return nullptr;
195 } else if (nreceived != response_length) {
196 h_errno = NO_RECOVERY;
197 return nullptr;
198 }
199
200 gethostbyname_name_buffer = name;
201 __gethostbyname_buffer.h_name = const_cast<char*>(gethostbyname_name_buffer.characters());
202 __gethostbyname_alias_list_buffer[0] = nullptr;
203 __gethostbyname_buffer.h_aliases = __gethostbyname_alias_list_buffer;
204 __gethostbyname_buffer.h_addrtype = AF_INET;
205 __gethostbyname_address_list_buffer[0] = &__gethostbyname_address;
206 __gethostbyname_address_list_buffer[1] = nullptr;
207 __gethostbyname_buffer.h_addr_list = (char**)__gethostbyname_address_list_buffer;
208 __gethostbyname_buffer.h_length = 4;
209
210 return &__gethostbyname_buffer;
211}
212
213static DeprecatedString gethostbyaddr_name_buffer;
214
215hostent* gethostbyaddr(void const* addr, socklen_t addr_size, int type)
216{
217 h_errno = 0;
218
219 if (type != AF_INET) {
220 errno = EAFNOSUPPORT;
221 return nullptr;
222 }
223
224 if (addr_size < sizeof(in_addr)) {
225 errno = EINVAL;
226 return nullptr;
227 }
228
229 int fd = connect_to_lookup_server();
230 if (fd < 0) {
231 h_errno = TRY_AGAIN;
232 return nullptr;
233 }
234
235 auto close_fd_on_exit = ScopeGuard([fd] {
236 close(fd);
237 });
238
239 in_addr_t const& in_addr = ((const struct in_addr*)addr)->s_addr;
240
241 struct [[gnu::packed]] {
242 u32 message_size;
243 u32 endpoint_magic;
244 i32 message_id;
245 i32 address_length;
246 } request_header = {
247 sizeof(request_header) - sizeof(request_header.message_size) + sizeof(in_addr),
248 lookup_server_endpoint_magic,
249 3,
250 sizeof(in_addr),
251 };
252 if (auto nsent = write(fd, &request_header, sizeof(request_header)); nsent < 0) {
253 h_errno = TRY_AGAIN;
254 return nullptr;
255 } else if (nsent != sizeof(request_header)) {
256 h_errno = NO_RECOVERY;
257 return nullptr;
258 }
259 if (auto nsent = write(fd, &in_addr, sizeof(in_addr)); nsent < 0) {
260 h_errno = TRY_AGAIN;
261 return nullptr;
262 } else if (nsent != sizeof(in_addr)) {
263 h_errno = TRY_AGAIN;
264 return nullptr;
265 }
266
267 struct [[gnu::packed]] {
268 u32 message_size;
269 u32 endpoint_magic;
270 i32 message_id;
271 i32 code;
272 u32 name_length;
273 } response_header;
274
275 if (auto nreceived = read(fd, &response_header, sizeof(response_header)); nreceived < 0) {
276 h_errno = TRY_AGAIN;
277 return nullptr;
278 } else if (nreceived != sizeof(response_header)) {
279 h_errno = NO_RECOVERY;
280 return nullptr;
281 }
282 if (response_header.endpoint_magic != lookup_server_endpoint_magic
283 || response_header.message_id != 4
284 || response_header.code != 0) {
285 h_errno = NO_RECOVERY;
286 return nullptr;
287 }
288
289 char* buffer;
290 auto string_impl = StringImpl::create_uninitialized(response_header.name_length, buffer);
291
292 if (auto nreceived = read(fd, buffer, response_header.name_length); nreceived < 0) {
293 h_errno = TRY_AGAIN;
294 return nullptr;
295 } else if (static_cast<u32>(nreceived) != response_header.name_length) {
296 h_errno = NO_RECOVERY;
297 return nullptr;
298 }
299
300 gethostbyaddr_name_buffer = move(string_impl);
301 __gethostbyaddr_buffer.h_name = buffer;
302 __gethostbyaddr_alias_list_buffer[0] = nullptr;
303 __gethostbyaddr_buffer.h_aliases = __gethostbyaddr_alias_list_buffer;
304 __gethostbyaddr_buffer.h_addrtype = AF_INET;
305 // FIXME: Should we populate the hostent's address list here with a sockaddr_in for the provided host?
306 __gethostbyaddr_address_list_buffer[0] = nullptr;
307 __gethostbyaddr_buffer.h_addr_list = (char**)__gethostbyaddr_address_list_buffer;
308 __gethostbyaddr_buffer.h_length = 4;
309
310 return &__gethostbyaddr_buffer;
311}
312
313struct servent* getservent()
314{
315 // If the services file is not open, attempt to open it and return null if it fails.
316 if (!services_file) {
317 services_file = fopen(services_path, "r");
318
319 if (!services_file) {
320 perror("error opening services file");
321 return nullptr;
322 }
323 }
324
325 if (fseek(services_file, service_file_offset, SEEK_SET) != 0) {
326 perror("error seeking file");
327 fclose(services_file);
328 return nullptr;
329 }
330 char* line = nullptr;
331 size_t len = 0;
332 ssize_t read;
333
334 auto free_line_on_exit = ScopeGuard([line] {
335 if (line) {
336 free(line);
337 }
338 });
339
340 // Read lines from services file until an actual service name is found.
341 do {
342 read = getline(&line, &len, services_file);
343 service_file_offset += read;
344 if (read > 0 && (line[0] >= 65 && line[0] <= 122)) {
345 break;
346 }
347 } while (read != -1);
348 if (read == -1) {
349 fclose(services_file);
350 services_file = nullptr;
351 service_file_offset = 0;
352 return nullptr;
353 }
354
355 servent* service_entry = nullptr;
356 if (!fill_getserv_buffers(line, read))
357 return nullptr;
358
359 __getserv_buffer.s_name = const_cast<char*>(__getserv_name_buffer.characters());
360 __getserv_buffer.s_port = htons(__getserv_port_buffer);
361 __getserv_buffer.s_proto = const_cast<char*>(__getserv_protocol_buffer.characters());
362
363 __getserv_alias_list.clear_with_capacity();
364 __getserv_alias_list.ensure_capacity(__getserv_alias_list_buffer.size() + 1);
365 for (auto& alias : __getserv_alias_list_buffer)
366 __getserv_alias_list.unchecked_append(reinterpret_cast<char*>(alias.data()));
367 __getserv_alias_list.unchecked_append(nullptr);
368
369 __getserv_buffer.s_aliases = __getserv_alias_list.data();
370 service_entry = &__getserv_buffer;
371
372 if (!keep_service_file_open) {
373 endservent();
374 }
375 return service_entry;
376}
377
378struct servent* getservbyname(char const* name, char const* protocol)
379{
380 if (name == nullptr)
381 return nullptr;
382
383 bool previous_file_open_setting = keep_service_file_open;
384 setservent(1);
385 struct servent* current_service = nullptr;
386 auto service_file_handler = ScopeGuard([previous_file_open_setting] {
387 if (!previous_file_open_setting) {
388 endservent();
389 }
390 });
391
392 while (true) {
393 current_service = getservent();
394 if (current_service == nullptr)
395 break;
396 else if (!protocol && strcmp(current_service->s_name, name) == 0)
397 break;
398 else if (strcmp(current_service->s_name, name) == 0 && strcmp(current_service->s_proto, protocol) == 0)
399 break;
400 }
401
402 return current_service;
403}
404
405struct servent* getservbyport(int port, char const* protocol)
406{
407 bool previous_file_open_setting = keep_service_file_open;
408 setservent(1);
409 struct servent* current_service = nullptr;
410 auto service_file_handler = ScopeGuard([previous_file_open_setting] {
411 if (!previous_file_open_setting) {
412 endservent();
413 }
414 });
415 while (true) {
416 current_service = getservent();
417 if (current_service == nullptr)
418 break;
419 else if (!protocol && current_service->s_port == port)
420 break;
421 else if (current_service->s_port == port && (strcmp(current_service->s_proto, protocol) == 0))
422 break;
423 }
424
425 return current_service;
426}
427
428void setservent(int stay_open)
429{
430 if (!services_file) {
431 services_file = fopen(services_path, "r");
432
433 if (!services_file) {
434 perror("error opening services file");
435 return;
436 }
437 }
438 rewind(services_file);
439 keep_service_file_open = stay_open;
440 service_file_offset = 0;
441}
442
443void endservent()
444{
445 if (!services_file) {
446 return;
447 }
448 fclose(services_file);
449 services_file = nullptr;
450}
451
452// Fill the service entry buffer with the information contained
453// in the currently read line, returns true if successful,
454// false if failure occurs.
455static bool fill_getserv_buffers(char const* line, ssize_t read)
456{
457 // Splitting the line by tab delimiter and filling the servent buffers name, port, and protocol members.
458 auto split_line = StringView(line, read).replace(" "sv, "\t"sv, ReplaceMode::All).split('\t');
459
460 // This indicates an incorrect file format.
461 // Services file entries should always at least contain
462 // name and port/protocol, separated by tabs.
463 if (split_line.size() < 2) {
464 warnln("getservent(): malformed services file");
465 return false;
466 }
467 __getserv_name_buffer = split_line[0];
468
469 auto port_protocol_split = DeprecatedString(split_line[1]).split('/');
470 if (port_protocol_split.size() < 2) {
471 warnln("getservent(): malformed services file");
472 return false;
473 }
474 auto number = port_protocol_split[0].to_int();
475 if (!number.has_value())
476 return false;
477
478 __getserv_port_buffer = number.value();
479
480 // Remove any annoying whitespace at the end of the protocol.
481 __getserv_protocol_buffer = port_protocol_split[1].replace(" "sv, ""sv, ReplaceMode::All).replace("\t"sv, ""sv, ReplaceMode::All).replace("\n"sv, ""sv, ReplaceMode::All);
482 __getserv_alias_list_buffer.clear();
483
484 // If there are aliases for the service, we will fill the alias list buffer.
485 if (split_line.size() > 2 && !split_line[2].starts_with('#')) {
486
487 for (size_t i = 2; i < split_line.size(); i++) {
488 if (split_line[i].starts_with('#')) {
489 break;
490 }
491 auto alias = split_line[i].to_byte_buffer();
492 if (alias.try_append("\0", sizeof(char)).is_error())
493 return false;
494 __getserv_alias_list_buffer.append(move(alias));
495 }
496 }
497
498 return true;
499}
500
501struct protoent* getprotoent()
502{
503 // If protocols file isn't open, attempt to open and return null on failure.
504 if (!protocols_file) {
505 protocols_file = fopen(protocols_path, "r");
506
507 if (!protocols_file) {
508 perror("error opening protocols file");
509 return nullptr;
510 }
511 }
512
513 if (fseek(protocols_file, protocol_file_offset, SEEK_SET) != 0) {
514 perror("error seeking protocols file");
515 fclose(protocols_file);
516 return nullptr;
517 }
518
519 char* line = nullptr;
520 size_t len = 0;
521 ssize_t read;
522
523 auto free_line_on_exit = ScopeGuard([line] {
524 if (line) {
525 free(line);
526 }
527 });
528
529 do {
530 read = getline(&line, &len, protocols_file);
531 protocol_file_offset += read;
532 if (read > 0 && (line[0] >= 65 && line[0] <= 122)) {
533 break;
534 }
535 } while (read != -1);
536
537 if (read == -1) {
538 fclose(protocols_file);
539 protocols_file = nullptr;
540 protocol_file_offset = 0;
541 return nullptr;
542 }
543
544 struct protoent* protocol_entry = nullptr;
545 if (!fill_getproto_buffers(line, read))
546 return nullptr;
547
548 __getproto_buffer.p_name = const_cast<char*>(__getproto_name_buffer.characters());
549 __getproto_buffer.p_proto = __getproto_protocol_buffer;
550
551 __getproto_alias_list.clear_with_capacity();
552 __getproto_alias_list.ensure_capacity(__getproto_alias_list_buffer.size() + 1);
553 for (auto& alias : __getproto_alias_list_buffer)
554 __getproto_alias_list.unchecked_append(reinterpret_cast<char*>(alias.data()));
555 __getserv_alias_list.unchecked_append(nullptr);
556
557 __getproto_buffer.p_aliases = __getproto_alias_list.data();
558 protocol_entry = &__getproto_buffer;
559
560 if (!keep_protocols_file_open)
561 endprotoent();
562
563 return protocol_entry;
564}
565
566struct protoent* getprotobyname(char const* name)
567{
568 bool previous_file_open_setting = keep_protocols_file_open;
569 setprotoent(1);
570 struct protoent* current_protocol = nullptr;
571 auto protocol_file_handler = ScopeGuard([previous_file_open_setting] {
572 if (!previous_file_open_setting) {
573 endprotoent();
574 }
575 });
576
577 while (true) {
578 current_protocol = getprotoent();
579 if (current_protocol == nullptr)
580 break;
581 else if (strcmp(current_protocol->p_name, name) == 0)
582 break;
583 }
584
585 return current_protocol;
586}
587
588struct protoent* getprotobynumber(int proto)
589{
590 bool previous_file_open_setting = keep_protocols_file_open;
591 setprotoent(1);
592 struct protoent* current_protocol = nullptr;
593 auto protocol_file_handler = ScopeGuard([previous_file_open_setting] {
594 if (!previous_file_open_setting) {
595 endprotoent();
596 }
597 });
598
599 while (true) {
600 current_protocol = getprotoent();
601 if (current_protocol == nullptr)
602 break;
603 else if (current_protocol->p_proto == proto)
604 break;
605 }
606
607 return current_protocol;
608}
609
610void setprotoent(int stay_open)
611{
612 if (!protocols_file) {
613 protocols_file = fopen(protocols_path, "r");
614
615 if (!protocols_file) {
616 perror("setprotoent(): error opening protocols file");
617 return;
618 }
619 }
620 rewind(protocols_file);
621 keep_protocols_file_open = stay_open;
622 protocol_file_offset = 0;
623}
624
625void endprotoent()
626{
627 if (!protocols_file) {
628 return;
629 }
630 fclose(protocols_file);
631 protocols_file = nullptr;
632}
633
634static bool fill_getproto_buffers(char const* line, ssize_t read)
635{
636 DeprecatedString string_line = DeprecatedString(line, read);
637 auto split_line = string_line.replace(" "sv, "\t"sv, ReplaceMode::All).split('\t');
638
639 // This indicates an incorrect file format. Protocols file entries should
640 // always have at least a name and a protocol.
641 if (split_line.size() < 2) {
642 warnln("getprotoent(): malformed protocols file");
643 return false;
644 }
645 __getproto_name_buffer = split_line[0];
646
647 auto number = split_line[1].to_int();
648 if (!number.has_value())
649 return false;
650
651 __getproto_protocol_buffer = number.value();
652
653 __getproto_alias_list_buffer.clear();
654
655 // If there are aliases for the protocol, we will fill the alias list buffer.
656 if (split_line.size() > 2 && !split_line[2].starts_with('#')) {
657
658 for (size_t i = 2; i < split_line.size(); i++) {
659 if (split_line[i].starts_with('#'))
660 break;
661 auto alias = split_line[i].to_byte_buffer();
662 if (alias.try_append("\0", sizeof(char)).is_error())
663 return false;
664 __getproto_alias_list_buffer.append(move(alias));
665 }
666 }
667
668 return true;
669}
670
671int getaddrinfo(char const* __restrict node, char const* __restrict service, const struct addrinfo* __restrict hints, struct addrinfo** __restrict res)
672{
673 *res = nullptr;
674
675 if (hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC)
676 return EAI_FAMILY;
677
678 if (!node) {
679 if (hints && hints->ai_flags & AI_PASSIVE)
680 node = "0.0.0.0";
681 else
682 node = "127.0.0.1";
683 }
684
685 auto host_ent = gethostbyname(node);
686 if (!host_ent)
687 return EAI_FAIL;
688
689 char const* proto = nullptr;
690 if (hints && hints->ai_socktype) {
691 switch (hints->ai_socktype) {
692 case SOCK_STREAM:
693 proto = "tcp";
694 break;
695 case SOCK_DGRAM:
696 proto = "udp";
697 break;
698 default:
699 return EAI_SOCKTYPE;
700 }
701 }
702
703 long port;
704 int socktype;
705 servent* svc_ent = nullptr;
706 if (!hints || (hints->ai_flags & AI_NUMERICSERV) == 0) {
707 svc_ent = getservbyname(service, proto);
708 }
709 if (!svc_ent) {
710 if (service) {
711 char* end;
712 port = htons(strtol(service, &end, 10));
713 if (*end)
714 return EAI_FAIL;
715 } else {
716 port = htons(0);
717 }
718
719 if (hints && hints->ai_socktype != 0)
720 socktype = hints->ai_socktype;
721 else
722 socktype = SOCK_STREAM;
723 } else {
724 port = svc_ent->s_port;
725 socktype = strcmp(svc_ent->s_proto, "tcp") ? SOCK_STREAM : SOCK_DGRAM;
726 }
727
728 addrinfo* first_info = nullptr;
729 addrinfo* prev_info = nullptr;
730
731 for (int host_index = 0; host_ent->h_addr_list[host_index]; host_index++) {
732 sockaddr_in* sin = new sockaddr_in;
733 sin->sin_family = AF_INET;
734 sin->sin_port = port;
735 memcpy(&sin->sin_addr.s_addr, host_ent->h_addr_list[host_index], host_ent->h_length);
736
737 addrinfo* info = new addrinfo;
738 info->ai_flags = 0;
739 info->ai_family = AF_INET;
740 info->ai_socktype = socktype;
741 info->ai_protocol = PF_INET;
742 info->ai_addrlen = sizeof(*sin);
743 info->ai_addr = reinterpret_cast<sockaddr*>(sin);
744
745 if (hints && hints->ai_flags & AI_CANONNAME)
746 info->ai_canonname = strdup(host_ent->h_name);
747 else
748 info->ai_canonname = nullptr;
749
750 info->ai_next = nullptr;
751
752 if (!first_info)
753 first_info = info;
754
755 if (prev_info)
756 prev_info->ai_next = info;
757
758 prev_info = info;
759 }
760
761 if (first_info) {
762 *res = first_info;
763 return 0;
764 } else
765 return EAI_NONAME;
766}
767
768void freeaddrinfo(struct addrinfo* res)
769{
770 if (res) {
771 delete reinterpret_cast<sockaddr_in*>(res->ai_addr);
772 free(res->ai_canonname);
773 freeaddrinfo(res->ai_next);
774 delete res;
775 }
776}
777
778char const* gai_strerror(int errcode)
779{
780 switch (errcode) {
781 case EAI_ADDRFAMILY:
782 return "no address for this address family available";
783 case EAI_AGAIN:
784 return "name server returned temporary failure";
785 case EAI_BADFLAGS:
786 return "invalid flags";
787 case EAI_FAIL:
788 return "name server returned permanent failure";
789 case EAI_FAMILY:
790 return "unsupported address family";
791 case EAI_MEMORY:
792 return "out of memory";
793 case EAI_NODATA:
794 return "no address available";
795 case EAI_NONAME:
796 return "node or service is not known";
797 case EAI_SERVICE:
798 return "service not available";
799 case EAI_SOCKTYPE:
800 return "unsupported socket type";
801 case EAI_SYSTEM:
802 return "system error";
803 case EAI_OVERFLOW:
804 return "buffer too small";
805 default:
806 return "invalid error code";
807 }
808}
809
810int getnameinfo(const struct sockaddr* __restrict addr, socklen_t addrlen, char* __restrict host, socklen_t hostlen, char* __restrict serv, socklen_t servlen, int flags)
811{
812 if (addr->sa_family != AF_INET || addrlen < sizeof(sockaddr_in))
813 return EAI_FAMILY;
814
815 sockaddr_in const* sin = reinterpret_cast<sockaddr_in const*>(addr);
816
817 if (host && hostlen > 0) {
818 if (flags != 0)
819 dbgln("getnameinfo flags are not implemented: {:#x}", flags);
820
821 if (!inet_ntop(AF_INET, &sin->sin_addr, host, hostlen)) {
822 if (errno == ENOSPC)
823 return EAI_OVERFLOW;
824 else
825 return EAI_SYSTEM;
826 }
827 }
828
829 if (serv && servlen > 0) {
830 if (snprintf(serv, servlen, "%d", (int)ntohs(sin->sin_port)) > (int)servlen)
831 return EAI_OVERFLOW;
832 }
833
834 return 0;
835}
836
837void herror(char const* s)
838{
839 dbgln("herror(): {}: {}", s, hstrerror(h_errno));
840 warnln("{}: {}", s, hstrerror(h_errno));
841}
842
843char const* hstrerror(int err)
844{
845 switch (err) {
846 case HOST_NOT_FOUND:
847 return "The specified host is unknown.";
848 case NO_DATA:
849 return "The requested name is valid but does not have an IP address.";
850 case NO_RECOVERY:
851 return "A nonrecoverable name server error occurred.";
852 case TRY_AGAIN:
853 return "A temporary error occurred on an authoritative name server. Try again later.";
854 default:
855 return "Unknown error.";
856 }
857}
858}