aboutsummaryrefslogtreecommitdiff
path: root/gdbsupport/netstuff.cc
blob: 55b142df7f58590c713c1393001738478b5f672a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* Operations on network stuff.
   Copyright (C) 2018-2023 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "common-defs.h"
#include "netstuff.h"
#include <algorithm>

#ifdef USE_WIN32API
#include <ws2tcpip.h>
#else
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#endif

/* See gdbsupport/netstuff.h.  */

scoped_free_addrinfo::~scoped_free_addrinfo ()
{
  freeaddrinfo (m_res);
}

/* See gdbsupport/netstuff.h.  */

parsed_connection_spec
parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint)
{
  parsed_connection_spec ret;
  size_t last_colon_pos = 0;
  /* We're dealing with IPv6 if:

     - ai_family is AF_INET6, or
     - ai_family is not AF_INET, and
       - spec[0] is '[', or
       - the number of ':' on spec is greater than 1.  */
  bool is_ipv6 = (hint->ai_family == AF_INET6
		  || (hint->ai_family != AF_INET
		      && (spec[0] == '['
			  || std::count (spec.begin (),
					 spec.end (), ':') > 1)));

  if (is_ipv6)
    {
      if (spec[0] == '[')
	{
	  /* IPv6 addresses can be written as '[ADDR]:PORT', and we
	     support this notation.  */
	  size_t close_bracket_pos = spec.find_first_of (']');

	  if (close_bracket_pos == std::string::npos)
	    error (_("Missing close bracket in hostname '%s'"),
		   spec.c_str ());

	  hint->ai_family = AF_INET6;

	  const char c = spec[close_bracket_pos + 1];

	  if (c == '\0')
	    last_colon_pos = std::string::npos;
	  else if (c != ':')
	    error (_("Invalid cruft after close bracket in '%s'"),
		   spec.c_str ());

	  /* Erase both '[' and ']'.  */
	  spec.erase (0, 1);
	  spec.erase (close_bracket_pos - 1, 1);
	}
      else if (spec.find_first_of (']') != std::string::npos)
	error (_("Missing open bracket in hostname '%s'"),
	       spec.c_str ());
    }

  if (last_colon_pos == 0)
    last_colon_pos = spec.find_last_of (':');

  /* The length of the hostname part.  */
  size_t host_len;

  if (last_colon_pos != std::string::npos)
    {
      /* The user has provided a port.  */
      host_len = last_colon_pos;
      ret.port_str = spec.substr (last_colon_pos + 1);
    }
  else
    host_len = spec.size ();

  ret.host_str = spec.substr (0, host_len);

  /* Default hostname is localhost.  */
  if (ret.host_str.empty ())
    ret.host_str = "localhost";

  return ret;
}

/* See gdbsupport/netstuff.h.  */

parsed_connection_spec
parse_connection_spec (const char *spec, struct addrinfo *hint)
{
  /* Struct to hold the association between valid prefixes, their
     family and socktype.  */
  struct host_prefix
    {
      /* The prefix.  */
      const char *prefix;

      /* The 'ai_family'.  */
      int family;

      /* The 'ai_socktype'.  */
      int socktype;
    };
  static const struct host_prefix prefixes[] =
    {
      { "udp:",  AF_UNSPEC, SOCK_DGRAM },
      { "tcp:",  AF_UNSPEC, SOCK_STREAM },
      { "udp4:", AF_INET,   SOCK_DGRAM },
      { "tcp4:", AF_INET,   SOCK_STREAM },
      { "udp6:", AF_INET6,  SOCK_DGRAM },
      { "tcp6:", AF_INET6,  SOCK_STREAM },
    };

  for (const host_prefix prefix : prefixes)
    if (startswith (spec, prefix.prefix))
      {
	spec += strlen (prefix.prefix);
	hint->ai_family = prefix.family;
	hint->ai_socktype = prefix.socktype;
	hint->ai_protocol
	  = hint->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
	break;
      }

  return parse_connection_spec_without_prefix (spec, hint);
}