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
35scoped_free_addrinfo::~scoped_free_addrinfo ()
36{
37  freeaddrinfo (m_res);
38}
39
40/* See gdbsupport/netstuff.h.  */
41
42parsed_connection_spec
43parse_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
116parsed_connection_spec
117parse_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