Home | History | Annotate | Line # | Download | only in gdbsupport
netstuff.cc revision 1.1
      1 /* Operations on network stuff.
      2    Copyright (C) 2018-2020 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 "common-defs.h"
     20 #include "netstuff.h"
     21 #include <algorithm>
     22 
     23 #ifdef USE_WIN32API
     24 #include <ws2tcpip.h>
     25 #else
     26 #include <netinet/in.h>
     27 #include <arpa/inet.h>
     28 #include <netdb.h>
     29 #include <sys/socket.h>
     30 #include <netinet/tcp.h>
     31 #endif
     32 
     33 /* See gdbsupport/netstuff.h.  */
     34 
     35 scoped_free_addrinfo::~scoped_free_addrinfo ()
     36 {
     37   freeaddrinfo (m_res);
     38 }
     39 
     40 /* See gdbsupport/netstuff.h.  */
     41 
     42 parsed_connection_spec
     43 parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint)
     44 {
     45   parsed_connection_spec ret;
     46   size_t last_colon_pos = 0;
     47   /* We're dealing with IPv6 if:
     48 
     49      - ai_family is AF_INET6, or
     50      - ai_family is not AF_INET, and
     51        - spec[0] is '[', or
     52        - the number of ':' on spec is greater than 1.  */
     53   bool is_ipv6 = (hint->ai_family == AF_INET6
     54 		  || (hint->ai_family != AF_INET
     55 		      && (spec[0] == '['
     56 			  || std::count (spec.begin (),
     57 					 spec.end (), ':') > 1)));
     58 
     59   if (is_ipv6)
     60     {
     61       if (spec[0] == '[')
     62 	{
     63 	  /* IPv6 addresses can be written as '[ADDR]:PORT', and we
     64 	     support this notation.  */
     65 	  size_t close_bracket_pos = spec.find_first_of (']');
     66 
     67 	  if (close_bracket_pos == std::string::npos)
     68 	    error (_("Missing close bracket in hostname '%s'"),
     69 		   spec.c_str ());
     70 
     71 	  hint->ai_family = AF_INET6;
     72 
     73 	  const char c = spec[close_bracket_pos + 1];
     74 
     75 	  if (c == '\0')
     76 	    last_colon_pos = std::string::npos;
     77 	  else if (c != ':')
     78 	    error (_("Invalid cruft after close bracket in '%s'"),
     79 		   spec.c_str ());
     80 
     81 	  /* Erase both '[' and ']'.  */
     82 	  spec.erase (0, 1);
     83 	  spec.erase (close_bracket_pos - 1, 1);
     84 	}
     85       else if (spec.find_first_of (']') != std::string::npos)
     86 	error (_("Missing open bracket in hostname '%s'"),
     87 	       spec.c_str ());
     88     }
     89 
     90   if (last_colon_pos == 0)
     91     last_colon_pos = spec.find_last_of (':');
     92 
     93   /* The length of the hostname part.  */
     94   size_t host_len;
     95 
     96   if (last_colon_pos != std::string::npos)
     97     {
     98       /* The user has provided a port.  */
     99       host_len = last_colon_pos;
    100       ret.port_str = spec.substr (last_colon_pos + 1);
    101     }
    102   else
    103     host_len = spec.size ();
    104 
    105   ret.host_str = spec.substr (0, host_len);
    106 
    107   /* Default hostname is localhost.  */
    108   if (ret.host_str.empty ())
    109     ret.host_str = "localhost";
    110 
    111   return ret;
    112 }
    113 
    114 /* See gdbsupport/netstuff.h.  */
    115 
    116 parsed_connection_spec
    117 parse_connection_spec (const char *spec, struct addrinfo *hint)
    118 {
    119   /* Struct to hold the association between valid prefixes, their
    120      family and socktype.  */
    121   struct host_prefix
    122     {
    123       /* The prefix.  */
    124       const char *prefix;
    125 
    126       /* The 'ai_family'.  */
    127       int family;
    128 
    129       /* The 'ai_socktype'.  */
    130       int socktype;
    131     };
    132   static const struct host_prefix prefixes[] =
    133     {
    134       { "udp:",  AF_UNSPEC, SOCK_DGRAM },
    135       { "tcp:",  AF_UNSPEC, SOCK_STREAM },
    136       { "udp4:", AF_INET,   SOCK_DGRAM },
    137       { "tcp4:", AF_INET,   SOCK_STREAM },
    138       { "udp6:", AF_INET6,  SOCK_DGRAM },
    139       { "tcp6:", AF_INET6,  SOCK_STREAM },
    140     };
    141 
    142   for (const host_prefix prefix : prefixes)
    143     if (startswith (spec, prefix.prefix))
    144       {
    145 	spec += strlen (prefix.prefix);
    146 	hint->ai_family = prefix.family;
    147 	hint->ai_socktype = prefix.socktype;
    148 	hint->ai_protocol
    149 	  = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
    150 	break;
    151       }
    152 
    153   return parse_connection_spec_without_prefix (spec, hint);
    154 }
    155