Serenity Operating System
at master 858 lines 26 kB view raw
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}