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