18dffb485Schristos /* Operations on network stuff. 2*5ba1f45fSchristos Copyright (C) 2018-2024 Free Software Foundation, Inc. 38dffb485Schristos 48dffb485Schristos This file is part of GDB. 58dffb485Schristos 68dffb485Schristos This program is free software; you can redistribute it and/or modify 78dffb485Schristos it under the terms of the GNU General Public License as published by 88dffb485Schristos the Free Software Foundation; either version 3 of the License, or 98dffb485Schristos (at your option) any later version. 108dffb485Schristos 118dffb485Schristos This program is distributed in the hope that it will be useful, 128dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 138dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148dffb485Schristos GNU General Public License for more details. 158dffb485Schristos 168dffb485Schristos You should have received a copy of the GNU General Public License 178dffb485Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 188dffb485Schristos 198dffb485Schristos #include "netstuff.h" 208dffb485Schristos #include <algorithm> 218dffb485Schristos 228dffb485Schristos #ifdef USE_WIN32API 238dffb485Schristos #include <ws2tcpip.h> 248dffb485Schristos #else 258dffb485Schristos #include <netinet/in.h> 268dffb485Schristos #include <arpa/inet.h> 278dffb485Schristos #include <netdb.h> 288dffb485Schristos #include <sys/socket.h> 298dffb485Schristos #include <netinet/tcp.h> 308dffb485Schristos #endif 318dffb485Schristos 328dffb485Schristos /* See gdbsupport/netstuff.h. */ 338dffb485Schristos 348dffb485Schristos scoped_free_addrinfo::~scoped_free_addrinfo () 358dffb485Schristos { 368dffb485Schristos freeaddrinfo (m_res); 378dffb485Schristos } 388dffb485Schristos 398dffb485Schristos /* See gdbsupport/netstuff.h. */ 408dffb485Schristos 418dffb485Schristos parsed_connection_spec 428dffb485Schristos parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint) 438dffb485Schristos { 448dffb485Schristos parsed_connection_spec ret; 458dffb485Schristos size_t last_colon_pos = 0; 468dffb485Schristos /* We're dealing with IPv6 if: 478dffb485Schristos 488dffb485Schristos - ai_family is AF_INET6, or 498dffb485Schristos - ai_family is not AF_INET, and 508dffb485Schristos - spec[0] is '[', or 518dffb485Schristos - the number of ':' on spec is greater than 1. */ 528dffb485Schristos bool is_ipv6 = (hint->ai_family == AF_INET6 538dffb485Schristos || (hint->ai_family != AF_INET 548dffb485Schristos && (spec[0] == '[' 558dffb485Schristos || std::count (spec.begin (), 568dffb485Schristos spec.end (), ':') > 1))); 578dffb485Schristos 588dffb485Schristos if (is_ipv6) 598dffb485Schristos { 608dffb485Schristos if (spec[0] == '[') 618dffb485Schristos { 628dffb485Schristos /* IPv6 addresses can be written as '[ADDR]:PORT', and we 638dffb485Schristos support this notation. */ 648dffb485Schristos size_t close_bracket_pos = spec.find_first_of (']'); 658dffb485Schristos 668dffb485Schristos if (close_bracket_pos == std::string::npos) 678dffb485Schristos error (_("Missing close bracket in hostname '%s'"), 688dffb485Schristos spec.c_str ()); 698dffb485Schristos 708dffb485Schristos hint->ai_family = AF_INET6; 718dffb485Schristos 728dffb485Schristos const char c = spec[close_bracket_pos + 1]; 738dffb485Schristos 748dffb485Schristos if (c == '\0') 758dffb485Schristos last_colon_pos = std::string::npos; 768dffb485Schristos else if (c != ':') 778dffb485Schristos error (_("Invalid cruft after close bracket in '%s'"), 788dffb485Schristos spec.c_str ()); 798dffb485Schristos 808dffb485Schristos /* Erase both '[' and ']'. */ 818dffb485Schristos spec.erase (0, 1); 828dffb485Schristos spec.erase (close_bracket_pos - 1, 1); 838dffb485Schristos } 848dffb485Schristos else if (spec.find_first_of (']') != std::string::npos) 858dffb485Schristos error (_("Missing open bracket in hostname '%s'"), 868dffb485Schristos spec.c_str ()); 878dffb485Schristos } 888dffb485Schristos 898dffb485Schristos if (last_colon_pos == 0) 908dffb485Schristos last_colon_pos = spec.find_last_of (':'); 918dffb485Schristos 928dffb485Schristos /* The length of the hostname part. */ 938dffb485Schristos size_t host_len; 948dffb485Schristos 958dffb485Schristos if (last_colon_pos != std::string::npos) 968dffb485Schristos { 978dffb485Schristos /* The user has provided a port. */ 988dffb485Schristos host_len = last_colon_pos; 998dffb485Schristos ret.port_str = spec.substr (last_colon_pos + 1); 1008dffb485Schristos } 1018dffb485Schristos else 1028dffb485Schristos host_len = spec.size (); 1038dffb485Schristos 1048dffb485Schristos ret.host_str = spec.substr (0, host_len); 1058dffb485Schristos 1068dffb485Schristos /* Default hostname is localhost. */ 1078dffb485Schristos if (ret.host_str.empty ()) 1088dffb485Schristos ret.host_str = "localhost"; 1098dffb485Schristos 1108dffb485Schristos return ret; 1118dffb485Schristos } 1128dffb485Schristos 1138dffb485Schristos /* See gdbsupport/netstuff.h. */ 1148dffb485Schristos 1158dffb485Schristos parsed_connection_spec 1168dffb485Schristos parse_connection_spec (const char *spec, struct addrinfo *hint) 1178dffb485Schristos { 1188dffb485Schristos /* Struct to hold the association between valid prefixes, their 1198dffb485Schristos family and socktype. */ 1208dffb485Schristos struct host_prefix 1218dffb485Schristos { 1228dffb485Schristos /* The prefix. */ 1238dffb485Schristos const char *prefix; 1248dffb485Schristos 1258dffb485Schristos /* The 'ai_family'. */ 1268dffb485Schristos int family; 1278dffb485Schristos 1288dffb485Schristos /* The 'ai_socktype'. */ 1298dffb485Schristos int socktype; 1308dffb485Schristos }; 1318dffb485Schristos static const struct host_prefix prefixes[] = 1328dffb485Schristos { 1338dffb485Schristos { "udp:", AF_UNSPEC, SOCK_DGRAM }, 1348dffb485Schristos { "tcp:", AF_UNSPEC, SOCK_STREAM }, 1358dffb485Schristos { "udp4:", AF_INET, SOCK_DGRAM }, 1368dffb485Schristos { "tcp4:", AF_INET, SOCK_STREAM }, 1378dffb485Schristos { "udp6:", AF_INET6, SOCK_DGRAM }, 1388dffb485Schristos { "tcp6:", AF_INET6, SOCK_STREAM }, 1398dffb485Schristos }; 1408dffb485Schristos 1418dffb485Schristos for (const host_prefix prefix : prefixes) 1428dffb485Schristos if (startswith (spec, prefix.prefix)) 1438dffb485Schristos { 1448dffb485Schristos spec += strlen (prefix.prefix); 1458dffb485Schristos hint->ai_family = prefix.family; 1468dffb485Schristos hint->ai_socktype = prefix.socktype; 1478dffb485Schristos hint->ai_protocol 1488dffb485Schristos = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; 1498dffb485Schristos break; 1508dffb485Schristos } 1518dffb485Schristos 1528dffb485Schristos return parse_connection_spec_without_prefix (spec, hint); 1538dffb485Schristos } 154