xref: /netbsd-src/external/gpl3/gdb.old/dist/gdbsupport/netstuff.cc (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
17d62b00eSchristos /* Operations on network stuff.
2*6881a400Schristos    Copyright (C) 2018-2023 Free Software Foundation, Inc.
37d62b00eSchristos 
47d62b00eSchristos    This file is part of GDB.
57d62b00eSchristos 
67d62b00eSchristos    This program is free software; you can redistribute it and/or modify
77d62b00eSchristos    it under the terms of the GNU General Public License as published by
87d62b00eSchristos    the Free Software Foundation; either version 3 of the License, or
97d62b00eSchristos    (at your option) any later version.
107d62b00eSchristos 
117d62b00eSchristos    This program is distributed in the hope that it will be useful,
127d62b00eSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
137d62b00eSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147d62b00eSchristos    GNU General Public License for more details.
157d62b00eSchristos 
167d62b00eSchristos    You should have received a copy of the GNU General Public License
177d62b00eSchristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
187d62b00eSchristos 
197d62b00eSchristos #include "common-defs.h"
207d62b00eSchristos #include "netstuff.h"
217d62b00eSchristos #include <algorithm>
227d62b00eSchristos 
237d62b00eSchristos #ifdef USE_WIN32API
247d62b00eSchristos #include <ws2tcpip.h>
257d62b00eSchristos #else
267d62b00eSchristos #include <netinet/in.h>
277d62b00eSchristos #include <arpa/inet.h>
287d62b00eSchristos #include <netdb.h>
297d62b00eSchristos #include <sys/socket.h>
307d62b00eSchristos #include <netinet/tcp.h>
317d62b00eSchristos #endif
327d62b00eSchristos 
337d62b00eSchristos /* See gdbsupport/netstuff.h.  */
347d62b00eSchristos 
357d62b00eSchristos scoped_free_addrinfo::~scoped_free_addrinfo ()
367d62b00eSchristos {
377d62b00eSchristos   freeaddrinfo (m_res);
387d62b00eSchristos }
397d62b00eSchristos 
407d62b00eSchristos /* See gdbsupport/netstuff.h.  */
417d62b00eSchristos 
427d62b00eSchristos parsed_connection_spec
437d62b00eSchristos parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint)
447d62b00eSchristos {
457d62b00eSchristos   parsed_connection_spec ret;
467d62b00eSchristos   size_t last_colon_pos = 0;
477d62b00eSchristos   /* We're dealing with IPv6 if:
487d62b00eSchristos 
497d62b00eSchristos      - ai_family is AF_INET6, or
507d62b00eSchristos      - ai_family is not AF_INET, and
517d62b00eSchristos        - spec[0] is '[', or
527d62b00eSchristos        - the number of ':' on spec is greater than 1.  */
537d62b00eSchristos   bool is_ipv6 = (hint->ai_family == AF_INET6
547d62b00eSchristos 		  || (hint->ai_family != AF_INET
557d62b00eSchristos 		      && (spec[0] == '['
567d62b00eSchristos 			  || std::count (spec.begin (),
577d62b00eSchristos 					 spec.end (), ':') > 1)));
587d62b00eSchristos 
597d62b00eSchristos   if (is_ipv6)
607d62b00eSchristos     {
617d62b00eSchristos       if (spec[0] == '[')
627d62b00eSchristos 	{
637d62b00eSchristos 	  /* IPv6 addresses can be written as '[ADDR]:PORT', and we
647d62b00eSchristos 	     support this notation.  */
657d62b00eSchristos 	  size_t close_bracket_pos = spec.find_first_of (']');
667d62b00eSchristos 
677d62b00eSchristos 	  if (close_bracket_pos == std::string::npos)
687d62b00eSchristos 	    error (_("Missing close bracket in hostname '%s'"),
697d62b00eSchristos 		   spec.c_str ());
707d62b00eSchristos 
717d62b00eSchristos 	  hint->ai_family = AF_INET6;
727d62b00eSchristos 
737d62b00eSchristos 	  const char c = spec[close_bracket_pos + 1];
747d62b00eSchristos 
757d62b00eSchristos 	  if (c == '\0')
767d62b00eSchristos 	    last_colon_pos = std::string::npos;
777d62b00eSchristos 	  else if (c != ':')
787d62b00eSchristos 	    error (_("Invalid cruft after close bracket in '%s'"),
797d62b00eSchristos 		   spec.c_str ());
807d62b00eSchristos 
817d62b00eSchristos 	  /* Erase both '[' and ']'.  */
827d62b00eSchristos 	  spec.erase (0, 1);
837d62b00eSchristos 	  spec.erase (close_bracket_pos - 1, 1);
847d62b00eSchristos 	}
857d62b00eSchristos       else if (spec.find_first_of (']') != std::string::npos)
867d62b00eSchristos 	error (_("Missing open bracket in hostname '%s'"),
877d62b00eSchristos 	       spec.c_str ());
887d62b00eSchristos     }
897d62b00eSchristos 
907d62b00eSchristos   if (last_colon_pos == 0)
917d62b00eSchristos     last_colon_pos = spec.find_last_of (':');
927d62b00eSchristos 
937d62b00eSchristos   /* The length of the hostname part.  */
947d62b00eSchristos   size_t host_len;
957d62b00eSchristos 
967d62b00eSchristos   if (last_colon_pos != std::string::npos)
977d62b00eSchristos     {
987d62b00eSchristos       /* The user has provided a port.  */
997d62b00eSchristos       host_len = last_colon_pos;
1007d62b00eSchristos       ret.port_str = spec.substr (last_colon_pos + 1);
1017d62b00eSchristos     }
1027d62b00eSchristos   else
1037d62b00eSchristos     host_len = spec.size ();
1047d62b00eSchristos 
1057d62b00eSchristos   ret.host_str = spec.substr (0, host_len);
1067d62b00eSchristos 
1077d62b00eSchristos   /* Default hostname is localhost.  */
1087d62b00eSchristos   if (ret.host_str.empty ())
1097d62b00eSchristos     ret.host_str = "localhost";
1107d62b00eSchristos 
1117d62b00eSchristos   return ret;
1127d62b00eSchristos }
1137d62b00eSchristos 
1147d62b00eSchristos /* See gdbsupport/netstuff.h.  */
1157d62b00eSchristos 
1167d62b00eSchristos parsed_connection_spec
1177d62b00eSchristos parse_connection_spec (const char *spec, struct addrinfo *hint)
1187d62b00eSchristos {
1197d62b00eSchristos   /* Struct to hold the association between valid prefixes, their
1207d62b00eSchristos      family and socktype.  */
1217d62b00eSchristos   struct host_prefix
1227d62b00eSchristos     {
1237d62b00eSchristos       /* The prefix.  */
1247d62b00eSchristos       const char *prefix;
1257d62b00eSchristos 
1267d62b00eSchristos       /* The 'ai_family'.  */
1277d62b00eSchristos       int family;
1287d62b00eSchristos 
1297d62b00eSchristos       /* The 'ai_socktype'.  */
1307d62b00eSchristos       int socktype;
1317d62b00eSchristos     };
1327d62b00eSchristos   static const struct host_prefix prefixes[] =
1337d62b00eSchristos     {
1347d62b00eSchristos       { "udp:",  AF_UNSPEC, SOCK_DGRAM },
1357d62b00eSchristos       { "tcp:",  AF_UNSPEC, SOCK_STREAM },
1367d62b00eSchristos       { "udp4:", AF_INET,   SOCK_DGRAM },
1377d62b00eSchristos       { "tcp4:", AF_INET,   SOCK_STREAM },
1387d62b00eSchristos       { "udp6:", AF_INET6,  SOCK_DGRAM },
1397d62b00eSchristos       { "tcp6:", AF_INET6,  SOCK_STREAM },
1407d62b00eSchristos     };
1417d62b00eSchristos 
1427d62b00eSchristos   for (const host_prefix prefix : prefixes)
1437d62b00eSchristos     if (startswith (spec, prefix.prefix))
1447d62b00eSchristos       {
1457d62b00eSchristos 	spec += strlen (prefix.prefix);
1467d62b00eSchristos 	hint->ai_family = prefix.family;
1477d62b00eSchristos 	hint->ai_socktype = prefix.socktype;
1487d62b00eSchristos 	hint->ai_protocol
1497d62b00eSchristos 	  = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
1507d62b00eSchristos 	break;
1517d62b00eSchristos       }
1527d62b00eSchristos 
1537d62b00eSchristos   return parse_connection_spec_without_prefix (spec, hint);
1547d62b00eSchristos }
155