xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/netstuff.cc (revision 4439cfd0acf9c7dc90625e5cd83b2317a9ab8967)
1 /* Operations on network stuff.
2    Copyright (C) 2018-2024 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