xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/netstuff.cc (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
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