Home | History | Annotate | Line # | Download | only in gdbsupport
netstuff.cc revision 1.1.1.4
      1 /* Operations on network stuff.
      2    Copyright (C) 2018-2025 Free Software Foundation, Inc.
      3 
      4    This file is part of GDB.
      5 
      6    This program is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3 of the License, or
      9    (at your option) any later version.
     10 
     11    This program is distributed in the hope that it will be useful,
     12    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     18 
     19 #include "netstuff.h"
     20 #include <algorithm>
     21 
     22 #ifdef USE_WIN32API
     23 #include <ws2tcpip.h>
     24 #else
     25 #include <netinet/in.h>
     26 #include <arpa/inet.h>
     27 #include <netdb.h>
     28 #include <sys/socket.h>
     29 #include <netinet/tcp.h>
     30 #endif
     31 
     32 /* See gdbsupport/netstuff.h.  */
     33 
     34 scoped_free_addrinfo::~scoped_free_addrinfo ()
     35 {
     36   freeaddrinfo (m_res);
     37 }
     38 
     39 /* See gdbsupport/netstuff.h.  */
     40 
     41 parsed_connection_spec
     42 parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint)
     43 {
     44   parsed_connection_spec ret;
     45   size_t last_colon_pos = 0;
     46   /* We're dealing with IPv6 if:
     47 
     48      - ai_family is AF_INET6, or
     49      - ai_family is not AF_INET, and
     50        - spec[0] is '[', or
     51        - the number of ':' on spec is greater than 1.  */
     52   bool is_ipv6 = (hint->ai_family == AF_INET6
     53 		  || (hint->ai_family != AF_INET
     54 		      && (spec[0] == '['
     55 			  || std::count (spec.begin (),
     56 					 spec.end (), ':') > 1)));
     57 
     58   if (is_ipv6)
     59     {
     60       if (spec[0] == '[')
     61 	{
     62 	  /* IPv6 addresses can be written as '[ADDR]:PORT', and we
     63 	     support this notation.  */
     64 	  size_t close_bracket_pos = spec.find_first_of (']');
     65 
     66 	  if (close_bracket_pos == std::string::npos)
     67 	    error (_("Missing close bracket in hostname '%s'"),
     68 		   spec.c_str ());
     69 
     70 	  hint->ai_family = AF_INET6;
     71 
     72 	  const char c = spec[close_bracket_pos + 1];
     73 
     74 	  if (c == '\0')
     75 	    last_colon_pos = std::string::npos;
     76 	  else if (c != ':')
     77 	    error (_("Invalid cruft after close bracket in '%s'"),
     78 		   spec.c_str ());
     79 
     80 	  /* Erase both '[' and ']'.  */
     81 	  spec.erase (0, 1);
     82 	  spec.erase (close_bracket_pos - 1, 1);
     83 	}
     84       else if (spec.find_first_of (']') != std::string::npos)
     85 	error (_("Missing open bracket in hostname '%s'"),
     86 	       spec.c_str ());
     87     }
     88 
     89   if (last_colon_pos == 0)
     90     last_colon_pos = spec.find_last_of (':');
     91 
     92   /* The length of the hostname part.  */
     93   size_t host_len;
     94 
     95   if (last_colon_pos != std::string::npos)
     96     {
     97       /* The user has provided a port.  */
     98       host_len = last_colon_pos;
     99       ret.port_str = spec.substr (last_colon_pos + 1);
    100     }
    101   else
    102     host_len = spec.size ();
    103 
    104   ret.host_str = spec.substr (0, host_len);
    105 
    106   /* Default hostname is localhost.  */
    107   if (ret.host_str.empty ())
    108     ret.host_str = "localhost";
    109 
    110   return ret;
    111 }
    112 
    113 /* See gdbsupport/netstuff.h.  */
    114 
    115 parsed_connection_spec
    116 parse_connection_spec (const char *spec, struct addrinfo *hint)
    117 {
    118   /* Struct to hold the association between valid prefixes, their
    119      family and socktype.  */
    120   struct host_prefix
    121     {
    122       /* The prefix.  */
    123       const char *prefix;
    124 
    125       /* The 'ai_family'.  */
    126       int family;
    127 
    128       /* The 'ai_socktype'.  */
    129       int socktype;
    130     };
    131   static const struct host_prefix prefixes[] =
    132     {
    133       { "udp:",  AF_UNSPEC, SOCK_DGRAM },
    134       { "tcp:",  AF_UNSPEC, SOCK_STREAM },
    135       { "udp4:", AF_INET,   SOCK_DGRAM },
    136       { "tcp4:", AF_INET,   SOCK_STREAM },
    137       { "udp6:", AF_INET6,  SOCK_DGRAM },
    138       { "tcp6:", AF_INET6,  SOCK_STREAM },
    139     };
    140 
    141   for (const host_prefix prefix : prefixes)
    142     if (startswith (spec, prefix.prefix))
    143       {
    144 	spec += strlen (prefix.prefix);
    145 	hint->ai_family = prefix.family;
    146 	hint->ai_socktype = prefix.socktype;
    147 	hint->ai_protocol
    148 	  = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
    149 	break;
    150       }
    151 
    152   return parse_connection_spec_without_prefix (spec, hint);
    153 }
    154