Remove some checks of .empty()
[external/binutils.git] / gdb / gdbsupport / netstuff.c
1 /* Operations on network stuff.
2    Copyright (C) 2018-2019 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 }