public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Implement IPv6 support for GDB/gdbserver
@ 2018-05-23 21:48 Sergio Durigan Junior
  2018-05-23 23:40 ` Eli Zaretskii
                   ` (5 more replies)
  0 siblings, 6 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-05-23 21:48 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Sergio Durigan Junior

This patch implements IPv6 support for both GDB and gdbserver.  Based
on my research, it is the fourth attempt to do that since 2006.  Since
I used ideas from all of the previous patches, I also added their
authors's names on the ChangeLogs as a way to recognize their
efforts.  For reference sake, you can find the previous attempts at:

  https://sourceware.org/ml/gdb-patches/2006-09/msg00192.html

  https://sourceware.org/ml/gdb-patches/2014-02/msg00248.html

  https://sourceware.org/ml/gdb-patches/2016-02/msg00226.html

The basic idea behind the patch is to start using the new
'getaddrinfo'/'getnameinfo' calls, which are responsible for
translating names and addresses in a protocol-independent way.  This
means that if we ever have an IPv8, we won't need to change the code
again.

The function 'getaddrinfo' returns a linked list of possible addresses
to connect to, so I modified ser-tcp.c:net_open's code to loop through
the linked list and try all the addresses until it finds a valid one.
The same rationale was used for gdbserver, but without the "retry"
mechanism that GDB has.

I also implemented some hostname parsing functions which are used to
help GDB and gdbserver to parse hostname strings provided by the user.
These new functions are living inside common/netstuff.[ch].  I've had
to do that since IPv6 introduces a new URL scheme, which defines that
square brackets can be used to enclose the host part and differentiate
it from the port (e.g., "[::1]:1234" means "host ::1, port 1234").  I
spent some time thinking about a reasonable way to interpret what the
user wants, and I came up with the following:

- If the user has provided a prefix that doesn't specify the protocol
  version (i.e., "tcp:" or "udp:"), or if the user has not provided
  any prefix, don't make any assumptions (i.e., assume AF_UNSPEC when
  dealing with 'getaddrinfo') *unless* the host starts with "[" (in
  which case, assume it's an IPv6 host).

- If the user has provided a prefix that does specify the protocol
  version (i.e., "tcp4:", "tcp6:", "udp4:" or "udp6:"), then respect
  that.

This method doesn't follow strictly what RFC 2732 proposes (that
literal IPv6 addresses should be provided enclosed in "[" and "]")
because IPv6 addresses still can be provided without square brackets
in our case, but since we have prefixes to specify protocol versions I
think this is not an issue.

Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
parameter, which instructs GDB and gdbserver to use IPv6 for
connections.  This way, if you want to run IPv6 tests, you do:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'

This required a few changes on the gdbserver-base.exp,
native-gdbserver.exp and native-extended-gdbserver.exp boards, and
also a minimal adjustment on gdb.server/run-without-local-binary.exp.

This patch has been regression-tested on BuildBot and locally, and
also built using a x86_64-w64-mingw32 GCC, and no problems were found.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (COMMON_SFILES): Add 'common/netstuff.c'.
	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
	* NEWS (Changes since GDB 8.1): Mention IPv6 support.
	* common/netstuff.c: New file.
	* common/netstuff.h: New file.
	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
	(net_open): Handle IPv6-style hostnames; implement support for
	IPv6 connections.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
	(OBS): Add 'common/netstuff.o'.
	* gdbreplay.c: Include 'wspiapi.h'.
	(remote_open): Implement support for IPv6
	connections.
	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
	and 'wspiapi.h'.
	(handle_accept_event): Accept connections from IPv6 sources.
	(remote_prepare): Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	(remote_open): Implement support for printing connections from
	IPv6 sources.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* README (Testsuite Parameters): Mention new 'GDB_TEST_IPV6'
	parameter.
	* boards/gdbserver-base.exp (get_comm_port_localhost_ipv6):
	New procedure.
	* boards/native-extended-gdbserver.exp: Detect 'GDB_TEST_IPV6'
	and change board info accordingly.
	* boards/native-gdbserver.exp: Likewise.
	* gdb.server/run-without-local-binary.exp: Improve regexp used
	for detecting when a remote debugging connection succeeds.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* gdb.texinfo (Remote Connection Commands): Add explanation
	about new IPv6 support.  Add new connection prefixes.
---
 gdb/Makefile.in                                    |   2 +
 gdb/NEWS                                           |   4 +
 gdb/common/netstuff.c                              | 136 +++++++++++++
 gdb/common/netstuff.h                              |  52 +++++
 gdb/doc/gdb.texinfo                                |  48 ++++-
 gdb/gdbserver/Makefile.in                          |   2 +
 gdb/gdbserver/gdbreplay.c                          | 181 +++++++++++++----
 gdb/gdbserver/remote-utils.c                       | 119 +++++++----
 gdb/ser-tcp.c                                      | 217 ++++++++++-----------
 gdb/testsuite/README                               |   7 +
 gdb/testsuite/boards/gdbserver-base.exp            |   5 +
 gdb/testsuite/boards/native-extended-gdbserver.exp |   7 +-
 gdb/testsuite/boards/native-gdbserver.exp          |   7 +-
 .../gdb.server/run-without-local-binary.exp        |   2 +-
 14 files changed, 602 insertions(+), 187 deletions(-)
 create mode 100644 gdb/common/netstuff.c
 create mode 100644 gdb/common/netstuff.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index df6ebab851..06ce12a4ee 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -962,6 +962,7 @@ COMMON_SFILES = \
 	common/job-control.c \
 	common/gdb_tilde_expand.c \
 	common/gdb_vecs.c \
+	common/netstuff.c \
 	common/new-op.c \
 	common/pathstuff.c \
 	common/print-utils.c \
@@ -1443,6 +1444,7 @@ HFILES_NO_SRCDIR = \
 	common/gdb_vecs.h \
 	common/gdb_wait.h \
 	common/common-inferior.h \
+	common/netstuff.h \
 	common/host-defs.h \
 	common/pathstuff.h \
 	common/print-utils.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index cef558039e..1f95ced912 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 8.1
 
+* GDB and GDBserver now support IPv6 connections.  IPv6 hostnames
+  can be passed using the '[ADDRESS]:PORT' notation, or the regular
+  'ADDRESS:PORT' method.
+
 * The commands 'info variables/functions/types' now show the source line
   numbers of symbol definitions when available.
 
diff --git a/gdb/common/netstuff.c b/gdb/common/netstuff.c
new file mode 100644
index 0000000000..cdf4b611db
--- /dev/null
+++ b/gdb/common/netstuff.c
@@ -0,0 +1,136 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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"
+
+#ifdef USE_WIN32API
+#include <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+/* See common/netstuff.h.  */
+
+scoped_free_addrinfo::scoped_free_addrinfo (struct addrinfo *a)
+{
+  m_res = a;
+}
+/* See common/netstuff.h.  */
+
+scoped_free_addrinfo::~scoped_free_addrinfo ()
+{
+  freeaddrinfo (m_res);
+}
+
+/* See common/netstuff.h.  */
+
+void
+parse_hostname_without_prefix (const char *hostname, std::string &host_str,
+			       std::string &port_str, struct addrinfo *hint)
+{
+  std::string strname (hostname);
+
+  if (hint->ai_family != AF_INET && strname[0] == '[')
+    {
+      /* IPv6 addresses can be written as '[ADDR]:PORT', and we
+	 support this notation.  */
+      size_t close_bracket_pos = strname.find_first_of (']');
+
+      if (close_bracket_pos == std::string::npos)
+	error (_("Missing close bracket in hostname '%s'"),
+	       strname.c_str ());
+
+      /* Erase both '[' and ']'.  */
+      strname.erase (0, 1);
+      strname.erase (close_bracket_pos - 1, 1);
+
+      hint->ai_family = AF_INET6;
+    }
+
+  /* The length of the hostname part.  */
+  size_t host_len;
+
+  size_t last_colon_pos = strname.find_last_of (':');
+
+  if (last_colon_pos != std::string::npos)
+    {
+      /* The user has provided a port.  */
+      host_len = last_colon_pos;
+      port_str = strname.substr (last_colon_pos + 1);
+    }
+  else
+    host_len = strname.size ();
+
+  host_str = strname.substr (0, host_len);
+
+  /* Default hostname is localhost.  */
+  if (host_str.empty ())
+    host_str = "localhost";
+}
+
+/* See common/netstuff.h.  */
+
+void
+parse_hostname (const char *hostname, std::string &host_str,
+		std::string &port_str, 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 },
+      { NULL, 0, 0 },
+    };
+
+  for (const struct host_prefix *prefix = prefixes;
+       prefix->prefix != NULL;
+       ++prefix)
+    if (startswith (hostname, prefix->prefix))
+      {
+	hostname += 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;
+      }
+
+  parse_hostname_without_prefix (hostname, host_str, port_str, hint);
+}
diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
new file mode 100644
index 0000000000..1ac2433f11
--- /dev/null
+++ b/gdb/common/netstuff.h
@@ -0,0 +1,52 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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/>.  */
+
+#ifndef NETSTUFF_H
+#define NETSTUFF_H
+
+#include <string>
+
+/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
+
+class scoped_free_addrinfo
+{
+public:
+  scoped_free_addrinfo (struct addrinfo *a);
+
+  ~scoped_free_addrinfo ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
+
+private:
+  struct addrinfo *m_res;
+};
+
+/* Parse HOSTNAME (which is a string in the of "ADDR:PORT") and fill
+   in HOST_STR, PORT_STR and HINT accordingly.  */
+extern void parse_hostname_without_prefix (const char *hostname,
+					   std::string &host_str,
+					   std::string &port_str,
+					   struct addrinfo *hint);
+
+/* Parse HOSTNAME (which is a string in the form of
+   "[tcp[6]:|udp[6]:]ADDR:PORT") and fill in HOST_STR, PORT_STR and
+   HINT accordingly.  */
+extern void parse_hostname (const char *hostname, std::string &host_str,
+			    std::string &port_str, struct addrinfo *hint);
+
+#endif /* ! NETSTUFF_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 28f083f96e..7994204140 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20496,16 +20496,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
 @code{target} command.
 
 @item target remote @code{@var{host}:@var{port}}
+@itemx target remote @code{@var{@r{[}host@r{]}}:@var{port}}
 @itemx target remote @code{tcp:@var{host}:@var{port}}
+@itemx target remote @code{tcp:@var{@r{[}host@r{]}}:@var{port}}
+@itemx target remote @code{tcp4:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{@r{[}host@r{]}}:@var{port}}
 @itemx target extended-remote @code{@var{host}:@var{port}}
+@itemx target extended-remote @code{@var{@r{[}host@r{]}}:@var{port}}
 @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp:@var{@r{[}host@r{]}}:@var{port}}
+@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{@r{[}host@r{]}}:@var{port}}
 @cindex @acronym{TCP} port, @code{target remote}
 Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
-The @var{host} may be either a host name or a numeric @acronym{IP}
-address; @var{port} must be a decimal number.  The @var{host} could be
-the target machine itself, if it is directly connected to the net, or
-it might be a terminal server which in turn has a serial line to the
-target.
+The @var{host} may be either a host name, a numeric @acronym{IPv4}
+address, or a numeric @acronym{IPv6} address (with or without the
+square brackets to separate the address from the port); @var{port}
+must be a decimal number.  The @var{host} could be the target machine
+itself, if it is directly connected to the net, or it might be a
+terminal server which in turn has a serial line to the target.
 
 For example, to connect to port 2828 on a terminal server named
 @code{manyfarms}:
@@ -20514,6 +20525,24 @@ For example, to connect to port 2828 on a terminal server named
 target remote manyfarms:2828
 @end smallexample
 
+To connect to port 2828 on a terminal server whose address is
+@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
+
+@smallexample
+target remote [2001::f8ff::67cf]:2828
+@end smallexample
+
+Or explicitly specify the @acronym{IPv6} protocol:
+
+@smallexample
+target remote tcp6:2001::f8ff::67cf:2828
+@end smallexample
+
+This last example may be confusing to the reader, because there is no
+visible separation between the hostname and the port number.
+Therefore, we recommend the user to provide @acronym{IPv6} addresses
+using square brackets for clarity.
+
 If your remote target is actually running on the same machine as your
 debugger session (e.g.@: a simulator for your target running on the
 same host), you can omit the hostname.  For example, to connect to
@@ -20527,7 +20556,16 @@ target remote :1234
 Note that the colon is still required here.
 
 @item target remote @code{udp:@var{host}:@var{port}}
+@itemx target remote @code{udp:@var{host}:@var{port}}
+@itemx target remote @code{udp:@var{@r{[}host@r{]}}:@var{port}}
+@itemx target remote @code{udp4:@var{host}:@var{port}}
+@itemx target remote @code{udp6:@var{@r{[}host@r{]}}:@var{port}}
+@itemx target extended-remote @code{udp:@var{host}:@var{port}}
 @itemx target extended-remote @code{udp:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp:@var{@r{[}host@r{]}}:@var{port}}
+@itemx target extended-remote @code{udp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{@r{[}host@r{]}}:@var{port}}
 @cindex @acronym{UDP} port, @code{target remote}
 Debug using @acronym{UDP} packets to @var{port} on @var{host}.  For example, to
 connect to @acronym{UDP} port 2828 on a terminal server named @code{manyfarms}:
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 675faa4364..d65346a357 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -211,6 +211,7 @@ SFILES = \
 	$(srcdir)/common/job-control.c \
 	$(srcdir)/common/gdb_tilde_expand.c \
 	$(srcdir)/common/gdb_vecs.c \
+	$(srcdir)/common/netstuff.c \
 	$(srcdir)/common/new-op.c \
 	$(srcdir)/common/pathstuff.c \
 	$(srcdir)/common/print-utils.c \
@@ -253,6 +254,7 @@ OBS = \
 	common/format.o \
 	common/gdb_tilde_expand.o \
 	common/gdb_vecs.o \
+	common/netstuff.o \
 	common/new-op.o \
 	common/pathstuff.o \
 	common/print-utils.o \
diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
index c1a639069a..01b70d49f4 100644
--- a/gdb/gdbserver/gdbreplay.c
+++ b/gdb/gdbserver/gdbreplay.c
@@ -53,6 +53,7 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
 #ifndef HAVE_SOCKLEN_T
@@ -175,56 +176,159 @@ remote_close (void)
 static void
 remote_open (char *name)
 {
-  if (!strchr (name, ':'))
+  char *last_colon = strrchr (name, ':');
+
+  if (last_colon == NULL)
     {
       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
       fflush (stderr);
       exit (1);
     }
-  else
-    {
+
 #ifdef USE_WIN32API
-      static int winsock_initialized;
+  static int winsock_initialized;
 #endif
-      char *port_str;
-      int port;
-      struct sockaddr_in sockaddr;
-      socklen_t tmp;
-      int tmp_desc;
+  char *port_str;
+  int tmp;
+  int tmp_desc;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+  char *orig_name = strdup (name);
+
+  struct prefix
+  {
+    /* The prefix to be parsed.  */
+    const char *str;
+
+    /* The address family.  */
+    int ai_family;
+
+    /* The socktype.  */
+    int ai_socktype;
+  };
+  static const struct 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 },
+	{ NULL, 0, 0 } };
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  for (const struct prefix *p = prefixes; p->str != NULL; ++p)
+    if (strncmp (name, p->str, strlen (p->str)) == 0)
+      {
+	name += strlen (p->str);
+	hint.ai_family = p->ai_family;
+	hint.ai_socktype = p->ai_socktype;
+	hint.ai_protocol
+	  = p->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+	break;
+      }
 
-      port_str = strchr (name, ':');
+  if (hint.ai_family != AF_INET && *name == '[')
+    {
+      ++name;
+      port_str = strchr (name, ']');
+      if (port_str == NULL)
+	{
+	  fprintf (stderr, "Missing closing bracket on hostname: %s\n",
+		   orig_name);
+	  exit (1);
+	}
+      /* Skip closing bracket.  */
+      *port_str = '\0';
+      ++port_str;
+      if (*port_str != ':')
+	{
+	  fprintf (stderr, "Missing port number on hostname: %s\n",
+		   orig_name);
+	  exit (1);
+	}
+      hint.ai_family = AF_INET6;
+    }
+  else
+    port_str = last_colon;
 
-      port = atoi (port_str + 1);
+  /* Skip the colon.  */
+  *port_str = '\0';
+  ++port_str;
 
 #ifdef USE_WIN32API
-      if (!winsock_initialized)
-	{
-	  WSADATA wsad;
+  if (!winsock_initialized)
+    {
+      WSADATA wsad;
 
-	  WSAStartup (MAKEWORD (1, 0), &wsad);
-	  winsock_initialized = 1;
-	}
+      WSAStartup (MAKEWORD (1, 0), &wsad);
+      winsock_initialized = 1;
+    }
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
-      if (tmp_desc == -1)
-	perror_with_name ("Can't open socket");
+  int r = getaddrinfo (name, port_str, &hint, &ainfo);
 
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-		  sizeof (tmp));
+  if (r != 0)
+    {
+      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+	       name, port_str, gai_strerror (r));
+      fflush (stderr);
+      exit (1);
+    }
 
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      sockaddr.sin_addr.s_addr = INADDR_ANY;
+  struct addrinfo *p;
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-	  || listen (tmp_desc, 1))
-	perror_with_name ("Can't bind address");
+  for (p = ainfo; p != NULL; p = p->ai_next)
+    {
+      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
+
+      if (tmp_desc >= 0)
+	break;
+    }
+
+  if (p == NULL)
+    perror_with_name ("Cannot open socket");
+
+  /* Allow rapid reuse of this port. */
+  tmp = 1;
+  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+	      sizeof (tmp));
+
+  switch (p->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+      exit (1);
+    }
+
+  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+    perror_with_name ("Can't bind address");
+
+  if (p->ai_socktype == SOCK_DGRAM)
+    remote_desc = tmp_desc;
+  else
+    {
+      struct sockaddr_storage sockaddr;
+      socklen_t sockaddrsize = sizeof (sockaddr);
+      char orig_host[64], orig_port[16];
+
+      if (listen (tmp_desc, 1) != 0)
+	perror_with_name ("Can't listen on socket");
+
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+			    &sockaddrsize);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
       if (remote_desc == -1)
 	perror_with_name ("Accept failed");
 
@@ -239,6 +343,16 @@ remote_open (char *name)
       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
 		  (char *) &tmp, sizeof (tmp));
 
+      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+	{
+	  fprintf (stderr, "Remote debugging from host %s, port %s\n",
+		   orig_host, orig_port);
+	  fflush (stderr);
+	}
+
 #ifndef USE_WIN32API
       close (tmp_desc);		/* No longer need this */
 
@@ -254,8 +368,9 @@ remote_open (char *name)
   fcntl (remote_desc, F_SETFL, FASYNC);
 #endif
 
-  fprintf (stderr, "Replay logfile using %s\n", name);
+  fprintf (stderr, "Replay logfile using %s\n", orig_name);
   fflush (stderr);
+  free (orig_name);
 }
 
 static int
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 3b5a459ae4..c8b5dcdbba 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -26,6 +26,8 @@
 #include "dll.h"
 #include "rsp-low.h"
 #include "gdbthread.h"
+#include "netstuff.h"
+#include "filestuff.h"
 #include <ctype.h>
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -63,6 +65,7 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
 #if __QNX__
@@ -156,19 +159,18 @@ enable_async_notification (int fd)
 static int
 handle_accept_event (int err, gdb_client_data client_data)
 {
-  struct sockaddr_in sockaddr;
-  socklen_t tmp;
+  struct sockaddr_storage sockaddr;
+  socklen_t len = sizeof (sockaddr);
 
   if (debug_threads)
     debug_printf ("handling possible accept event\n");
 
-  tmp = sizeof (sockaddr);
-  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
+  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
   if (remote_desc == -1)
     perror_with_name ("Accept failed");
 
   /* Enable TCP keep alive process. */
-  tmp = 1;
+  socklen_t tmp = 1;
   setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
 	      (char *) &tmp, sizeof (tmp));
 
@@ -197,8 +199,19 @@ handle_accept_event (int err, gdb_client_data client_data)
   delete_file_handler (listen_desc);
 
   /* Convert IP address to string.  */
-  fprintf (stderr, "Remote debugging from host %s\n",
-	   inet_ntoa (sockaddr.sin_addr));
+  char orig_host[64], orig_port[16];
+
+  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV);
+
+  if (r != 0)
+    fprintf (stderr, _("Could not obtain remote address: %s\n"),
+	     gai_strerror (r));
+  else
+    fprintf (stderr, "Remote debugging from host %s, port %s\n", orig_host,
+	     orig_port);
 
   enable_async_notification (remote_desc);
 
@@ -222,14 +235,10 @@ handle_accept_event (int err, gdb_client_data client_data)
 void
 remote_prepare (const char *name)
 {
-  const char *port_str;
 #ifdef USE_WIN32API
   static int winsock_initialized;
 #endif
-  int port;
-  struct sockaddr_in sockaddr;
   socklen_t tmp;
-  char *port_end;
 
   remote_is_stdio = 0;
   if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
@@ -242,17 +251,25 @@ remote_prepare (const char *name)
       return;
     }
 
-  port_str = strchr (name, ':');
-  if (port_str == NULL)
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+  std::string host_str, port_str;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parse_hostname_without_prefix (name, host_str, port_str, &hint);
+
+  if (port_str.empty ())
     {
       transport_is_reliable = 0;
       return;
     }
 
-  port = strtoul (port_str + 1, &port_end, 10);
-  if (port_str[1] == '\0' || *port_end != '\0')
-    error ("Bad port argument: %s", name);
-
 #ifdef USE_WIN32API
   if (!winsock_initialized)
     {
@@ -263,8 +280,25 @@ remote_prepare (const char *name)
     }
 #endif
 
-  listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (listen_desc == -1)
+  int r = getaddrinfo (host_str.c_str (), port_str.c_str (), &hint, &ainfo);
+
+  if (r != 0)
+    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+
+  scoped_free_addrinfo freeaddrinfo (ainfo);
+
+  struct addrinfo *iter;
+
+  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+    {
+      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+					iter->ai_protocol);
+
+      if (listen_desc >= 0)
+	break;
+    }
+
+  if (iter == NULL)
     perror_with_name ("Can't open socket");
 
   /* Allow rapid reuse of this port. */
@@ -272,14 +306,25 @@ remote_prepare (const char *name)
   setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
 	      sizeof (tmp));
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  sockaddr.sin_addr.s_addr = INADDR_ANY;
+  switch (iter->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
+    }
 
-  if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-      || listen (listen_desc, 1))
+  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
     perror_with_name ("Can't bind address");
 
+  if (listen (listen_desc, 1) != 0)
+    perror_with_name ("Can't listen on socket");
+
   transport_is_reliable = 1;
 }
 
@@ -354,18 +399,24 @@ remote_open (const char *name)
 #endif /* USE_WIN32API */
   else
     {
-      int port;
-      socklen_t len;
-      struct sockaddr_in sockaddr;
-
-      len = sizeof (sockaddr);
-      if (getsockname (listen_desc,
-		       (struct sockaddr *) &sockaddr, &len) < 0
-	  || len < sizeof (sockaddr))
+      char listen_port[16];
+      struct sockaddr_storage sockaddr;
+      socklen_t len = sizeof (sockaddr);
+
+      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
 	perror_with_name ("Can't determine port");
-      port = ntohs (sockaddr.sin_port);
 
-      fprintf (stderr, "Listening on port %d\n", port);
+      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+			   NULL, 0,
+			   listen_port, sizeof (listen_port),
+			   NI_NUMERICSERV);
+
+      if (r != 0)
+	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
+		 gai_strerror (r));
+      else
+	fprintf (stderr, "Listening on port %s\n", listen_port);
+
       fflush (stderr);
 
       /* Register the event loop handler.  */
diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index 23ef3b04b8..3d9fbd866f 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -25,6 +25,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
 #include "filestuff.h"
+#include "netstuff.h"
 
 #include <sys/types.h>
 
@@ -39,6 +40,7 @@
 
 #ifdef USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #ifndef ETIMEDOUT
 #define ETIMEDOUT WSAETIMEDOUT
 #endif
@@ -158,166 +160,157 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
 int
 net_open (struct serial *scb, const char *name)
 {
-  char hostname[100];
-  const char *port_str;
-  int n, port, tmp;
-  int use_udp;
-  struct hostent *hostent;
-  struct sockaddr_in sockaddr;
+  int n;
+  bool use_udp;
 #ifdef USE_WIN32API
   u_long ioarg;
 #else
   int ioarg;
 #endif
   unsigned int polls = 0;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+  std::string host_str, port_str;
 
-  use_udp = 0;
-  if (startswith (name, "udp:"))
-    {
-      use_udp = 1;
-      name = name + 4;
-    }
-  else if (startswith (name, "tcp:"))
-    name = name + 4;
-
-  port_str = strchr (name, ':');
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_INET;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
 
-  if (!port_str)
-    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
-						       happen.  */
+  parse_hostname (name, host_str, port_str, &hint);
 
-  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
-  strncpy (hostname, name, tmp);	/* Don't want colon.  */
-  hostname[tmp] = '\000';	/* Tie off host name.  */
-  port = atoi (port_str + 1);
+  if (port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
 
-  /* Default hostname is localhost.  */
-  if (!hostname[0])
-    strcpy (hostname, "localhost");
+  int r = getaddrinfo (host_str.c_str (), port_str.c_str (), &hint, &ainfo);
 
-  hostent = gethostbyname (hostname);
-  if (!hostent)
+  if (r != 0)
     {
-      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
+      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
+			  name, gai_strerror (r));
       errno = ENOENT;
       return -1;
     }
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
-	  sizeof (struct in_addr));
+  scoped_free_addrinfo free_ainfo (ainfo);
 
- retry:
+  struct addrinfo *cur_ainfo;
 
-  if (use_udp)
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
-  else
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
+  for (cur_ainfo = ainfo; cur_ainfo != NULL; cur_ainfo = cur_ainfo->ai_next)
+    {
+retry:
+      scb->fd = gdb_socket_cloexec (cur_ainfo->ai_family,
+				    cur_ainfo->ai_socktype,
+				    cur_ainfo->ai_protocol);
 
-  if (scb->fd == -1)
-    return -1;
-  
-  /* Set socket nonblocking.  */
-  ioarg = 1;
-  ioctl (scb->fd, FIONBIO, &ioarg);
+      if (scb->fd < 0)
+	continue;
 
-  /* Use Non-blocking connect.  connect() will return 0 if connected
-     already.  */
-  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
+      /* Set socket nonblocking.  */
+      ioarg = 1;
+      ioctl (scb->fd, FIONBIO, &ioarg);
 
-  if (n < 0)
-    {
+      /* Use Non-blocking connect.  connect() will return 0 if connected
+	 already.  */
+      n = connect (scb->fd, cur_ainfo->ai_addr, cur_ainfo->ai_addrlen);
+
+      if (n < 0)
+	{
 #ifdef USE_WIN32API
-      int err = WSAGetLastError();
+	  int err = WSAGetLastError();
 #else
-      int err = errno;
+	  int err = errno;
 #endif
 
-      /* Maybe we're waiting for the remote target to become ready to
-	 accept connections.  */
-      if (tcp_auto_retry
+	  /* Maybe we're waiting for the remote target to become ready to
+	     accept connections.  */
+	  if (tcp_auto_retry
 #ifdef USE_WIN32API
-	  && err == WSAECONNREFUSED
+	      && err == WSAECONNREFUSED
 #else
-	  && err == ECONNREFUSED
+	      && err == ECONNREFUSED
 #endif
-	  && wait_for_connect (NULL, &polls) >= 0)
-	{
-	  close (scb->fd);
-	  goto retry;
-	}
+	      && wait_for_connect (NULL, &polls) >= 0)
+	    {
+	      close (scb->fd);
+	      goto retry;
+	    }
 
-      if (
+	  if (
 #ifdef USE_WIN32API
-	  /* Under Windows, calling "connect" with a non-blocking socket
-	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
-	  err != WSAEWOULDBLOCK
+	      /* Under Windows, calling "connect" with a non-blocking socket
+		 results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
+	      err != WSAEWOULDBLOCK
 #else
-	  err != EINPROGRESS
+	      err != EINPROGRESS
 #endif
-	  )
-	{
-	  errno = err;
-	  net_close (scb);
-	  return -1;
+	      )
+	    {
+	      errno = err;
+	      continue;
+	    }
+
+	  /* Looks like we need to wait for the connect.  */
+	  do 
+	    {
+	      n = wait_for_connect (scb, &polls);
+	    } 
+	  while (n == 0);
+	  if (n < 0)
+	    continue;
 	}
 
-      /* Looks like we need to wait for the connect.  */
-      do 
-	{
-	  n = wait_for_connect (scb, &polls);
-	} 
-      while (n == 0);
-      if (n < 0)
-	{
-	  net_close (scb);
-	  return -1;
-	}
-    }
-
-  /* Got something.  Is it an error?  */
-  {
-    int res, err;
-    socklen_t len;
-
-    len = sizeof (err);
-    /* On Windows, the fourth parameter to getsockopt is a "char *";
-       on UNIX systems it is generally "void *".  The cast to "char *"
-       is OK everywhere, since in C++ any data pointer type can be
-       implicitly converted to "void *".  */
-    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
-    if (res < 0 || err)
+      /* Got something.  Is it an error?  */
       {
-	/* Maybe the target still isn't ready to accept the connection.  */
-	if (tcp_auto_retry
+	int res, err;
+	socklen_t len = sizeof (err);
+
+	/* On Windows, the fourth parameter to getsockopt is a "char *";
+	   on UNIX systems it is generally "void *".  The cast to "char *"
+	   is OK everywhere, since in C++ any data pointer type can be
+	   implicitly converted to "void *".  */
+	res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
+	if (res < 0 || err)
+	  {
+	    /* Maybe the target still isn't ready to accept the connection.  */
+	    if (tcp_auto_retry
 #ifdef USE_WIN32API
-	    && err == WSAECONNREFUSED
+		&& err == WSAECONNREFUSED
 #else
-	    && err == ECONNREFUSED
+		&& err == ECONNREFUSED
 #endif
-	    && wait_for_connect (NULL, &polls) >= 0)
-	  {
-	    close (scb->fd);
-	    goto retry;
+		&& wait_for_connect (NULL, &polls) >= 0)
+	      {
+		close (scb->fd);
+		goto retry;
+	      }
+	    if (err)
+	      errno = err;
+	    continue;
 	  }
-	if (err)
-	  errno = err;
-	net_close (scb);
-	return -1;
       }
-  } 
+      break;
+    }
+
+  if (cur_ainfo == NULL)
+    {
+      net_close (scb);
+      return -1;
+    }
 
   /* Turn off nonblocking.  */
   ioarg = 0;
   ioctl (scb->fd, FIONBIO, &ioarg);
 
-  if (use_udp == 0)
+  if (cur_ainfo->ai_socktype == IPPROTO_TCP)
     {
       /* Disable Nagle algorithm.  Needed in some cases.  */
-      tmp = 1;
+      int tmp = 1;
+
       setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
-		  (char *)&tmp, sizeof (tmp));
+		  (char *) &tmp, sizeof (tmp));
     }
 
 #ifdef SIGPIPE
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 4475ac21a9..37f676d252 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -259,6 +259,13 @@ This make (not runtest) variable is used to specify whether the
 testsuite preloads the read1.so library into expect.  Any non-empty
 value means true.  See "Race detection" below.
 
+GDB_TEST_IPV6
+
+This variable makes the tests related to GDBserver to run with IPv6
+local addresses, instead of IPv4.  This is useful to test the IPv6
+support, and only makes sense for the native-gdbserver and the
+native-extended-gdbserver boards.
+
 Race detection
 **************
 
diff --git a/gdb/testsuite/boards/gdbserver-base.exp b/gdb/testsuite/boards/gdbserver-base.exp
index 52ad698b3f..f738c90e8e 100644
--- a/gdb/testsuite/boards/gdbserver-base.exp
+++ b/gdb/testsuite/boards/gdbserver-base.exp
@@ -32,3 +32,8 @@ set_board_info gdb,nofileio 1
 set_board_info gdb,predefined_tsv "\\\$trace_timestamp"
 
 set GDBFLAGS "${GDBFLAGS} -ex \"set auto-connect-native-target off\""
+
+# Helper function that returns a local IPv6 address to connect to.
+proc get_comm_port_localhost_ipv6 { port } {
+    return "\\\[::1\\\]:${port}"
+}
diff --git a/gdb/testsuite/boards/native-extended-gdbserver.exp b/gdb/testsuite/boards/native-extended-gdbserver.exp
index df949994fd..9ec053c9d6 100644
--- a/gdb/testsuite/boards/native-extended-gdbserver.exp
+++ b/gdb/testsuite/boards/native-extended-gdbserver.exp
@@ -24,7 +24,12 @@ load_generic_config "extended-gdbserver"
 load_board_description "gdbserver-base"
 load_board_description "local-board"
 
-set_board_info sockethost "localhost:"
+if { [info exists GDB_TEST_IPV6] } {
+    set_board_info sockethost "tcp6:\[::1\]:"
+    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
+} else {
+    set_board_info sockethost "localhost:"
+}
 
 # We will be using the extended GDB remote protocol.
 set_board_info gdb_protocol "extended-remote"
diff --git a/gdb/testsuite/boards/native-gdbserver.exp b/gdb/testsuite/boards/native-gdbserver.exp
index ef9316007e..d491aa451a 100644
--- a/gdb/testsuite/boards/native-gdbserver.exp
+++ b/gdb/testsuite/boards/native-gdbserver.exp
@@ -30,7 +30,12 @@ set_board_info gdb,do_reload_on_run 1
 # There's no support for argument-passing (yet).
 set_board_info noargs 1
 
-set_board_info sockethost "localhost:"
+if { [info exists GDB_TEST_IPV6] } {
+    set_board_info sockethost "tcp6:\[::1\]:"
+    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
+} else {
+    set_board_info sockethost "localhost:"
+}
 set_board_info use_gdb_stub 1
 set_board_info exit_is_reliable 1
 
diff --git a/gdb/testsuite/gdb.server/run-without-local-binary.exp b/gdb/testsuite/gdb.server/run-without-local-binary.exp
index 1665ca9912..6ba3e711d9 100644
--- a/gdb/testsuite/gdb.server/run-without-local-binary.exp
+++ b/gdb/testsuite/gdb.server/run-without-local-binary.exp
@@ -53,7 +53,7 @@ save_vars { GDBFLAGS } {
     set use_gdb_stub 0
 
     gdb_test "target ${gdbserver_protocol} ${gdbserver_gdbport}" \
-	"Remote debugging using $gdbserver_gdbport" \
+	"Remote debugging using [string_to_regexp $gdbserver_gdbport]" \
 	"connect to gdbserver"
 
     gdb_test "run" \
-- 
2.14.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
@ 2018-05-23 23:40 ` Eli Zaretskii
  2018-05-24  0:41   ` Sergio Durigan Junior
  2018-05-31 20:10 ` Sergio Durigan Junior
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-05-23 23:40 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: gdb-patches, palves, jan.kratochvil, fercerpav, sekiriki

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Jan Kratochvil <jan.kratochvil@redhat.com>,
> 	Paul Fertser <fercerpav@gmail.com>,
> 	Tsutomu Seki <sekiriki@gmail.com>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Wed, 23 May 2018 14:57:19 -0400
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
> 
> 	* Makefile.in (COMMON_SFILES): Add 'common/netstuff.c'.
> 	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
> 	* NEWS (Changes since GDB 8.1): Mention IPv6 support.
> 	* common/netstuff.c: New file.
> 	* common/netstuff.h: New file.
> 	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
> 	(net_open): Handle IPv6-style hostnames; implement support for
> 	IPv6 connections.
> 
> gdb/gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
> 
> 	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
> 	(OBS): Add 'common/netstuff.o'.
> 	* gdbreplay.c: Include 'wspiapi.h'.
> 	(remote_open): Implement support for IPv6
> 	connections.
> 	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
> 	and 'wspiapi.h'.
> 	(handle_accept_event): Accept connections from IPv6 sources.
> 	(remote_prepare): Handle IPv6-style hostnames; implement
> 	support for IPv6 connections.
> 	(remote_open): Implement support for printing connections from
> 	IPv6 sources.
> 
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
> 
> 	* README (Testsuite Parameters): Mention new 'GDB_TEST_IPV6'
> 	parameter.
> 	* boards/gdbserver-base.exp (get_comm_port_localhost_ipv6):
> 	New procedure.
> 	* boards/native-extended-gdbserver.exp: Detect 'GDB_TEST_IPV6'
> 	and change board info accordingly.
> 	* boards/native-gdbserver.exp: Likewise.
> 	* gdb.server/run-without-local-binary.exp: Improve regexp used
> 	for detecting when a remote debugging connection succeeds.
> 
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
> 
> 	* gdb.texinfo (Remote Connection Commands): Add explanation
> 	about new IPv6 support.  Add new connection prefixes.

The documentation parts are approved, with a couple of minor comments:

>  @item target remote @code{@var{host}:@var{port}}
> +@itemx target remote @code{@var{@r{[}host@r{]}}:@var{port}}

Isn't having 2 lines here redundant?  If the HOST part is optional,
the second line already covers the first, right?

Similarly with other forms of this command.

>  @item target remote @code{udp:@var{host}:@var{port}}
> +@itemx target remote @code{udp:@var{host}:@var{port}}

And here these two lines are exactly identical, right?

> +To connect to port 2828 on a terminal server whose address is
> +@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
> +
> +@smallexample
> +target remote [2001::f8ff::67cf]:2828
> +@end smallexample
> +
> +Or explicitly specify the @acronym{IPv6} protocol:

The last sentence is actually a continuation of the one before the
example.  So it should start with a lower-case letter, and please
insert a @noindent (on its own line) before that, to prevent Texnfo
processors from indenting that line.

Thanks.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-23 23:40 ` Eli Zaretskii
@ 2018-05-24  0:41   ` Sergio Durigan Junior
  2018-05-24 16:54     ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-05-24  0:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, palves, jan.kratochvil, fercerpav, sekiriki

On Wednesday, May 23 2018, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: Pedro Alves <palves@redhat.com>,
>> 	Eli Zaretskii <eliz@gnu.org>,
>> 	Jan Kratochvil <jan.kratochvil@redhat.com>,
>> 	Paul Fertser <fercerpav@gmail.com>,
>> 	Tsutomu Seki <sekiriki@gmail.com>,
>> 	Sergio Durigan Junior <sergiodj@redhat.com>
>> Date: Wed, 23 May 2018 14:57:19 -0400
>> 
>> gdb/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 	    Paul Fertser  <fercerpav@gmail.com>
>> 	    Tsutomu Seki  <sekiriki@gmail.com>
>> 
>> 	* Makefile.in (COMMON_SFILES): Add 'common/netstuff.c'.
>> 	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
>> 	* NEWS (Changes since GDB 8.1): Mention IPv6 support.
>> 	* common/netstuff.c: New file.
>> 	* common/netstuff.h: New file.
>> 	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
>> 	(net_open): Handle IPv6-style hostnames; implement support for
>> 	IPv6 connections.
>> 
>> gdb/gdbserver/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 	    Paul Fertser  <fercerpav@gmail.com>
>> 	    Tsutomu Seki  <sekiriki@gmail.com>
>> 
>> 	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
>> 	(OBS): Add 'common/netstuff.o'.
>> 	* gdbreplay.c: Include 'wspiapi.h'.
>> 	(remote_open): Implement support for IPv6
>> 	connections.
>> 	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
>> 	and 'wspiapi.h'.
>> 	(handle_accept_event): Accept connections from IPv6 sources.
>> 	(remote_prepare): Handle IPv6-style hostnames; implement
>> 	support for IPv6 connections.
>> 	(remote_open): Implement support for printing connections from
>> 	IPv6 sources.
>> 
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 	    Paul Fertser  <fercerpav@gmail.com>
>> 	    Tsutomu Seki  <sekiriki@gmail.com>
>> 
>> 	* README (Testsuite Parameters): Mention new 'GDB_TEST_IPV6'
>> 	parameter.
>> 	* boards/gdbserver-base.exp (get_comm_port_localhost_ipv6):
>> 	New procedure.
>> 	* boards/native-extended-gdbserver.exp: Detect 'GDB_TEST_IPV6'
>> 	and change board info accordingly.
>> 	* boards/native-gdbserver.exp: Likewise.
>> 	* gdb.server/run-without-local-binary.exp: Improve regexp used
>> 	for detecting when a remote debugging connection succeeds.
>> 
>> gdb/doc/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 	    Paul Fertser  <fercerpav@gmail.com>
>> 	    Tsutomu Seki  <sekiriki@gmail.com>
>> 
>> 	* gdb.texinfo (Remote Connection Commands): Add explanation
>> 	about new IPv6 support.  Add new connection prefixes.
>
> The documentation parts are approved, with a couple of minor comments:

Thanks for the review, Eli.

>>  @item target remote @code{@var{host}:@var{port}}
>> +@itemx target remote @code{@var{@r{[}host@r{]}}:@var{port}}
>
> Isn't having 2 lines here redundant?  If the HOST part is optional,
> the second line already covers the first, right?
>
> Similarly with other forms of this command.

Hm, I think you have misunderstood what the square brackets mean, which
tells me that there must be a better way to write this...

The square brackets in this case don't mean that the HOST is optional.
Rather, they *enclose* the hostname.  As explained in the text above
this, IPv6 introduced a new way to specify URLs: by enclosing them in
square brackets.  This is because the IPv6 separator (':') is the same
as the resource (port) separator, which can cause confusion.  Therefore,
an IPv6 URL can have the form:

  [::1]:1234

That's what I tried to convey with the additional @itemx.  Perhaps I
shouldn't use @r{[} and @r{]}?  Not sure if there's a better way to do
that.

>>  @item target remote @code{udp:@var{host}:@var{port}}
>> +@itemx target remote @code{udp:@var{host}:@var{port}}
>
> And here these two lines are exactly identical, right?

Indeed.  Removed.

>> +To connect to port 2828 on a terminal server whose address is
>> +@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
>> +
>> +@smallexample
>> +target remote [2001::f8ff::67cf]:2828
>> +@end smallexample
>> +
>> +Or explicitly specify the @acronym{IPv6} protocol:
>
> The last sentence is actually a continuation of the one before the
> example.  So it should start with a lower-case letter, and please
> insert a @noindent (on its own line) before that, to prevent Texnfo
> processors from indenting that line.

Ah, OK.  Fixed.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-24  0:41   ` Sergio Durigan Junior
@ 2018-05-24 16:54     ` Eli Zaretskii
  2018-05-25  1:57       ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-05-24 16:54 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: gdb-patches, palves, jan.kratochvil, fercerpav, sekiriki

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: gdb-patches@sourceware.org,  palves@redhat.com,  jan.kratochvil@redhat.com,  fercerpav@gmail.com,  sekiriki@gmail.com
> Date: Wed, 23 May 2018 19:40:06 -0400
> 
> The square brackets in this case don't mean that the HOST is optional.
> Rather, they *enclose* the hostname.  As explained in the text above
> this, IPv6 introduced a new way to specify URLs: by enclosing them in
> square brackets.  This is because the IPv6 separator (':') is the same
> as the resource (port) separator, which can cause confusion.  Therefore,
> an IPv6 URL can have the form:
> 
>   [::1]:1234

Then perhaps we shouldn't advertise the bracket-less syntax at all,
and only say somewhere that it is accepted for backward compatibility?

> Perhaps I shouldn't use @r{[} and @r{]}?

Yes, @r{..} is definitely wrong in that case, you should drop it.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-24 16:54     ` Eli Zaretskii
@ 2018-05-25  1:57       ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-05-25  1:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, palves, jan.kratochvil, fercerpav, sekiriki

On Thursday, May 24 2018, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: gdb-patches@sourceware.org,  palves@redhat.com,  jan.kratochvil@redhat.com,  fercerpav@gmail.com,  sekiriki@gmail.com
>> Date: Wed, 23 May 2018 19:40:06 -0400
>> 
>> The square brackets in this case don't mean that the HOST is optional.
>> Rather, they *enclose* the hostname.  As explained in the text above
>> this, IPv6 introduced a new way to specify URLs: by enclosing them in
>> square brackets.  This is because the IPv6 separator (':') is the same
>> as the resource (port) separator, which can cause confusion.  Therefore,
>> an IPv6 URL can have the form:
>> 
>>   [::1]:1234
>
> Then perhaps we shouldn't advertise the bracket-less syntax at all,
> and only say somewhere that it is accepted for backward compatibility?

Well, the bracket-less syntax is still useful for when you want to
provide IPv4 addresses.  For example:

  target remote tcp:192.168.1.1:1234

is still a valid use, and:

  target remote tcp:[192.168.1.1]:1234

doesn't work/make sense.  Therefore, I think it's still important to
mention both syntaxes.

>> Perhaps I shouldn't use @r{[} and @r{]}?
>
> Yes, @r{..} is definitely wrong in that case, you should drop it.

Noted.  I'll remove the @r{}.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
  2018-05-23 23:40 ` Eli Zaretskii
@ 2018-05-31 20:10 ` Sergio Durigan Junior
  2018-06-06 12:26 ` Pedro Alves
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-05-31 20:10 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Wednesday, May 23 2018, I wrote:

> This patch implements IPv6 support for both GDB and gdbserver.  Based
> on my research, it is the fourth attempt to do that since 2006.  Since
> I used ideas from all of the previous patches, I also added their
> authors's names on the ChangeLogs as a way to recognize their
> efforts.  For reference sake, you can find the previous attempts at:
>
>   https://sourceware.org/ml/gdb-patches/2006-09/msg00192.html
>
>   https://sourceware.org/ml/gdb-patches/2014-02/msg00248.html
>
>   https://sourceware.org/ml/gdb-patches/2016-02/msg00226.html

Ping.

> The basic idea behind the patch is to start using the new
> 'getaddrinfo'/'getnameinfo' calls, which are responsible for
> translating names and addresses in a protocol-independent way.  This
> means that if we ever have an IPv8, we won't need to change the code
> again.
>
> The function 'getaddrinfo' returns a linked list of possible addresses
> to connect to, so I modified ser-tcp.c:net_open's code to loop through
> the linked list and try all the addresses until it finds a valid one.
> The same rationale was used for gdbserver, but without the "retry"
> mechanism that GDB has.
>
> I also implemented some hostname parsing functions which are used to
> help GDB and gdbserver to parse hostname strings provided by the user.
> These new functions are living inside common/netstuff.[ch].  I've had
> to do that since IPv6 introduces a new URL scheme, which defines that
> square brackets can be used to enclose the host part and differentiate
> it from the port (e.g., "[::1]:1234" means "host ::1, port 1234").  I
> spent some time thinking about a reasonable way to interpret what the
> user wants, and I came up with the following:
>
> - If the user has provided a prefix that doesn't specify the protocol
>   version (i.e., "tcp:" or "udp:"), or if the user has not provided
>   any prefix, don't make any assumptions (i.e., assume AF_UNSPEC when
>   dealing with 'getaddrinfo') *unless* the host starts with "[" (in
>   which case, assume it's an IPv6 host).
>
> - If the user has provided a prefix that does specify the protocol
>   version (i.e., "tcp4:", "tcp6:", "udp4:" or "udp6:"), then respect
>   that.
>
> This method doesn't follow strictly what RFC 2732 proposes (that
> literal IPv6 addresses should be provided enclosed in "[" and "]")
> because IPv6 addresses still can be provided without square brackets
> in our case, but since we have prefixes to specify protocol versions I
> think this is not an issue.
>
> Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
> parameter, which instructs GDB and gdbserver to use IPv6 for
> connections.  This way, if you want to run IPv6 tests, you do:
>
>   $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'
>
> This required a few changes on the gdbserver-base.exp,
> native-gdbserver.exp and native-extended-gdbserver.exp boards, and
> also a minimal adjustment on gdb.server/run-without-local-binary.exp.
>
> This patch has been regression-tested on BuildBot and locally, and
> also built using a x86_64-w64-mingw32 GCC, and no problems were found.
>
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
>
> 	* Makefile.in (COMMON_SFILES): Add 'common/netstuff.c'.
> 	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
> 	* NEWS (Changes since GDB 8.1): Mention IPv6 support.
> 	* common/netstuff.c: New file.
> 	* common/netstuff.h: New file.
> 	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
> 	(net_open): Handle IPv6-style hostnames; implement support for
> 	IPv6 connections.
>
> gdb/gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
>
> 	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
> 	(OBS): Add 'common/netstuff.o'.
> 	* gdbreplay.c: Include 'wspiapi.h'.
> 	(remote_open): Implement support for IPv6
> 	connections.
> 	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
> 	and 'wspiapi.h'.
> 	(handle_accept_event): Accept connections from IPv6 sources.
> 	(remote_prepare): Handle IPv6-style hostnames; implement
> 	support for IPv6 connections.
> 	(remote_open): Implement support for printing connections from
> 	IPv6 sources.
>
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
>
> 	* README (Testsuite Parameters): Mention new 'GDB_TEST_IPV6'
> 	parameter.
> 	* boards/gdbserver-base.exp (get_comm_port_localhost_ipv6):
> 	New procedure.
> 	* boards/native-extended-gdbserver.exp: Detect 'GDB_TEST_IPV6'
> 	and change board info accordingly.
> 	* boards/native-gdbserver.exp: Likewise.
> 	* gdb.server/run-without-local-binary.exp: Improve regexp used
> 	for detecting when a remote debugging connection succeeds.
>
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Paul Fertser  <fercerpav@gmail.com>
> 	    Tsutomu Seki  <sekiriki@gmail.com>
>
> 	* gdb.texinfo (Remote Connection Commands): Add explanation
> 	about new IPv6 support.  Add new connection prefixes.
> ---
>  gdb/Makefile.in                                    |   2 +
>  gdb/NEWS                                           |   4 +
>  gdb/common/netstuff.c                              | 136 +++++++++++++
>  gdb/common/netstuff.h                              |  52 +++++
>  gdb/doc/gdb.texinfo                                |  48 ++++-
>  gdb/gdbserver/Makefile.in                          |   2 +
>  gdb/gdbserver/gdbreplay.c                          | 181 +++++++++++++----
>  gdb/gdbserver/remote-utils.c                       | 119 +++++++----
>  gdb/ser-tcp.c                                      | 217 ++++++++++-----------
>  gdb/testsuite/README                               |   7 +
>  gdb/testsuite/boards/gdbserver-base.exp            |   5 +
>  gdb/testsuite/boards/native-extended-gdbserver.exp |   7 +-
>  gdb/testsuite/boards/native-gdbserver.exp          |   7 +-
>  .../gdb.server/run-without-local-binary.exp        |   2 +-
>  14 files changed, 602 insertions(+), 187 deletions(-)
>  create mode 100644 gdb/common/netstuff.c
>  create mode 100644 gdb/common/netstuff.h
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index df6ebab851..06ce12a4ee 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -962,6 +962,7 @@ COMMON_SFILES = \
>  	common/job-control.c \
>  	common/gdb_tilde_expand.c \
>  	common/gdb_vecs.c \
> +	common/netstuff.c \
>  	common/new-op.c \
>  	common/pathstuff.c \
>  	common/print-utils.c \
> @@ -1443,6 +1444,7 @@ HFILES_NO_SRCDIR = \
>  	common/gdb_vecs.h \
>  	common/gdb_wait.h \
>  	common/common-inferior.h \
> +	common/netstuff.h \
>  	common/host-defs.h \
>  	common/pathstuff.h \
>  	common/print-utils.h \
> diff --git a/gdb/NEWS b/gdb/NEWS
> index cef558039e..1f95ced912 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,10 @@
>  
>  *** Changes since GDB 8.1
>  
> +* GDB and GDBserver now support IPv6 connections.  IPv6 hostnames
> +  can be passed using the '[ADDRESS]:PORT' notation, or the regular
> +  'ADDRESS:PORT' method.
> +
>  * The commands 'info variables/functions/types' now show the source line
>    numbers of symbol definitions when available.
>  
> diff --git a/gdb/common/netstuff.c b/gdb/common/netstuff.c
> new file mode 100644
> index 0000000000..cdf4b611db
> --- /dev/null
> +++ b/gdb/common/netstuff.c
> @@ -0,0 +1,136 @@
> +/* Operations on network stuff.
> +   Copyright (C) 2018 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"
> +
> +#ifdef USE_WIN32API
> +#include <winsock2.h>
> +#include <wspiapi.h>
> +#else
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
> +#include <netdb.h>
> +#include <sys/socket.h>
> +#include <netinet/tcp.h>
> +#endif
> +
> +/* See common/netstuff.h.  */
> +
> +scoped_free_addrinfo::scoped_free_addrinfo (struct addrinfo *a)
> +{
> +  m_res = a;
> +}
> +/* See common/netstuff.h.  */
> +
> +scoped_free_addrinfo::~scoped_free_addrinfo ()
> +{
> +  freeaddrinfo (m_res);
> +}
> +
> +/* See common/netstuff.h.  */
> +
> +void
> +parse_hostname_without_prefix (const char *hostname, std::string &host_str,
> +			       std::string &port_str, struct addrinfo *hint)
> +{
> +  std::string strname (hostname);
> +
> +  if (hint->ai_family != AF_INET && strname[0] == '[')
> +    {
> +      /* IPv6 addresses can be written as '[ADDR]:PORT', and we
> +	 support this notation.  */
> +      size_t close_bracket_pos = strname.find_first_of (']');
> +
> +      if (close_bracket_pos == std::string::npos)
> +	error (_("Missing close bracket in hostname '%s'"),
> +	       strname.c_str ());
> +
> +      /* Erase both '[' and ']'.  */
> +      strname.erase (0, 1);
> +      strname.erase (close_bracket_pos - 1, 1);
> +
> +      hint->ai_family = AF_INET6;
> +    }
> +
> +  /* The length of the hostname part.  */
> +  size_t host_len;
> +
> +  size_t last_colon_pos = strname.find_last_of (':');
> +
> +  if (last_colon_pos != std::string::npos)
> +    {
> +      /* The user has provided a port.  */
> +      host_len = last_colon_pos;
> +      port_str = strname.substr (last_colon_pos + 1);
> +    }
> +  else
> +    host_len = strname.size ();
> +
> +  host_str = strname.substr (0, host_len);
> +
> +  /* Default hostname is localhost.  */
> +  if (host_str.empty ())
> +    host_str = "localhost";
> +}
> +
> +/* See common/netstuff.h.  */
> +
> +void
> +parse_hostname (const char *hostname, std::string &host_str,
> +		std::string &port_str, 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 },
> +      { NULL, 0, 0 },
> +    };
> +
> +  for (const struct host_prefix *prefix = prefixes;
> +       prefix->prefix != NULL;
> +       ++prefix)
> +    if (startswith (hostname, prefix->prefix))
> +      {
> +	hostname += 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;
> +      }
> +
> +  parse_hostname_without_prefix (hostname, host_str, port_str, hint);
> +}
> diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
> new file mode 100644
> index 0000000000..1ac2433f11
> --- /dev/null
> +++ b/gdb/common/netstuff.h
> @@ -0,0 +1,52 @@
> +/* Operations on network stuff.
> +   Copyright (C) 2018 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/>.  */
> +
> +#ifndef NETSTUFF_H
> +#define NETSTUFF_H
> +
> +#include <string>
> +
> +/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
> +
> +class scoped_free_addrinfo
> +{
> +public:
> +  scoped_free_addrinfo (struct addrinfo *a);
> +
> +  ~scoped_free_addrinfo ();
> +
> +  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
> +
> +private:
> +  struct addrinfo *m_res;
> +};
> +
> +/* Parse HOSTNAME (which is a string in the of "ADDR:PORT") and fill
> +   in HOST_STR, PORT_STR and HINT accordingly.  */
> +extern void parse_hostname_without_prefix (const char *hostname,
> +					   std::string &host_str,
> +					   std::string &port_str,
> +					   struct addrinfo *hint);
> +
> +/* Parse HOSTNAME (which is a string in the form of
> +   "[tcp[6]:|udp[6]:]ADDR:PORT") and fill in HOST_STR, PORT_STR and
> +   HINT accordingly.  */
> +extern void parse_hostname (const char *hostname, std::string &host_str,
> +			    std::string &port_str, struct addrinfo *hint);
> +
> +#endif /* ! NETSTUFF_H */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 28f083f96e..7994204140 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -20496,16 +20496,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
>  @code{target} command.
>  
>  @item target remote @code{@var{host}:@var{port}}
> +@itemx target remote @code{@var{@r{[}host@r{]}}:@var{port}}
>  @itemx target remote @code{tcp:@var{host}:@var{port}}
> +@itemx target remote @code{tcp:@var{@r{[}host@r{]}}:@var{port}}
> +@itemx target remote @code{tcp4:@var{host}:@var{port}}
> +@itemx target remote @code{tcp6:@var{host}:@var{port}}
> +@itemx target remote @code{tcp6:@var{@r{[}host@r{]}}:@var{port}}
>  @itemx target extended-remote @code{@var{host}:@var{port}}
> +@itemx target extended-remote @code{@var{@r{[}host@r{]}}:@var{port}}
>  @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp:@var{@r{[}host@r{]}}:@var{port}}
> +@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp6:@var{@r{[}host@r{]}}:@var{port}}
>  @cindex @acronym{TCP} port, @code{target remote}
>  Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
> -The @var{host} may be either a host name or a numeric @acronym{IP}
> -address; @var{port} must be a decimal number.  The @var{host} could be
> -the target machine itself, if it is directly connected to the net, or
> -it might be a terminal server which in turn has a serial line to the
> -target.
> +The @var{host} may be either a host name, a numeric @acronym{IPv4}
> +address, or a numeric @acronym{IPv6} address (with or without the
> +square brackets to separate the address from the port); @var{port}
> +must be a decimal number.  The @var{host} could be the target machine
> +itself, if it is directly connected to the net, or it might be a
> +terminal server which in turn has a serial line to the target.
>  
>  For example, to connect to port 2828 on a terminal server named
>  @code{manyfarms}:
> @@ -20514,6 +20525,24 @@ For example, to connect to port 2828 on a terminal server named
>  target remote manyfarms:2828
>  @end smallexample
>  
> +To connect to port 2828 on a terminal server whose address is
> +@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
> +
> +@smallexample
> +target remote [2001::f8ff::67cf]:2828
> +@end smallexample
> +
> +Or explicitly specify the @acronym{IPv6} protocol:
> +
> +@smallexample
> +target remote tcp6:2001::f8ff::67cf:2828
> +@end smallexample
> +
> +This last example may be confusing to the reader, because there is no
> +visible separation between the hostname and the port number.
> +Therefore, we recommend the user to provide @acronym{IPv6} addresses
> +using square brackets for clarity.
> +
>  If your remote target is actually running on the same machine as your
>  debugger session (e.g.@: a simulator for your target running on the
>  same host), you can omit the hostname.  For example, to connect to
> @@ -20527,7 +20556,16 @@ target remote :1234
>  Note that the colon is still required here.
>  
>  @item target remote @code{udp:@var{host}:@var{port}}
> +@itemx target remote @code{udp:@var{host}:@var{port}}
> +@itemx target remote @code{udp:@var{@r{[}host@r{]}}:@var{port}}
> +@itemx target remote @code{udp4:@var{host}:@var{port}}
> +@itemx target remote @code{udp6:@var{@r{[}host@r{]}}:@var{port}}
> +@itemx target extended-remote @code{udp:@var{host}:@var{port}}
>  @itemx target extended-remote @code{udp:@var{host}:@var{port}}
> +@itemx target extended-remote @code{udp:@var{@r{[}host@r{]}}:@var{port}}
> +@itemx target extended-remote @code{udp4:@var{host}:@var{port}}
> +@itemx target extended-remote @code{udp6:@var{host}:@var{port}}
> +@itemx target extended-remote @code{udp6:@var{@r{[}host@r{]}}:@var{port}}
>  @cindex @acronym{UDP} port, @code{target remote}
>  Debug using @acronym{UDP} packets to @var{port} on @var{host}.  For example, to
>  connect to @acronym{UDP} port 2828 on a terminal server named @code{manyfarms}:
> diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
> index 675faa4364..d65346a357 100644
> --- a/gdb/gdbserver/Makefile.in
> +++ b/gdb/gdbserver/Makefile.in
> @@ -211,6 +211,7 @@ SFILES = \
>  	$(srcdir)/common/job-control.c \
>  	$(srcdir)/common/gdb_tilde_expand.c \
>  	$(srcdir)/common/gdb_vecs.c \
> +	$(srcdir)/common/netstuff.c \
>  	$(srcdir)/common/new-op.c \
>  	$(srcdir)/common/pathstuff.c \
>  	$(srcdir)/common/print-utils.c \
> @@ -253,6 +254,7 @@ OBS = \
>  	common/format.o \
>  	common/gdb_tilde_expand.o \
>  	common/gdb_vecs.o \
> +	common/netstuff.o \
>  	common/new-op.o \
>  	common/pathstuff.o \
>  	common/print-utils.o \
> diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
> index c1a639069a..01b70d49f4 100644
> --- a/gdb/gdbserver/gdbreplay.c
> +++ b/gdb/gdbserver/gdbreplay.c
> @@ -53,6 +53,7 @@
>  
>  #if USE_WIN32API
>  #include <winsock2.h>
> +#include <wspiapi.h>
>  #endif
>  
>  #ifndef HAVE_SOCKLEN_T
> @@ -175,56 +176,159 @@ remote_close (void)
>  static void
>  remote_open (char *name)
>  {
> -  if (!strchr (name, ':'))
> +  char *last_colon = strrchr (name, ':');
> +
> +  if (last_colon == NULL)
>      {
>        fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
>        fflush (stderr);
>        exit (1);
>      }
> -  else
> -    {
> +
>  #ifdef USE_WIN32API
> -      static int winsock_initialized;
> +  static int winsock_initialized;
>  #endif
> -      char *port_str;
> -      int port;
> -      struct sockaddr_in sockaddr;
> -      socklen_t tmp;
> -      int tmp_desc;
> +  char *port_str;
> +  int tmp;
> +  int tmp_desc;
> +  struct addrinfo hint;
> +  struct addrinfo *ainfo;
> +  char *orig_name = strdup (name);
> +
> +  struct prefix
> +  {
> +    /* The prefix to be parsed.  */
> +    const char *str;
> +
> +    /* The address family.  */
> +    int ai_family;
> +
> +    /* The socktype.  */
> +    int ai_socktype;
> +  };
> +  static const struct 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 },
> +	{ NULL, 0, 0 } };
> +
> +  memset (&hint, 0, sizeof (hint));
> +  /* Assume no prefix will be passed, therefore we should use
> +     AF_UNSPEC.  */
> +  hint.ai_family = AF_UNSPEC;
> +  hint.ai_socktype = SOCK_STREAM;
> +  hint.ai_protocol = IPPROTO_TCP;
> +
> +  for (const struct prefix *p = prefixes; p->str != NULL; ++p)
> +    if (strncmp (name, p->str, strlen (p->str)) == 0)
> +      {
> +	name += strlen (p->str);
> +	hint.ai_family = p->ai_family;
> +	hint.ai_socktype = p->ai_socktype;
> +	hint.ai_protocol
> +	  = p->ai_socktype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
> +	break;
> +      }
>  
> -      port_str = strchr (name, ':');
> +  if (hint.ai_family != AF_INET && *name == '[')
> +    {
> +      ++name;
> +      port_str = strchr (name, ']');
> +      if (port_str == NULL)
> +	{
> +	  fprintf (stderr, "Missing closing bracket on hostname: %s\n",
> +		   orig_name);
> +	  exit (1);
> +	}
> +      /* Skip closing bracket.  */
> +      *port_str = '\0';
> +      ++port_str;
> +      if (*port_str != ':')
> +	{
> +	  fprintf (stderr, "Missing port number on hostname: %s\n",
> +		   orig_name);
> +	  exit (1);
> +	}
> +      hint.ai_family = AF_INET6;
> +    }
> +  else
> +    port_str = last_colon;
>  
> -      port = atoi (port_str + 1);
> +  /* Skip the colon.  */
> +  *port_str = '\0';
> +  ++port_str;
>  
>  #ifdef USE_WIN32API
> -      if (!winsock_initialized)
> -	{
> -	  WSADATA wsad;
> +  if (!winsock_initialized)
> +    {
> +      WSADATA wsad;
>  
> -	  WSAStartup (MAKEWORD (1, 0), &wsad);
> -	  winsock_initialized = 1;
> -	}
> +      WSAStartup (MAKEWORD (1, 0), &wsad);
> +      winsock_initialized = 1;
> +    }
>  #endif
>  
> -      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
> -      if (tmp_desc == -1)
> -	perror_with_name ("Can't open socket");
> +  int r = getaddrinfo (name, port_str, &hint, &ainfo);
>  
> -      /* Allow rapid reuse of this port. */
> -      tmp = 1;
> -      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> -		  sizeof (tmp));
> +  if (r != 0)
> +    {
> +      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
> +	       name, port_str, gai_strerror (r));
> +      fflush (stderr);
> +      exit (1);
> +    }
>  
> -      sockaddr.sin_family = PF_INET;
> -      sockaddr.sin_port = htons (port);
> -      sockaddr.sin_addr.s_addr = INADDR_ANY;
> +  struct addrinfo *p;
>  
> -      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
> -	  || listen (tmp_desc, 1))
> -	perror_with_name ("Can't bind address");
> +  for (p = ainfo; p != NULL; p = p->ai_next)
> +    {
> +      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
> +
> +      if (tmp_desc >= 0)
> +	break;
> +    }
> +
> +  if (p == NULL)
> +    perror_with_name ("Cannot open socket");
> +
> +  /* Allow rapid reuse of this port. */
> +  tmp = 1;
> +  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> +	      sizeof (tmp));
> +
> +  switch (p->ai_family)
> +    {
> +    case AF_INET:
> +      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> +      break;
> +    case AF_INET6:
> +      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
> +      break;
> +    default:
> +      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
> +      exit (1);
> +    }
> +
> +  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
> +    perror_with_name ("Can't bind address");
> +
> +  if (p->ai_socktype == SOCK_DGRAM)
> +    remote_desc = tmp_desc;
> +  else
> +    {
> +      struct sockaddr_storage sockaddr;
> +      socklen_t sockaddrsize = sizeof (sockaddr);
> +      char orig_host[64], orig_port[16];
> +
> +      if (listen (tmp_desc, 1) != 0)
> +	perror_with_name ("Can't listen on socket");
> +
> +      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
> +			    &sockaddrsize);
>  
> -      tmp = sizeof (sockaddr);
> -      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
>        if (remote_desc == -1)
>  	perror_with_name ("Accept failed");
>  
> @@ -239,6 +343,16 @@ remote_open (char *name)
>        setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
>  		  (char *) &tmp, sizeof (tmp));
>  
> +      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
> +		       orig_host, sizeof (orig_host),
> +		       orig_port, sizeof (orig_port),
> +		       NI_NUMERICHOST | NI_NUMERICSERV) == 0)
> +	{
> +	  fprintf (stderr, "Remote debugging from host %s, port %s\n",
> +		   orig_host, orig_port);
> +	  fflush (stderr);
> +	}
> +
>  #ifndef USE_WIN32API
>        close (tmp_desc);		/* No longer need this */
>  
> @@ -254,8 +368,9 @@ remote_open (char *name)
>    fcntl (remote_desc, F_SETFL, FASYNC);
>  #endif
>  
> -  fprintf (stderr, "Replay logfile using %s\n", name);
> +  fprintf (stderr, "Replay logfile using %s\n", orig_name);
>    fflush (stderr);
> +  free (orig_name);
>  }
>  
>  static int
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 3b5a459ae4..c8b5dcdbba 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -26,6 +26,8 @@
>  #include "dll.h"
>  #include "rsp-low.h"
>  #include "gdbthread.h"
> +#include "netstuff.h"
> +#include "filestuff.h"
>  #include <ctype.h>
>  #if HAVE_SYS_IOCTL_H
>  #include <sys/ioctl.h>
> @@ -63,6 +65,7 @@
>  
>  #if USE_WIN32API
>  #include <winsock2.h>
> +#include <wspiapi.h>
>  #endif
>  
>  #if __QNX__
> @@ -156,19 +159,18 @@ enable_async_notification (int fd)
>  static int
>  handle_accept_event (int err, gdb_client_data client_data)
>  {
> -  struct sockaddr_in sockaddr;
> -  socklen_t tmp;
> +  struct sockaddr_storage sockaddr;
> +  socklen_t len = sizeof (sockaddr);
>  
>    if (debug_threads)
>      debug_printf ("handling possible accept event\n");
>  
> -  tmp = sizeof (sockaddr);
> -  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
> +  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
>    if (remote_desc == -1)
>      perror_with_name ("Accept failed");
>  
>    /* Enable TCP keep alive process. */
> -  tmp = 1;
> +  socklen_t tmp = 1;
>    setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
>  	      (char *) &tmp, sizeof (tmp));
>  
> @@ -197,8 +199,19 @@ handle_accept_event (int err, gdb_client_data client_data)
>    delete_file_handler (listen_desc);
>  
>    /* Convert IP address to string.  */
> -  fprintf (stderr, "Remote debugging from host %s\n",
> -	   inet_ntoa (sockaddr.sin_addr));
> +  char orig_host[64], orig_port[16];
> +
> +  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +		       orig_host, sizeof (orig_host),
> +		       orig_port, sizeof (orig_port),
> +		       NI_NUMERICHOST | NI_NUMERICSERV);
> +
> +  if (r != 0)
> +    fprintf (stderr, _("Could not obtain remote address: %s\n"),
> +	     gai_strerror (r));
> +  else
> +    fprintf (stderr, "Remote debugging from host %s, port %s\n", orig_host,
> +	     orig_port);
>  
>    enable_async_notification (remote_desc);
>  
> @@ -222,14 +235,10 @@ handle_accept_event (int err, gdb_client_data client_data)
>  void
>  remote_prepare (const char *name)
>  {
> -  const char *port_str;
>  #ifdef USE_WIN32API
>    static int winsock_initialized;
>  #endif
> -  int port;
> -  struct sockaddr_in sockaddr;
>    socklen_t tmp;
> -  char *port_end;
>  
>    remote_is_stdio = 0;
>    if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
> @@ -242,17 +251,25 @@ remote_prepare (const char *name)
>        return;
>      }
>  
> -  port_str = strchr (name, ':');
> -  if (port_str == NULL)
> +  struct addrinfo hint;
> +  struct addrinfo *ainfo;
> +  std::string host_str, port_str;
> +
> +  memset (&hint, 0, sizeof (hint));
> +  /* Assume no prefix will be passed, therefore we should use
> +     AF_UNSPEC.  */
> +  hint.ai_family = AF_UNSPEC;
> +  hint.ai_socktype = SOCK_STREAM;
> +  hint.ai_protocol = IPPROTO_TCP;
> +
> +  parse_hostname_without_prefix (name, host_str, port_str, &hint);
> +
> +  if (port_str.empty ())
>      {
>        transport_is_reliable = 0;
>        return;
>      }
>  
> -  port = strtoul (port_str + 1, &port_end, 10);
> -  if (port_str[1] == '\0' || *port_end != '\0')
> -    error ("Bad port argument: %s", name);
> -
>  #ifdef USE_WIN32API
>    if (!winsock_initialized)
>      {
> @@ -263,8 +280,25 @@ remote_prepare (const char *name)
>      }
>  #endif
>  
> -  listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
> -  if (listen_desc == -1)
> +  int r = getaddrinfo (host_str.c_str (), port_str.c_str (), &hint, &ainfo);
> +
> +  if (r != 0)
> +    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
> +
> +  scoped_free_addrinfo freeaddrinfo (ainfo);
> +
> +  struct addrinfo *iter;
> +
> +  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
> +    {
> +      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
> +					iter->ai_protocol);
> +
> +      if (listen_desc >= 0)
> +	break;
> +    }
> +
> +  if (iter == NULL)
>      perror_with_name ("Can't open socket");
>  
>    /* Allow rapid reuse of this port. */
> @@ -272,14 +306,25 @@ remote_prepare (const char *name)
>    setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
>  	      sizeof (tmp));
>  
> -  sockaddr.sin_family = PF_INET;
> -  sockaddr.sin_port = htons (port);
> -  sockaddr.sin_addr.s_addr = INADDR_ANY;
> +  switch (iter->ai_family)
> +    {
> +    case AF_INET:
> +      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> +      break;
> +    case AF_INET6:
> +      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
> +      break;
> +    default:
> +      internal_error (__FILE__, __LINE__,
> +		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
> +    }
>  
> -  if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
> -      || listen (listen_desc, 1))
> +  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
>      perror_with_name ("Can't bind address");
>  
> +  if (listen (listen_desc, 1) != 0)
> +    perror_with_name ("Can't listen on socket");
> +
>    transport_is_reliable = 1;
>  }
>  
> @@ -354,18 +399,24 @@ remote_open (const char *name)
>  #endif /* USE_WIN32API */
>    else
>      {
> -      int port;
> -      socklen_t len;
> -      struct sockaddr_in sockaddr;
> -
> -      len = sizeof (sockaddr);
> -      if (getsockname (listen_desc,
> -		       (struct sockaddr *) &sockaddr, &len) < 0
> -	  || len < sizeof (sockaddr))
> +      char listen_port[16];
> +      struct sockaddr_storage sockaddr;
> +      socklen_t len = sizeof (sockaddr);
> +
> +      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
>  	perror_with_name ("Can't determine port");
> -      port = ntohs (sockaddr.sin_port);
>  
> -      fprintf (stderr, "Listening on port %d\n", port);
> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +			   NULL, 0,
> +			   listen_port, sizeof (listen_port),
> +			   NI_NUMERICSERV);
> +
> +      if (r != 0)
> +	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
> +		 gai_strerror (r));
> +      else
> +	fprintf (stderr, "Listening on port %s\n", listen_port);
> +
>        fflush (stderr);
>  
>        /* Register the event loop handler.  */
> diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
> index 23ef3b04b8..3d9fbd866f 100644
> --- a/gdb/ser-tcp.c
> +++ b/gdb/ser-tcp.c
> @@ -25,6 +25,7 @@
>  #include "cli/cli-decode.h"
>  #include "cli/cli-setshow.h"
>  #include "filestuff.h"
> +#include "netstuff.h"
>  
>  #include <sys/types.h>
>  
> @@ -39,6 +40,7 @@
>  
>  #ifdef USE_WIN32API
>  #include <winsock2.h>
> +#include <wspiapi.h>
>  #ifndef ETIMEDOUT
>  #define ETIMEDOUT WSAETIMEDOUT
>  #endif
> @@ -158,166 +160,157 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
>  int
>  net_open (struct serial *scb, const char *name)
>  {
> -  char hostname[100];
> -  const char *port_str;
> -  int n, port, tmp;
> -  int use_udp;
> -  struct hostent *hostent;
> -  struct sockaddr_in sockaddr;
> +  int n;
> +  bool use_udp;
>  #ifdef USE_WIN32API
>    u_long ioarg;
>  #else
>    int ioarg;
>  #endif
>    unsigned int polls = 0;
> +  struct addrinfo hint;
> +  struct addrinfo *ainfo;
> +  std::string host_str, port_str;
>  
> -  use_udp = 0;
> -  if (startswith (name, "udp:"))
> -    {
> -      use_udp = 1;
> -      name = name + 4;
> -    }
> -  else if (startswith (name, "tcp:"))
> -    name = name + 4;
> -
> -  port_str = strchr (name, ':');
> +  memset (&hint, 0, sizeof (hint));
> +  /* Assume no prefix will be passed, therefore we should use
> +     AF_UNSPEC.  */
> +  hint.ai_family = AF_INET;
> +  hint.ai_socktype = SOCK_STREAM;
> +  hint.ai_protocol = IPPROTO_TCP;
>  
> -  if (!port_str)
> -    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
> -						       happen.  */
> +  parse_hostname (name, host_str, port_str, &hint);
>  
> -  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
> -  strncpy (hostname, name, tmp);	/* Don't want colon.  */
> -  hostname[tmp] = '\000';	/* Tie off host name.  */
> -  port = atoi (port_str + 1);
> +  if (port_str.empty ())
> +    error (_("Missing port on hostname '%s'"), name);
>  
> -  /* Default hostname is localhost.  */
> -  if (!hostname[0])
> -    strcpy (hostname, "localhost");
> +  int r = getaddrinfo (host_str.c_str (), port_str.c_str (), &hint, &ainfo);
>  
> -  hostent = gethostbyname (hostname);
> -  if (!hostent)
> +  if (r != 0)
>      {
> -      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
> +      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
> +			  name, gai_strerror (r));
>        errno = ENOENT;
>        return -1;
>      }
>  
> -  sockaddr.sin_family = PF_INET;
> -  sockaddr.sin_port = htons (port);
> -  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
> -	  sizeof (struct in_addr));
> +  scoped_free_addrinfo free_ainfo (ainfo);
>  
> - retry:
> +  struct addrinfo *cur_ainfo;
>  
> -  if (use_udp)
> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
> -  else
> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
> +  for (cur_ainfo = ainfo; cur_ainfo != NULL; cur_ainfo = cur_ainfo->ai_next)
> +    {
> +retry:
> +      scb->fd = gdb_socket_cloexec (cur_ainfo->ai_family,
> +				    cur_ainfo->ai_socktype,
> +				    cur_ainfo->ai_protocol);
>  
> -  if (scb->fd == -1)
> -    return -1;
> -  
> -  /* Set socket nonblocking.  */
> -  ioarg = 1;
> -  ioctl (scb->fd, FIONBIO, &ioarg);
> +      if (scb->fd < 0)
> +	continue;
>  
> -  /* Use Non-blocking connect.  connect() will return 0 if connected
> -     already.  */
> -  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
> +      /* Set socket nonblocking.  */
> +      ioarg = 1;
> +      ioctl (scb->fd, FIONBIO, &ioarg);
>  
> -  if (n < 0)
> -    {
> +      /* Use Non-blocking connect.  connect() will return 0 if connected
> +	 already.  */
> +      n = connect (scb->fd, cur_ainfo->ai_addr, cur_ainfo->ai_addrlen);
> +
> +      if (n < 0)
> +	{
>  #ifdef USE_WIN32API
> -      int err = WSAGetLastError();
> +	  int err = WSAGetLastError();
>  #else
> -      int err = errno;
> +	  int err = errno;
>  #endif
>  
> -      /* Maybe we're waiting for the remote target to become ready to
> -	 accept connections.  */
> -      if (tcp_auto_retry
> +	  /* Maybe we're waiting for the remote target to become ready to
> +	     accept connections.  */
> +	  if (tcp_auto_retry
>  #ifdef USE_WIN32API
> -	  && err == WSAECONNREFUSED
> +	      && err == WSAECONNREFUSED
>  #else
> -	  && err == ECONNREFUSED
> +	      && err == ECONNREFUSED
>  #endif
> -	  && wait_for_connect (NULL, &polls) >= 0)
> -	{
> -	  close (scb->fd);
> -	  goto retry;
> -	}
> +	      && wait_for_connect (NULL, &polls) >= 0)
> +	    {
> +	      close (scb->fd);
> +	      goto retry;
> +	    }
>  
> -      if (
> +	  if (
>  #ifdef USE_WIN32API
> -	  /* Under Windows, calling "connect" with a non-blocking socket
> -	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
> -	  err != WSAEWOULDBLOCK
> +	      /* Under Windows, calling "connect" with a non-blocking socket
> +		 results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
> +	      err != WSAEWOULDBLOCK
>  #else
> -	  err != EINPROGRESS
> +	      err != EINPROGRESS
>  #endif
> -	  )
> -	{
> -	  errno = err;
> -	  net_close (scb);
> -	  return -1;
> +	      )
> +	    {
> +	      errno = err;
> +	      continue;
> +	    }
> +
> +	  /* Looks like we need to wait for the connect.  */
> +	  do 
> +	    {
> +	      n = wait_for_connect (scb, &polls);
> +	    } 
> +	  while (n == 0);
> +	  if (n < 0)
> +	    continue;
>  	}
>  
> -      /* Looks like we need to wait for the connect.  */
> -      do 
> -	{
> -	  n = wait_for_connect (scb, &polls);
> -	} 
> -      while (n == 0);
> -      if (n < 0)
> -	{
> -	  net_close (scb);
> -	  return -1;
> -	}
> -    }
> -
> -  /* Got something.  Is it an error?  */
> -  {
> -    int res, err;
> -    socklen_t len;
> -
> -    len = sizeof (err);
> -    /* On Windows, the fourth parameter to getsockopt is a "char *";
> -       on UNIX systems it is generally "void *".  The cast to "char *"
> -       is OK everywhere, since in C++ any data pointer type can be
> -       implicitly converted to "void *".  */
> -    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
> -    if (res < 0 || err)
> +      /* Got something.  Is it an error?  */
>        {
> -	/* Maybe the target still isn't ready to accept the connection.  */
> -	if (tcp_auto_retry
> +	int res, err;
> +	socklen_t len = sizeof (err);
> +
> +	/* On Windows, the fourth parameter to getsockopt is a "char *";
> +	   on UNIX systems it is generally "void *".  The cast to "char *"
> +	   is OK everywhere, since in C++ any data pointer type can be
> +	   implicitly converted to "void *".  */
> +	res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
> +	if (res < 0 || err)
> +	  {
> +	    /* Maybe the target still isn't ready to accept the connection.  */
> +	    if (tcp_auto_retry
>  #ifdef USE_WIN32API
> -	    && err == WSAECONNREFUSED
> +		&& err == WSAECONNREFUSED
>  #else
> -	    && err == ECONNREFUSED
> +		&& err == ECONNREFUSED
>  #endif
> -	    && wait_for_connect (NULL, &polls) >= 0)
> -	  {
> -	    close (scb->fd);
> -	    goto retry;
> +		&& wait_for_connect (NULL, &polls) >= 0)
> +	      {
> +		close (scb->fd);
> +		goto retry;
> +	      }
> +	    if (err)
> +	      errno = err;
> +	    continue;
>  	  }
> -	if (err)
> -	  errno = err;
> -	net_close (scb);
> -	return -1;
>        }
> -  } 
> +      break;
> +    }
> +
> +  if (cur_ainfo == NULL)
> +    {
> +      net_close (scb);
> +      return -1;
> +    }
>  
>    /* Turn off nonblocking.  */
>    ioarg = 0;
>    ioctl (scb->fd, FIONBIO, &ioarg);
>  
> -  if (use_udp == 0)
> +  if (cur_ainfo->ai_socktype == IPPROTO_TCP)
>      {
>        /* Disable Nagle algorithm.  Needed in some cases.  */
> -      tmp = 1;
> +      int tmp = 1;
> +
>        setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
> -		  (char *)&tmp, sizeof (tmp));
> +		  (char *) &tmp, sizeof (tmp));
>      }
>  
>  #ifdef SIGPIPE
> diff --git a/gdb/testsuite/README b/gdb/testsuite/README
> index 4475ac21a9..37f676d252 100644
> --- a/gdb/testsuite/README
> +++ b/gdb/testsuite/README
> @@ -259,6 +259,13 @@ This make (not runtest) variable is used to specify whether the
>  testsuite preloads the read1.so library into expect.  Any non-empty
>  value means true.  See "Race detection" below.
>  
> +GDB_TEST_IPV6
> +
> +This variable makes the tests related to GDBserver to run with IPv6
> +local addresses, instead of IPv4.  This is useful to test the IPv6
> +support, and only makes sense for the native-gdbserver and the
> +native-extended-gdbserver boards.
> +
>  Race detection
>  **************
>  
> diff --git a/gdb/testsuite/boards/gdbserver-base.exp b/gdb/testsuite/boards/gdbserver-base.exp
> index 52ad698b3f..f738c90e8e 100644
> --- a/gdb/testsuite/boards/gdbserver-base.exp
> +++ b/gdb/testsuite/boards/gdbserver-base.exp
> @@ -32,3 +32,8 @@ set_board_info gdb,nofileio 1
>  set_board_info gdb,predefined_tsv "\\\$trace_timestamp"
>  
>  set GDBFLAGS "${GDBFLAGS} -ex \"set auto-connect-native-target off\""
> +
> +# Helper function that returns a local IPv6 address to connect to.
> +proc get_comm_port_localhost_ipv6 { port } {
> +    return "\\\[::1\\\]:${port}"
> +}
> diff --git a/gdb/testsuite/boards/native-extended-gdbserver.exp b/gdb/testsuite/boards/native-extended-gdbserver.exp
> index df949994fd..9ec053c9d6 100644
> --- a/gdb/testsuite/boards/native-extended-gdbserver.exp
> +++ b/gdb/testsuite/boards/native-extended-gdbserver.exp
> @@ -24,7 +24,12 @@ load_generic_config "extended-gdbserver"
>  load_board_description "gdbserver-base"
>  load_board_description "local-board"
>  
> -set_board_info sockethost "localhost:"
> +if { [info exists GDB_TEST_IPV6] } {
> +    set_board_info sockethost "tcp6:\[::1\]:"
> +    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
> +} else {
> +    set_board_info sockethost "localhost:"
> +}
>  
>  # We will be using the extended GDB remote protocol.
>  set_board_info gdb_protocol "extended-remote"
> diff --git a/gdb/testsuite/boards/native-gdbserver.exp b/gdb/testsuite/boards/native-gdbserver.exp
> index ef9316007e..d491aa451a 100644
> --- a/gdb/testsuite/boards/native-gdbserver.exp
> +++ b/gdb/testsuite/boards/native-gdbserver.exp
> @@ -30,7 +30,12 @@ set_board_info gdb,do_reload_on_run 1
>  # There's no support for argument-passing (yet).
>  set_board_info noargs 1
>  
> -set_board_info sockethost "localhost:"
> +if { [info exists GDB_TEST_IPV6] } {
> +    set_board_info sockethost "tcp6:\[::1\]:"
> +    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
> +} else {
> +    set_board_info sockethost "localhost:"
> +}
>  set_board_info use_gdb_stub 1
>  set_board_info exit_is_reliable 1
>  
> diff --git a/gdb/testsuite/gdb.server/run-without-local-binary.exp b/gdb/testsuite/gdb.server/run-without-local-binary.exp
> index 1665ca9912..6ba3e711d9 100644
> --- a/gdb/testsuite/gdb.server/run-without-local-binary.exp
> +++ b/gdb/testsuite/gdb.server/run-without-local-binary.exp
> @@ -53,7 +53,7 @@ save_vars { GDBFLAGS } {
>      set use_gdb_stub 0
>  
>      gdb_test "target ${gdbserver_protocol} ${gdbserver_gdbport}" \
> -	"Remote debugging using $gdbserver_gdbport" \
> +	"Remote debugging using [string_to_regexp $gdbserver_gdbport]" \
>  	"connect to gdbserver"
>  
>      gdb_test "run" \
> -- 
> 2.14.3

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
  2018-05-23 23:40 ` Eli Zaretskii
  2018-05-31 20:10 ` Sergio Durigan Junior
@ 2018-06-06 12:26 ` Pedro Alves
  2018-06-08  1:13   ` Sergio Durigan Junior
  2018-06-15  0:25 ` [PATCH v2] " Sergio Durigan Junior
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-06 12:26 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

Hi Sergio,

I noticed this when applying the patch:

 $ git am /tmp/sergio.mbox
 Applying: Implement IPv6 support for GDB/gdbserver
 .git/rebase-apply/patch:982: trailing whitespace.
           do 
 .git/rebase-apply/patch:985: trailing whitespace.
             } 
 warning: 2 lines add whitespace errors.

You can check it locally with:

 $ git show --check

 gdb/ser-tcp.c:256: trailing whitespace.
 +         do 
 gdb/ser-tcp.c:259: trailing whitespace.
 +           } 

Comments on the patch below.

> 
> Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
> parameter, which instructs GDB and gdbserver to use IPv6 for
> connections.  This way, if you want to run IPv6 tests, you do:
> 
>   $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'

That sounds useful, but:

#1 - I don't see how that works without also passing
     --target_board= pointing at one of the native-gdbserver and
     native-extended-gdbserver board files.  
     Can you expand on why you took this approach instead of:
 
  a) handling GDB_TEST_IPV6 somewhere central, like
     in gdb/testsuite/gdbserver-support.exp, where we
     default to "localhost:".  That would exercise the gdb.server/
     tests with ipv6, when testing with the default/unix board file.

  b) add new board files to test with ipv6, like native-gdbserver-v6
     or something like that.

  c) both?

#2 - I think it'd be also useful to have some gdb.server/ test that
     runs with the default board and exercises / smoke tests ipv6.
     (And if we have that, we might as well iterate the test on udp/udpv6
     too.)

#3 - Actually, this makes me wonder about changing the variable's
     spelling from GDB_TEST_IPV6=1 to something like
     GDB_TEST_SOCKETHOST and then one would be able to set it to:

      "localhost:",
      "localhost6:"
      "tcp:localhost6:"
      "\[::1\]:"
      "udp:127.0.0.1:"

     or whatever one would like.

#4 - Why do we need to override get_comm_port too, here? :

     -set_board_info sockethost "localhost:"
     +if { [info exists GDB_TEST_IPV6] } {
     +    set_board_info sockethost "tcp6:\[::1\]:"
     +    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
     +} else {
     +    set_board_info sockethost "localhost:"
     +}

    Doesn't overriding "sockethost" alone work?  Why not?



>  gdb/Makefile.in                                    |   2 +
>  gdb/NEWS                                           |   4 +
>  gdb/common/netstuff.c                              | 136 +++++++++++++
>  gdb/common/netstuff.h                              |  52 +++++
>  gdb/doc/gdb.texinfo                                |  48 ++++-
>  gdb/gdbserver/Makefile.in                          |   2 +
>  gdb/gdbserver/gdbreplay.c                          | 181 +++++++++++++----
>  gdb/gdbserver/remote-utils.c                       | 119 +++++++----
>  gdb/ser-tcp.c                                      | 217 ++++++++++-----------
>  gdb/testsuite/README                               |   7 +
>  gdb/testsuite/boards/gdbserver-base.exp            |   5 +
>  gdb/testsuite/boards/native-extended-gdbserver.exp |   7 +-
>  gdb/testsuite/boards/native-gdbserver.exp          |   7 +-
>  .../gdb.server/run-without-local-binary.exp        |   2 +-
>  14 files changed, 602 insertions(+), 187 deletions(-)
>  create mode 100644 gdb/common/netstuff.c
>  create mode 100644 gdb/common/netstuff.h
> 
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index df6ebab851..06ce12a4ee 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -962,6 +962,7 @@ COMMON_SFILES = \
>  	common/job-control.c \
>  	common/gdb_tilde_expand.c \
>  	common/gdb_vecs.c \
> +	common/netstuff.c \
>  	common/new-op.c \
>  	common/pathstuff.c \
>  	common/print-utils.c \
> @@ -1443,6 +1444,7 @@ HFILES_NO_SRCDIR = \
>  	common/gdb_vecs.h \
>  	common/gdb_wait.h \
>  	common/common-inferior.h \
> +	common/netstuff.h \
>  	common/host-defs.h \
>  	common/pathstuff.h \
>  	common/print-utils.h \
> diff --git a/gdb/NEWS b/gdb/NEWS
> index cef558039e..1f95ced912 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,10 @@
>  
>  *** Changes since GDB 8.1
>  
> +* GDB and GDBserver now support IPv6 connections.  IPv6 hostnames
> +  can be passed using the '[ADDRESS]:PORT' notation, or the regular
> +  'ADDRESS:PORT' method.

Saying "IPv6 hostnames" and then talking about "ADDRESS:PORT" is
confusing, I think.  If we're talking about host names then saying
HOST:PORT would more accurate.  But I think that what you really
mean is to say "IPv6 _addresses_ can be passed".

Does connecting with "localhost6:port" default to IPv6, BTW?
At least fedora includes "localhost6" in /etc/hosts.


> +/* See common/netstuff.h.  */
> +
> +void
> +parse_hostname_without_prefix (const char *hostname, std::string &host_str,
> +			       std::string &port_str, struct addrinfo *hint)
> +{
> +  std::string strname (hostname);

I suspect the local parsing can be written using
gdb::string_view to avoid copying?

> +
> +/* See common/netstuff.h.  */
> +
> +void
> +parse_hostname (const char *hostname, std::string &host_str,
> +		std::string &port_str, 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 },
> +      { NULL, 0, 0 },
> +    };
> +
> +  for (const struct host_prefix *prefix = prefixes;
> +       prefix->prefix != NULL;
> +       ++prefix)

I think you could drop the last/null entry and use range-for ?

> +    if (startswith (hostname, prefix->prefix))
> +      {
> +	hostname += 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;
> +      }
> +
> +  parse_hostname_without_prefix (hostname, host_str, port_str, hint);
> +}


> +/* Parse HOSTNAME (which is a string in the of "ADDR:PORT") and fill

Missing "form" in "in the of".

> +   in HOST_STR, PORT_STR and HINT accordingly.  */
> +extern void parse_hostname_without_prefix (const char *hostname,
> +					   std::string &host_str,
> +					   std::string &port_str,
> +					   struct addrinfo *hint);
> +
> +/* Parse HOSTNAME (which is a string in the form of
> +   "[tcp[6]:|udp[6]:]ADDR:PORT") and fill in HOST_STR, PORT_STR and
> +   HINT accordingly.  */
> +extern void parse_hostname (const char *hostname, std::string &host_str,
> +			    std::string &port_str, struct addrinfo *hint);

Really not a big deal, but instead of output parameters, I'd
consider returning all outputs via return.  Something like:

struct parsed_hostname
{
  std::string host_str;
  std::string port_str;
  struct addrinfo addrinfo;
};
extern parsed_hostname parse_hostname (const char *hostname,
                                       const struct addrinfo &hint);



>  For example, to connect to port 2828 on a terminal server named
>  @code{manyfarms}:
> @@ -20514,6 +20525,24 @@ For example, to connect to port 2828 on a terminal server named
>  target remote manyfarms:2828
>  @end smallexample
>  
> +To connect to port 2828 on a terminal server whose address is
> +@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
> +
> +@smallexample
> +target remote [2001::f8ff::67cf]:2828
> +@end smallexample
> +
> +Or explicitly specify the @acronym{IPv6} protocol:
> +
> +@smallexample
> +target remote tcp6:2001::f8ff::67cf:2828
> +@end smallexample
> +
> +This last example may be confusing to the reader, because there is no
> +visible separation between the hostname and the port number.

Is that really true?  It seems there's visible separation to me -- the
address/hosthoname part uses double colon, while the port name is
separated by a single colon?


> +Therefore, we recommend the user to provide @acronym{IPv6} addresses
> +using square brackets for clarity.
> +

>  #ifndef HAVE_SOCKLEN_T
> @@ -175,56 +176,159 @@ remote_close (void)
>  static void
>  remote_open (char *name)
>  {
> -  if (!strchr (name, ':'))
> +  char *last_colon = strrchr (name, ':');
> +
> +  if (last_colon == NULL)
>      {
>        fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
>        fflush (stderr);
>        exit (1);
>      }
> -  else
> -    {
> +
>  #ifdef USE_WIN32API
> -      static int winsock_initialized;
> +  static int winsock_initialized;
>  #endif
> -      char *port_str;
> -      int port;
> -      struct sockaddr_in sockaddr;
> -      socklen_t tmp;
> -      int tmp_desc;
> +  char *port_str;
> +  int tmp;
> +  int tmp_desc;
> +  struct addrinfo hint;
> +  struct addrinfo *ainfo;
> +  char *orig_name = strdup (name);

Do we need a deep copy?  And if we do, how about
using std::string to avoid having to call free further
down?

> +
> +  struct prefix
> +  {
> +    /* The prefix to be parsed.  */
> +    const char *str;
> +
> +    /* The address family.  */
> +    int ai_family;
> +
> +    /* The socktype.  */
> +    int ai_socktype;
> +  };
> +  static const struct 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 },
> +	{ NULL, 0, 0 } };

That seems like unusual formatting.  In common/netstuff.c
you broke the starting and ending '{' }' differently.

I wonder though, shouldn't this be using the new
netstuff.c shared routines?  It looks like duplicated code?

> +
> +  memset (&hint, 0, sizeof (hint));
> +  /* Assume no prefix will be passed, therefore we should use
> +     AF_UNSPEC.  */
> +  hint.ai_family = AF_UNSPEC;
> +  hint.ai_socktype = SOCK_STREAM;
> +  hint.ai_protocol = IPPROTO_TCP;
> +
> +  for (const struct prefix *p = prefixes; p->str != NULL; ++p)

Same comment about range-for.

> +
> +  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
> +    perror_with_name ("Can't bind address");
> +
> +  if (p->ai_socktype == SOCK_DGRAM)
> +    remote_desc = tmp_desc;
> +  else
> +    {
> +      struct sockaddr_storage sockaddr;
> +      socklen_t sockaddrsize = sizeof (sockaddr);
> +      char orig_host[64], orig_port[16];

I guess these magic sizes are garanteed to be enough, since
you specify NI_NUMERICHOST | NI_NUMERICSERV.  Correct?
A comment or giving those constants names or comments
would be good.  Something like:

/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
#define GDB_NI_MAX_ADDR 64
#define GDB_NI_MAX_PORT 16

>  #if __QNX__
> @@ -156,19 +159,18 @@ enable_async_notification (int fd)
>  static int
>  handle_accept_event (int err, gdb_client_data client_data)
>  {
> -  struct sockaddr_in sockaddr;
> -  socklen_t tmp;
> +  struct sockaddr_storage sockaddr;
> +  socklen_t len = sizeof (sockaddr);
>  
>    if (debug_threads)
>      debug_printf ("handling possible accept event\n");
>  
> -  tmp = sizeof (sockaddr);
> -  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
> +  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
>    if (remote_desc == -1)
>      perror_with_name ("Accept failed");
>  
>    /* Enable TCP keep alive process. */
> -  tmp = 1;
> +  socklen_t tmp = 1;
>    setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
>  	      (char *) &tmp, sizeof (tmp));
>  
> @@ -197,8 +199,19 @@ handle_accept_event (int err, gdb_client_data client_data)
>    delete_file_handler (listen_desc);
>  
>    /* Convert IP address to string.  */
> -  fprintf (stderr, "Remote debugging from host %s\n",
> -	   inet_ntoa (sockaddr.sin_addr));
> +  char orig_host[64], orig_port[16];

Same comment as for gdbreplay.

> +
> +  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +		       orig_host, sizeof (orig_host),
> +		       orig_port, sizeof (orig_port),
> +		       NI_NUMERICHOST | NI_NUMERICSERV);
> +
> +  if (r != 0)
> +    fprintf (stderr, _("Could not obtain remote address: %s\n"),
> +	     gai_strerror (r));
> +  else
> +    fprintf (stderr, "Remote debugging from host %s, port %s\n", orig_host,
> +	     orig_port);

While at it, couple you please add the missing _() for i18n.
BTW, is that line too long?  Can't tell from email client.


>  
> @@ -354,18 +399,24 @@ remote_open (const char *name)
>  #endif /* USE_WIN32API */
>    else
>      {
> -      int port;
> -      socklen_t len;
> -      struct sockaddr_in sockaddr;
> -
> -      len = sizeof (sockaddr);
> -      if (getsockname (listen_desc,
> -		       (struct sockaddr *) &sockaddr, &len) < 0
> -	  || len < sizeof (sockaddr))
> +      char listen_port[16];
> +      struct sockaddr_storage sockaddr;
> +      socklen_t len = sizeof (sockaddr);
> +
> +      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
>  	perror_with_name ("Can't determine port");
> -      port = ntohs (sockaddr.sin_port);
>  
> -      fprintf (stderr, "Listening on port %d\n", port);
> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +			   NULL, 0,
> +			   listen_port, sizeof (listen_port),
> +			   NI_NUMERICSERV);
> +
> +      if (r != 0)
> +	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
> +		 gai_strerror (r));
> +      else
> +	fprintf (stderr, "Listening on port %s\n", listen_port);

Preexisting, but while at it, adding the _() wouldn't hurt.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-06 12:26 ` Pedro Alves
@ 2018-06-08  1:13   ` Sergio Durigan Junior
  2018-06-08 13:53     ` Pedro Alves
  0 siblings, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-08  1:13 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Wednesday, June 06 2018, Pedro Alves wrote:

> Hi Sergio,

Hi Pedro,

Thanks for the review.

> I noticed this when applying the patch:
>
>  $ git am /tmp/sergio.mbox
>  Applying: Implement IPv6 support for GDB/gdbserver
>  .git/rebase-apply/patch:982: trailing whitespace.
>            do 
>  .git/rebase-apply/patch:985: trailing whitespace.
>              } 
>  warning: 2 lines add whitespace errors.
>
> You can check it locally with:
>
>  $ git show --check
>
>  gdb/ser-tcp.c:256: trailing whitespace.
>  +         do 
>  gdb/ser-tcp.c:259: trailing whitespace.
>  +           } 
>
> Comments on the patch below.

Yeah, I just reindented this region, without touching anything else.
I've now removed these trailing whitespaces.

>> Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
>> parameter, which instructs GDB and gdbserver to use IPv6 for
>> connections.  This way, if you want to run IPv6 tests, you do:
>> 
>>   $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'
>
> That sounds useful, but:
>
> #1 - I don't see how that works without also passing
>      --target_board= pointing at one of the native-gdbserver and
>      native-extended-gdbserver board files.  
>      Can you expand on why you took this approach instead of:
>  
>   a) handling GDB_TEST_IPV6 somewhere central, like
>      in gdb/testsuite/gdbserver-support.exp, where we
>      default to "localhost:".  That would exercise the gdb.server/
>      tests with ipv6, when testing with the default/unix board file.
>
>   b) add new board files to test with ipv6, like native-gdbserver-v6
>      or something like that.
>
>   c) both?

I was thinking about a good way to test this feature, and my initial
assumption was that the test would only make sense when --target-board=
is passed.  That's why I chose to implement the mechanism on
gdb/testsuite/boards/gdbserver-base.exp.  Now that you mentioned this, I
noticed that I should have also mentioned these expectations while
writing the commit message, and that the "make check-gdb
RUNTESTFLAGS='GDB_TEST_IPV6=1'" is actually wrong because it doesn't
specify any of the target boards.

Having said that, and after reading your question, I understand that the
testing can be made more flexible by implementing the logic inside
gdb/testsuite/gdbserver-support.exp instead, which will have the benefit
of activating the test even without a gdbserver target board being
specified.  I will give it a try and see if I can implement it in a
better way.

> #2 - I think it'd be also useful to have some gdb.server/ test that
>      runs with the default board and exercises / smoke tests ipv6.
>      (And if we have that, we might as well iterate the test on udp/udpv6
>      too.)

I thought about that, but I didn't know how to proceed when you don't
really know whether IPv6 support is present or not in the machine.  I'll
give it another try.

> #3 - Actually, this makes me wonder about changing the variable's
>      spelling from GDB_TEST_IPV6=1 to something like
>      GDB_TEST_SOCKETHOST and then one would be able to set it to:
>
>       "localhost:",
>       "localhost6:"
>       "tcp:localhost6:"
>       "\[::1\]:"
>       "udp:127.0.0.1:"
>
>      or whatever one would like.

That works, and has the benefit of being protocol-agnostic.  I'll
implement it.

> #4 - Why do we need to override get_comm_port too, here? :
>
>      -set_board_info sockethost "localhost:"
>      +if { [info exists GDB_TEST_IPV6] } {
>      +    set_board_info sockethost "tcp6:\[::1\]:"
>      +    set_board_info gdbserver,get_comm_port get_comm_port_localhost_ipv6
>      +} else {
>      +    set_board_info sockethost "localhost:"
>      +}
>
>     Doesn't overriding "sockethost" alone work?  Why not?

Hm.  I can swear this wasn't working before (otherwise I wouldn't have
created more work just by unnecessarily implement this override), but
now that I commented it out, it's working again.  This is
frustrating...  Anyway, I'm going to rewrite a lot of this code, so this
part will disappear.

>>  gdb/Makefile.in                                    |   2 +
>>  gdb/NEWS                                           |   4 +
>>  gdb/common/netstuff.c                              | 136 +++++++++++++
>>  gdb/common/netstuff.h                              |  52 +++++
>>  gdb/doc/gdb.texinfo                                |  48 ++++-
>>  gdb/gdbserver/Makefile.in                          |   2 +
>>  gdb/gdbserver/gdbreplay.c                          | 181 +++++++++++++----
>>  gdb/gdbserver/remote-utils.c                       | 119 +++++++----
>>  gdb/ser-tcp.c                                      | 217 ++++++++++-----------
>>  gdb/testsuite/README                               |   7 +
>>  gdb/testsuite/boards/gdbserver-base.exp            |   5 +
>>  gdb/testsuite/boards/native-extended-gdbserver.exp |   7 +-
>>  gdb/testsuite/boards/native-gdbserver.exp          |   7 +-
>>  .../gdb.server/run-without-local-binary.exp        |   2 +-
>>  14 files changed, 602 insertions(+), 187 deletions(-)
>>  create mode 100644 gdb/common/netstuff.c
>>  create mode 100644 gdb/common/netstuff.h
>> 
>> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
>> index df6ebab851..06ce12a4ee 100644
>> --- a/gdb/Makefile.in
>> +++ b/gdb/Makefile.in
>> @@ -962,6 +962,7 @@ COMMON_SFILES = \
>>  	common/job-control.c \
>>  	common/gdb_tilde_expand.c \
>>  	common/gdb_vecs.c \
>> +	common/netstuff.c \
>>  	common/new-op.c \
>>  	common/pathstuff.c \
>>  	common/print-utils.c \
>> @@ -1443,6 +1444,7 @@ HFILES_NO_SRCDIR = \
>>  	common/gdb_vecs.h \
>>  	common/gdb_wait.h \
>>  	common/common-inferior.h \
>> +	common/netstuff.h \
>>  	common/host-defs.h \
>>  	common/pathstuff.h \
>>  	common/print-utils.h \
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index cef558039e..1f95ced912 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -3,6 +3,10 @@
>>  
>>  *** Changes since GDB 8.1
>>  
>> +* GDB and GDBserver now support IPv6 connections.  IPv6 hostnames
>> +  can be passed using the '[ADDRESS]:PORT' notation, or the regular
>> +  'ADDRESS:PORT' method.
>
> Saying "IPv6 hostnames" and then talking about "ADDRESS:PORT" is
> confusing, I think.  If we're talking about host names then saying
> HOST:PORT would more accurate.  But I think that what you really
> mean is to say "IPv6 _addresses_ can be passed".

That's more correct, indeed.  I've changed the text.

> Does connecting with "localhost6:port" default to IPv6, BTW?
> At least fedora includes "localhost6" in /etc/hosts.

Using "localhost6:port" works, but it doesn't default to IPv6.  Here's
what I see on the gdbserver side:

  $ ./gdb/gdbserver/gdbserver --once localhost6:1234 a.out
  Process /path/to/a.out created; pid = 7742
  Listening on port 1234
  Remote debugging from host ::ffff:127.0.0.1, port 39196

This means that the connection came using IPv4; it works because IPv6
sockets also listen for IPv4 connection on Linux (one can change this
behaviour by setting the "IPV6_V6ONLY" socket option).

This happens because I've made a decision to default to AF_INET (instead
of AF_UNSPEC) when no prefix has been given.  This basically means that,
at least for now, we assume that an unknown (i.e., not prefixed)
address/hostname is IPv4.  I've made this decision thinking about the
convenience of the user: when AF_UNSPEC is used (and the user hasn't
specified any prefix), getaddrinfo will return a linked list of possible
addresses that we should try to connect to, which usually means an IPv6
and an IPv4 address, in that order.  Usually this is fine, because (as I
said) IPv6 sockets can also listen for IPv4 connections.  However, if
you start gdbserver with an explicit IPv4 address:

  $ ./gdb/gdbserver/gdbserver --once 127.0.0.1:1234 a.out

and try to connect GDB to it using an "ambiguous" hostname:

  $ ./gdb/gdb -ex 'target remote localhost:1234' a.out

you will notice that GDB will take a somewhat long time trying to
connect (to the IPv6 address, because of AF_UNSPEC), and then it will
error out saying that the connection timed out:

  tcp:localhost:1234: Connection timed out.

This is because of the auto-retry mechanism implemented for TCP
connections on GDB; it keeps retrying to connect to the IPv6 until it
decides it's not going to work.  Only after this timeout is that GDB
will try to connect to the IPv4 address, and succeed.

So, the way I see it, we have a few options to deal with this scenario:

1) Assume that the unprefixed address/hostname is AF_INET (i.e., keep
the patch as-is).

2) Don't assume anything about the unprefixed address/hostname (i.e.,
AF_UNSPEC), and don't change the auto-retry system.  This is not very
nice because of what I explained above.

3) Don't assume anything about the unprefixed address/hostname (i.e.,
AF_UNSPEC), but *DO* change the auto-retry system to retry less times
(currently it's set to 15 retries, which seems too much to me).  Maybe 5
times is enough?  This will still have an impact on the user, but she
will have to wait less time, at least.

Either (1) or (3) are fine by me.  If we go with (1), we'll eventually
need to change the default to IPv6 (or to AF_UNSPEC), but that's only
when IPv6 is more adopted.

Side note: while I was writing this, I noticed a problem in the code and
fixed it.  Basically, on net_open, should have been declared inside the
for loop, and not outside.  This was making GDB not attempt connecting
the IPv4 address after the IPv6 failed.  Now it's fixed.

>> +/* See common/netstuff.h.  */
>> +
>> +void
>> +parse_hostname_without_prefix (const char *hostname, std::string &host_str,
>> +			       std::string &port_str, struct addrinfo *hint)
>> +{
>> +  std::string strname (hostname);
>
> I suspect the local parsing can be written using
> gdb::string_view to avoid copying?

Hm, I'll investigate it.  So many shiny new features available to us
now, it's hard to keep track!

>> +
>> +/* See common/netstuff.h.  */
>> +
>> +void
>> +parse_hostname (const char *hostname, std::string &host_str,
>> +		std::string &port_str, 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 },
>> +      { NULL, 0, 0 },
>> +    };
>> +
>> +  for (const struct host_prefix *prefix = prefixes;
>> +       prefix->prefix != NULL;
>> +       ++prefix)
>
> I think you could drop the last/null entry and use range-for ?

I remember trying to use a rage-for but having issues.  I think I was
using a different setup for this struct...  Anyway, I implemented the
range-for idea now.

>> +    if (startswith (hostname, prefix->prefix))
>> +      {
>> +	hostname += 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;
>> +      }
>> +
>> +  parse_hostname_without_prefix (hostname, host_str, port_str, hint);
>> +}
>
>
>> +/* Parse HOSTNAME (which is a string in the of "ADDR:PORT") and fill
>
> Missing "form" in "in the of".

Fixed.

>> +   in HOST_STR, PORT_STR and HINT accordingly.  */
>> +extern void parse_hostname_without_prefix (const char *hostname,
>> +					   std::string &host_str,
>> +					   std::string &port_str,
>> +					   struct addrinfo *hint);
>> +
>> +/* Parse HOSTNAME (which is a string in the form of
>> +   "[tcp[6]:|udp[6]:]ADDR:PORT") and fill in HOST_STR, PORT_STR and
>> +   HINT accordingly.  */
>> +extern void parse_hostname (const char *hostname, std::string &host_str,
>> +			    std::string &port_str, struct addrinfo *hint);
>
> Really not a big deal, but instead of output parameters, I'd
> consider returning all outputs via return.  Something like:
>
> struct parsed_hostname
> {
>   std::string host_str;
>   std::string port_str;
>   struct addrinfo addrinfo;
> };
> extern parsed_hostname parse_hostname (const char *hostname,
>                                        const struct addrinfo &hint);

Sure, I can do it.

>>  For example, to connect to port 2828 on a terminal server named
>>  @code{manyfarms}:
>> @@ -20514,6 +20525,24 @@ For example, to connect to port 2828 on a terminal server named
>>  target remote manyfarms:2828
>>  @end smallexample
>>  
>> +To connect to port 2828 on a terminal server whose address is
>> +@code{2001::f8ff::67cf}, you can either use the square bracket syntax:
>> +
>> +@smallexample
>> +target remote [2001::f8ff::67cf]:2828
>> +@end smallexample
>> +
>> +Or explicitly specify the @acronym{IPv6} protocol:
>> +
>> +@smallexample
>> +target remote tcp6:2001::f8ff::67cf:2828
>> +@end smallexample
>> +
>> +This last example may be confusing to the reader, because there is no
>> +visible separation between the hostname and the port number.
>
> Is that really true?  It seems there's visible separation to me -- the
> address/hosthoname part uses double colon, while the port name is
> separated by a single colon?

Ah, I think that's not a good address to use as an example.  I'll use a
"regular" address without double colons.

>> +Therefore, we recommend the user to provide @acronym{IPv6} addresses
>> +using square brackets for clarity.
>> +
>
>>  #ifndef HAVE_SOCKLEN_T
>> @@ -175,56 +176,159 @@ remote_close (void)
>>  static void
>>  remote_open (char *name)
>>  {
>> -  if (!strchr (name, ':'))
>> +  char *last_colon = strrchr (name, ':');
>> +
>> +  if (last_colon == NULL)
>>      {
>>        fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
>>        fflush (stderr);
>>        exit (1);
>>      }
>> -  else
>> -    {
>> +
>>  #ifdef USE_WIN32API
>> -      static int winsock_initialized;
>> +  static int winsock_initialized;
>>  #endif
>> -      char *port_str;
>> -      int port;
>> -      struct sockaddr_in sockaddr;
>> -      socklen_t tmp;
>> -      int tmp_desc;
>> +  char *port_str;
>> +  int tmp;
>> +  int tmp_desc;
>> +  struct addrinfo hint;
>> +  struct addrinfo *ainfo;
>> +  char *orig_name = strdup (name);
>
> Do we need a deep copy?  And if we do, how about
> using std::string to avoid having to call free further
> down?

This is gdbserver/gdbreplay.c, where apparently we don't have access to
a lot of our regular facilities on GDB.  For example, I was trying to
use std::string, its methods, and other stuff here (even i18n
functions), but the code won't compile, and as far as I have researched
this is intentional, because gdbreplay needs to be a very small and
simple program.  Or at least that's what I understood from our
archives/documentation.  I did not feel confident reworking gdbreplay to
make it "modern", so I decided to implement things "the old way".

>> +
>> +  struct prefix
>> +  {
>> +    /* The prefix to be parsed.  */
>> +    const char *str;
>> +
>> +    /* The address family.  */
>> +    int ai_family;
>> +
>> +    /* The socktype.  */
>> +    int ai_socktype;
>> +  };
>> +  static const struct 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 },
>> +	{ NULL, 0, 0 } };
>
> That seems like unusual formatting.  In common/netstuff.c
> you broke the starting and ending '{' }' differently.

I'll improve the formatting here.

> I wonder though, shouldn't this be using the new
> netstuff.c shared routines?  It looks like duplicated code?

The reason here is the same as the one I wrote above: doing '#include
"netstuff.h"' brings in a lot of other stuff which break the build of
gdbreplay.c.  That's why I reimplemented the code (and yeah, it is code
duplication, and I wasn't happy about it, but as I said it feels that
these limitations are intentional on gdbreplay.c).

>> +
>> +  memset (&hint, 0, sizeof (hint));
>> +  /* Assume no prefix will be passed, therefore we should use
>> +     AF_UNSPEC.  */
>> +  hint.ai_family = AF_UNSPEC;
>> +  hint.ai_socktype = SOCK_STREAM;
>> +  hint.ai_protocol = IPPROTO_TCP;
>> +
>> +  for (const struct prefix *p = prefixes; p->str != NULL; ++p)
>
> Same comment about range-for.

Fixed.

>> +
>> +  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
>> +    perror_with_name ("Can't bind address");
>> +
>> +  if (p->ai_socktype == SOCK_DGRAM)
>> +    remote_desc = tmp_desc;
>> +  else
>> +    {
>> +      struct sockaddr_storage sockaddr;
>> +      socklen_t sockaddrsize = sizeof (sockaddr);
>> +      char orig_host[64], orig_port[16];
>
> I guess these magic sizes are garanteed to be enough, since
> you specify NI_NUMERICHOST | NI_NUMERICSERV.  Correct?
> A comment or giving those constants names or comments
> would be good.  Something like:
>
> /* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
> #define GDB_NI_MAX_ADDR 64
> #define GDB_NI_MAX_PORT 16

Good idea, I'll do it.

>>  #if __QNX__
>> @@ -156,19 +159,18 @@ enable_async_notification (int fd)
>>  static int
>>  handle_accept_event (int err, gdb_client_data client_data)
>>  {
>> -  struct sockaddr_in sockaddr;
>> -  socklen_t tmp;
>> +  struct sockaddr_storage sockaddr;
>> +  socklen_t len = sizeof (sockaddr);
>>  
>>    if (debug_threads)
>>      debug_printf ("handling possible accept event\n");
>>  
>> -  tmp = sizeof (sockaddr);
>> -  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
>> +  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
>>    if (remote_desc == -1)
>>      perror_with_name ("Accept failed");
>>  
>>    /* Enable TCP keep alive process. */
>> -  tmp = 1;
>> +  socklen_t tmp = 1;
>>    setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
>>  	      (char *) &tmp, sizeof (tmp));
>>  
>> @@ -197,8 +199,19 @@ handle_accept_event (int err, gdb_client_data client_data)
>>    delete_file_handler (listen_desc);
>>  
>>    /* Convert IP address to string.  */
>> -  fprintf (stderr, "Remote debugging from host %s\n",
>> -	   inet_ntoa (sockaddr.sin_addr));
>> +  char orig_host[64], orig_port[16];
>
> Same comment as for gdbreplay.

Consider it done.

>> +
>> +  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
>> +		       orig_host, sizeof (orig_host),
>> +		       orig_port, sizeof (orig_port),
>> +		       NI_NUMERICHOST | NI_NUMERICSERV);
>> +
>> +  if (r != 0)
>> +    fprintf (stderr, _("Could not obtain remote address: %s\n"),
>> +	     gai_strerror (r));
>> +  else
>> +    fprintf (stderr, "Remote debugging from host %s, port %s\n", orig_host,
>> +	     orig_port);
>
> While at it, couple you please add the missing _() for i18n.

Done.

> BTW, is that line too long?  Can't tell from email client.

78 chars with _() added.  I've decided to break it anyway.

>>  
>> @@ -354,18 +399,24 @@ remote_open (const char *name)
>>  #endif /* USE_WIN32API */
>>    else
>>      {
>> -      int port;
>> -      socklen_t len;
>> -      struct sockaddr_in sockaddr;
>> -
>> -      len = sizeof (sockaddr);
>> -      if (getsockname (listen_desc,
>> -		       (struct sockaddr *) &sockaddr, &len) < 0
>> -	  || len < sizeof (sockaddr))
>> +      char listen_port[16];
>> +      struct sockaddr_storage sockaddr;
>> +      socklen_t len = sizeof (sockaddr);
>> +
>> +      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
>>  	perror_with_name ("Can't determine port");
>> -      port = ntohs (sockaddr.sin_port);
>>  
>> -      fprintf (stderr, "Listening on port %d\n", port);
>> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
>> +			   NULL, 0,
>> +			   listen_port, sizeof (listen_port),
>> +			   NI_NUMERICSERV);
>> +
>> +      if (r != 0)
>> +	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
>> +		 gai_strerror (r));
>> +      else
>> +	fprintf (stderr, "Listening on port %s\n", listen_port);
>
> Preexisting, but while at it, adding the _() wouldn't hurt.

Done.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08  1:13   ` Sergio Durigan Junior
@ 2018-06-08 13:53     ` Pedro Alves
  2018-06-08 17:47       ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-08 13:53 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On 06/08/2018 02:13 AM, Sergio Durigan Junior wrote:
> On Wednesday, June 06 2018, Pedro Alves wrote:


>>> Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
>>> parameter, which instructs GDB and gdbserver to use IPv6 for
>>> connections.  This way, if you want to run IPv6 tests, you do:
>>>
>>>   $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'
>>
>> That sounds useful, but:
>>
>> #1 - I don't see how that works without also passing
>>      --target_board= pointing at one of the native-gdbserver and
>>      native-extended-gdbserver board files.  
>>      Can you expand on why you took this approach instead of:
>>  
>>   a) handling GDB_TEST_IPV6 somewhere central, like
>>      in gdb/testsuite/gdbserver-support.exp, where we
>>      default to "localhost:".  That would exercise the gdb.server/
>>      tests with ipv6, when testing with the default/unix board file.
>>
>>   b) add new board files to test with ipv6, like native-gdbserver-v6
>>      or something like that.
>>
>>   c) both?
> 
> I was thinking about a good way to test this feature, and my initial
> assumption was that the test would only make sense when --target-board=
> is passed.  That's why I chose to implement the mechanism on
> gdb/testsuite/boards/gdbserver-base.exp.  Now that you mentioned this, I
> noticed that I should have also mentioned these expectations while
> writing the commit message, and that the "make check-gdb
> RUNTESTFLAGS='GDB_TEST_IPV6=1'" is actually wrong because it doesn't
> specify any of the target boards.
> 
> Having said that, and after reading your question, I understand that the
> testing can be made more flexible by implementing the logic inside
> gdb/testsuite/gdbserver-support.exp instead, which will have the benefit
> of activating the test even without a gdbserver target board being
> specified.  I will give it a try and see if I can implement it in a
> better way.

I'd think you just have to hook the GDB_TEST_LOCALHOST env var reading here,
in gdbserver_start:

    # Extract the local and remote host ids from the target board struct.
    if [target_info exists sockethost] {
	set debughost [target_info sockethost]
    } else {
	set debughost "localhost:"
    }

I'd also try removing the

  set_board_info sockethost "localhost:"

line from native-gdbserver.exp and native-extended-gdbserver.exp,
since that's the default.  But it's not really necessary if 
the env var takes precedence of the target board setting.

>> Does connecting with "localhost6:port" default to IPv6, BTW?
>> At least fedora includes "localhost6" in /etc/hosts.
> 
> Using "localhost6:port" works, but it doesn't default to IPv6.  Here's
> what I see on the gdbserver side:
> 
>   $ ./gdb/gdbserver/gdbserver --once localhost6:1234 a.out
>   Process /path/to/a.out created; pid = 7742
>   Listening on port 1234
>   Remote debugging from host ::ffff:127.0.0.1, port 39196
> 
> This means that the connection came using IPv4; it works because IPv6
> sockets also listen for IPv4 connection on Linux (one can change this
> behaviour by setting the "IPV6_V6ONLY" socket option).
> 
> This happens because I've made a decision to default to AF_INET (instead
> of AF_UNSPEC) when no prefix has been given.  This basically means that,
> at least for now, we assume that an unknown (i.e., not prefixed)
> address/hostname is IPv4.  I've made this decision thinking about the
> convenience of the user: when AF_UNSPEC is used (and the user hasn't
> specified any prefix), getaddrinfo will return a linked list of possible
> addresses that we should try to connect to, which usually means an IPv6
> and an IPv4 address, in that order.  Usually this is fine, because (as I
> said) IPv6 sockets can also listen for IPv4 connections.  However, if
> you start gdbserver with an explicit IPv4 address:
> 
>   $ ./gdb/gdbserver/gdbserver --once 127.0.0.1:1234 a.out
> 
> and try to connect GDB to it using an "ambiguous" hostname:
> 
>   $ ./gdb/gdb -ex 'target remote localhost:1234' a.out
> 
> you will notice that GDB will take a somewhat long time trying to
> connect (to the IPv6 address, because of AF_UNSPEC), and then it will
> error out saying that the connection timed out:
> 
>   tcp:localhost:1234: Connection timed out.

How do other tools handle this?  For example, with ping, I get:

 $ ping localhost
 PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.048 ms
 ^C

 $ ping localhost6
 PING localhost6(localhost6.localdomain6 (::1)) 56 data bytes
 64 bytes from localhost6.localdomain6 (::1): icmp_seq=1 ttl=64 time=0.086 ms
 ^C

how does ping instantly know without visible delay that "localhost"
resolves to an IPv4 address, and that "localhost6" resolves to
an IPv6 address?

Same with telnet:

 $ telnet localhost
 Trying 127.0.0.1...
 telnet: connect to address 127.0.0.1: Connection refused
 $ telnet localhost6
 Trying ::1...
 telnet: connect to address ::1: Connection refused

Same with netcat:

 $ nc -vv localhost
 Ncat: Version 7.60 ( https://nmap.org/ncat )
 NCAT DEBUG: Using system default trusted CA certificates and those in /usr/share/ncat/ca-bundle.crt.
 NCAT DEBUG: Unable to load trusted CA certificates from /usr/share/ncat/ca-bundle.crt: error:02001002:system library:fopen:No such file or directory
 libnsock nsock_iod_new2(): nsock_iod_new (IOD #1)
 libnsock nsock_connect_tcp(): TCP connection requested to 127.0.0.1:31337 (IOD #1) EID 8
                                                           ^^^^^^^^^
 libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 8 [127.0.0.1:31337]
 Ncat: Connection refused.

 $ nc -vv localhost6
 Ncat: Version 7.60 ( https://nmap.org/ncat )
 NCAT DEBUG: Using system default trusted CA certificates and those in /usr/share/ncat/ca-bundle.crt.
 NCAT DEBUG: Unable to load trusted CA certificates from /usr/share/ncat/ca-bundle.crt: error:02001002:system library:fopen:No such file or directory
 libnsock nsock_iod_new2(): nsock_iod_new (IOD #1)
 libnsock nsock_connect_tcp(): TCP connection requested to ::1:31337 (IOD #1) EID 8
                                                           ^^^
 libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 8 [::1:31337]
 Ncat: Connection refused.
[

BTW, I think a much more common scenario of local use of
gdbserver is to omit the host name:

 ./gdb/gdbserver/gdbserver --once :1234
 ./gdb/gdb -ex 'target remote :1234'

I assume that would work fine with AF_UNSPEC ?

> 
> This is because of the auto-retry mechanism implemented for TCP
> connections on GDB; it keeps retrying to connect to the IPv6 until it
> decides it's not going to work.  Only after this timeout is that GDB
> will try to connect to the IPv4 address, and succeed.
> 
> So, the way I see it, we have a few options to deal with this scenario:
> 
> 1) Assume that the unprefixed address/hostname is AF_INET (i.e., keep
> the patch as-is).
> 
> 2) Don't assume anything about the unprefixed address/hostname (i.e.,
> AF_UNSPEC), and don't change the auto-retry system.  This is not very
> nice because of what I explained above.
> 
> 3) Don't assume anything about the unprefixed address/hostname (i.e.,
> AF_UNSPEC), but *DO* change the auto-retry system to retry less times
> (currently it's set to 15 retries, which seems too much to me).  Maybe 5
> times is enough?  This will still have an impact on the user, but she
> will have to wait less time, at least.
> 
> Either (1) or (3) are fine by me.  If we go with (1), we'll eventually
> need to change the default to IPv6 (or to AF_UNSPEC), but that's only
> when IPv6 is more adopted.

I'd like to understand this a bit more before coming up with a
decision.  I feel like we're missing something.

A part of it is that it kind of looks like a "it hurts when I do this
doctor; then just don't" scenario, with the using different host names
on both gdbserver and gdb (localhost vs 127.0.0.1).  Why would you do that
for local debugging?  You'd get the same problem if localhost
always only resolved to an IPv6 address, I'd think.  But still, I'd
like to understand how can other tools handle this.

>>> +  char *orig_name = strdup (name);
>>
>> Do we need a deep copy?  And if we do, how about
>> using std::string to avoid having to call free further
>> down?
> 
> This is gdbserver/gdbreplay.c, where apparently we don't have access to
> a lot of our regular facilities on GDB.  For example, I was trying to
> use std::string, its methods, and other stuff here (even i18n
> functions), but the code won't compile, and as far as I have researched
> this is intentional, because gdbreplay needs to be a very small and
> simple program.  

What did you find that gave you that impression?  There's no reason
that gdbreplay needs to be small or simple.  Certainly doesn't need
to be smaller than gdbserver.

> at least that's what I understood from our
> archives/documentation.  I did not feel confident reworking gdbreplay to
> make it "modern", so I decided to implement things "the old way".

Seems like adding to technical debt to be honest.  Did you hit some
unsurmountable problem, or would just a little bit of fixing here and
there be doable?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 13:53     ` Pedro Alves
@ 2018-06-08 17:47       ` Sergio Durigan Junior
  2018-06-08 18:44         ` Pedro Alves
  0 siblings, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-08 17:47 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Friday, June 08 2018, Pedro Alves wrote:

> On 06/08/2018 02:13 AM, Sergio Durigan Junior wrote:
>> On Wednesday, June 06 2018, Pedro Alves wrote:
>
>
>>>> Another thing worth mentioning is the new 'GDB_TEST_IPV6' testcase
>>>> parameter, which instructs GDB and gdbserver to use IPv6 for
>>>> connections.  This way, if you want to run IPv6 tests, you do:
>>>>
>>>>   $ make check-gdb RUNTESTFLAGS='GDB_TEST_IPV6=1'
>>>
>>> That sounds useful, but:
>>>
>>> #1 - I don't see how that works without also passing
>>>      --target_board= pointing at one of the native-gdbserver and
>>>      native-extended-gdbserver board files.  
>>>      Can you expand on why you took this approach instead of:
>>>  
>>>   a) handling GDB_TEST_IPV6 somewhere central, like
>>>      in gdb/testsuite/gdbserver-support.exp, where we
>>>      default to "localhost:".  That would exercise the gdb.server/
>>>      tests with ipv6, when testing with the default/unix board file.
>>>
>>>   b) add new board files to test with ipv6, like native-gdbserver-v6
>>>      or something like that.
>>>
>>>   c) both?
>> 
>> I was thinking about a good way to test this feature, and my initial
>> assumption was that the test would only make sense when --target-board=
>> is passed.  That's why I chose to implement the mechanism on
>> gdb/testsuite/boards/gdbserver-base.exp.  Now that you mentioned this, I
>> noticed that I should have also mentioned these expectations while
>> writing the commit message, and that the "make check-gdb
>> RUNTESTFLAGS='GDB_TEST_IPV6=1'" is actually wrong because it doesn't
>> specify any of the target boards.
>> 
>> Having said that, and after reading your question, I understand that the
>> testing can be made more flexible by implementing the logic inside
>> gdb/testsuite/gdbserver-support.exp instead, which will have the benefit
>> of activating the test even without a gdbserver target board being
>> specified.  I will give it a try and see if I can implement it in a
>> better way.
>
> I'd think you just have to hook the GDB_TEST_LOCALHOST env var reading here,
> in gdbserver_start:
>
>     # Extract the local and remote host ids from the target board struct.
>     if [target_info exists sockethost] {
> 	set debughost [target_info sockethost]
>     } else {
> 	set debughost "localhost:"
>     }

Yes, that's my plan.

> I'd also try removing the
>
>   set_board_info sockethost "localhost:"
>
> line from native-gdbserver.exp and native-extended-gdbserver.exp,
> since that's the default.  But it's not really necessary if 
> the env var takes precedence of the target board setting.

I wasn't planning to remove these lines from the board files, but I can
do it.

>>> Does connecting with "localhost6:port" default to IPv6, BTW?
>>> At least fedora includes "localhost6" in /etc/hosts.
>> 
>> Using "localhost6:port" works, but it doesn't default to IPv6.  Here's
>> what I see on the gdbserver side:
>> 
>>   $ ./gdb/gdbserver/gdbserver --once localhost6:1234 a.out
>>   Process /path/to/a.out created; pid = 7742
>>   Listening on port 1234
>>   Remote debugging from host ::ffff:127.0.0.1, port 39196
>> 
>> This means that the connection came using IPv4; it works because IPv6
>> sockets also listen for IPv4 connection on Linux (one can change this
>> behaviour by setting the "IPV6_V6ONLY" socket option).
>> 
>> This happens because I've made a decision to default to AF_INET (instead
>> of AF_UNSPEC) when no prefix has been given.  This basically means that,
>> at least for now, we assume that an unknown (i.e., not prefixed)
>> address/hostname is IPv4.  I've made this decision thinking about the
>> convenience of the user: when AF_UNSPEC is used (and the user hasn't
>> specified any prefix), getaddrinfo will return a linked list of possible
>> addresses that we should try to connect to, which usually means an IPv6
>> and an IPv4 address, in that order.  Usually this is fine, because (as I
>> said) IPv6 sockets can also listen for IPv4 connections.  However, if
>> you start gdbserver with an explicit IPv4 address:
>> 
>>   $ ./gdb/gdbserver/gdbserver --once 127.0.0.1:1234 a.out
>> 
>> and try to connect GDB to it using an "ambiguous" hostname:
>> 
>>   $ ./gdb/gdb -ex 'target remote localhost:1234' a.out
>> 
>> you will notice that GDB will take a somewhat long time trying to
>> connect (to the IPv6 address, because of AF_UNSPEC), and then it will
>> error out saying that the connection timed out:
>> 
>>   tcp:localhost:1234: Connection timed out.
>
> How do other tools handle this?

Just like GDB.


> For example, with ping, I get:
>
>  $ ping localhost
>  PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
>  64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.048 ms
>  ^C
>
>  $ ping localhost6
>  PING localhost6(localhost6.localdomain6 (::1)) 56 data bytes
>  64 bytes from localhost6.localdomain6 (::1): icmp_seq=1 ttl=64 time=0.086 ms
>  ^C

And I get:

  $ ping localhost
  PING localhost(localhost (::1)) 56 data bytes
  64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.050 ms
  ^C

  $ ping localhost6
  PING localhost6(localhost (::1)) 56 data bytes
  64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.089 ms
  ^C

Maybe your /etc/hosts is different than mine:

  127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
  ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

> how does ping instantly know without visible delay that "localhost"
> resolves to an IPv4 address, and that "localhost6" resolves to
> an IPv6 address?

It doesn't.  It just tries to connect to the first entry returned by
getaddrinfo:

  https://github.com/iputils/iputils/blob/master/ping.c#L518

In my case, since both IPv4 and IPv6 localhost addresses are valid, it
just connects to the first one, which is the IPv6.

> Same with telnet:
>
>  $ telnet localhost
>  Trying 127.0.0.1...
>  telnet: connect to address 127.0.0.1: Connection refused
>  $ telnet localhost6
>  Trying ::1...
>  telnet: connect to address ::1: Connection refused

Using telnet allows one to see the algorithm I explained before working:

  $ telnet localhost
  Trying ::1...
  telnet: connect to address ::1: Connection refused
  Trying 127.0.0.1...
  telnet: connect to address 127.0.0.1: Connection refused

It tried to connect to the IPv6 address, got a connection refused (which
happened instantly, without delay, because telnet doesn't implement our
tcp-retry thing), then it tried to connect to the IPv4 address, which
also failed.  This is the getaddrinfo loop working.

  https://github.com/marado/netkit-telnet/blob/596f1a79d3868f733fc15765b107638822a0f8a9/netkit-telnet-0.17/telnet/commands.cc#L1808

> Same with netcat:
>
>  $ nc -vv localhost
>  Ncat: Version 7.60 ( https://nmap.org/ncat )
>  NCAT DEBUG: Using system default trusted CA certificates and those in /usr/share/ncat/ca-bundle.crt.
>  NCAT DEBUG: Unable to load trusted CA certificates from /usr/share/ncat/ca-bundle.crt: error:02001002:system library:fopen:No such file or directory
>  libnsock nsock_iod_new2(): nsock_iod_new (IOD #1)
>  libnsock nsock_connect_tcp(): TCP connection requested to 127.0.0.1:31337 (IOD #1) EID 8
>                                                            ^^^^^^^^^
>  libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 8 [127.0.0.1:31337]
>  Ncat: Connection refused.
>
>  $ nc -vv localhost6
>  Ncat: Version 7.60 ( https://nmap.org/ncat )
>  NCAT DEBUG: Using system default trusted CA certificates and those in /usr/share/ncat/ca-bundle.crt.
>  NCAT DEBUG: Unable to load trusted CA certificates from /usr/share/ncat/ca-bundle.crt: error:02001002:system library:fopen:No such file or directory
>  libnsock nsock_iod_new2(): nsock_iod_new (IOD #1)
>  libnsock nsock_connect_tcp(): TCP connection requested to ::1:31337 (IOD #1) EID 8
>                                                            ^^^
>  libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 8 [::1:31337]
>  Ncat: Connection refused.
> [

  $ nc -vv localhost
  Ncat: Version 7.60 ( https://nmap.org/ncat )
  NCAT DEBUG: Using system default trusted CA certificates and those in /usr/share/ncat/ca-bundle.crt.
  NCAT DEBUG: Unable to load trusted CA certificates from /usr/share/ncat/ca-bundle.crt: error:02001002:system library:fopen:No such file or directory
  libnsock nsock_iod_new2(): nsock_iod_new (IOD #1)
  libnsock nsock_connect_tcp(): TCP connection requested to ::1:31337 (IOD #1) EID 8
                                                            ^^^
  libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 8 [::1:31337]
  Ncat: Connection to ::1 failed: Connection refused.
  Ncat: Trying next address...
        ^^^^^^^^^^^^^^^^^^^
  libnsock nsock_connect_tcp(): TCP connection requested to 127.0.0.1:31337 (IOD #1) EID 16
                                                            ^^^^^^^^^
  libnsock nsock_trace_handler_callback(): Callback: CONNECT ERROR [Connection refused (111)] for EID 16 [127.0.0.1:31337]
  Ncat: Connection refused.

> BTW, I think a much more common scenario of local use of
> gdbserver is to omit the host name:
>
>  ./gdb/gdbserver/gdbserver --once :1234
>  ./gdb/gdb -ex 'target remote :1234'
>
> I assume that would work fine with AF_UNSPEC ?

Yes, with AF_UNSPEC we assume the hostname is "localhost" if it's not
given, and it works like it should (i.e., IPv6 first, IPv4 later).

>> 
>> This is because of the auto-retry mechanism implemented for TCP
>> connections on GDB; it keeps retrying to connect to the IPv6 until it
>> decides it's not going to work.  Only after this timeout is that GDB
>> will try to connect to the IPv4 address, and succeed.
>> 
>> So, the way I see it, we have a few options to deal with this scenario:
>> 
>> 1) Assume that the unprefixed address/hostname is AF_INET (i.e., keep
>> the patch as-is).
>> 
>> 2) Don't assume anything about the unprefixed address/hostname (i.e.,
>> AF_UNSPEC), and don't change the auto-retry system.  This is not very
>> nice because of what I explained above.
>> 
>> 3) Don't assume anything about the unprefixed address/hostname (i.e.,
>> AF_UNSPEC), but *DO* change the auto-retry system to retry less times
>> (currently it's set to 15 retries, which seems too much to me).  Maybe 5
>> times is enough?  This will still have an impact on the user, but she
>> will have to wait less time, at least.
>> 
>> Either (1) or (3) are fine by me.  If we go with (1), we'll eventually
>> need to change the default to IPv6 (or to AF_UNSPEC), but that's only
>> when IPv6 is more adopted.
>
> I'd like to understand this a bit more before coming up with a
> decision.  I feel like we're missing something.
>
> A part of it is that it kind of looks like a "it hurts when I do this
> doctor; then just don't" scenario, with the using different host names
> on both gdbserver and gdb (localhost vs 127.0.0.1).  Why would you do that
> for local debugging?  You'd get the same problem if localhost
> always only resolved to an IPv6 address, I'd think.  But still, I'd
> like to understand how can other tools handle this.

The "getaddrinfo loop" is a well known way to implement IPv6 support on
IPv4-only tools.  I think it is totally fine to iterate through the
possible addresses and try each one until we have success, but our
problem is that we implemented a retry mechanism on top of that, so when
we get "connection refused" GDB won't just try the next address, but
will keep retrying the refused one...  That is the problem.

I don't know why would anyone use different hostnames on both GDB and
gdbserver, I just stated the fact that if someone does it, she will have
problems.  And yes, you'd get the same problem if localhost always only
resolved to IPv6.  The difference is that the tools you're using for
your example don't implement retry (or at least not the way we do), so
you don't have huge delays when they can't connect to an address.

It is clear to me, after investigating this, that the problem is our
retry mechanism.  We can either adjust it to a lower delay, get rid of
it, or leave it as is and assume that unprefixed addresses are IPv4.  I
fail to see what else we're missing.

>>>> +  char *orig_name = strdup (name);
>>>
>>> Do we need a deep copy?  And if we do, how about
>>> using std::string to avoid having to call free further
>>> down?
>> 
>> This is gdbserver/gdbreplay.c, where apparently we don't have access to
>> a lot of our regular facilities on GDB.  For example, I was trying to
>> use std::string, its methods, and other stuff here (even i18n
>> functions), but the code won't compile, and as far as I have researched
>> this is intentional, because gdbreplay needs to be a very small and
>> simple program.  
>
> What did you find that gave you that impression?  There's no reason
> that gdbreplay needs to be small or simple.  Certainly doesn't need
> to be smaller than gdbserver.

First, the way it is written.  It doesn't use any of our facilities
(e.g., i18n, strdup instead of xstrdup), and it seems to be treated in a
"special" way, because it is a separate program.  I found this message:

  https://sourceware.org/ml/gdb/2008-06/msg00117.html

  > I've tried to find information in the doc about gdbreplay without luck.
  > Really quickly, does gdbreplay, as its name suggest, allow to record an
  > re-run an application session? 

  Yes, exactly -- but with rather stringent limits.
  In a nutshell, during the replay session, you must give
  EXACTLY the same sequence of gdb commands as were given
  during the record session.  gdbreplay will prompt you for
  the next command, but if you do *anything* different, 
  it will throw up its hands and quit.

And it seems to imply that gdbreplay is a very limited program.  And
Jan's first patch (back in 2006) implementing IPv6 also duplicated code
on gdbreplay.  I admit I may have read too much between the lines here,
but I just assumed that this was just the way things were.

>> at least that's what I understood from our
>> archives/documentation.  I did not feel confident reworking gdbreplay to
>> make it "modern", so I decided to implement things "the old way".
>
> Seems like adding to technical debt to be honest.  Did you hit some
> unsurmountable problem, or would just a little bit of fixing here and
> there be doable?

I don't know if it's unsurmountable.  I know I had trouble getting i18n
and trying to include a few headers here and there, but I haven't tried
very hard to work around it.  I just decided to "add to the technical
debt".

I'll take a better look at this.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 17:47       ` Sergio Durigan Junior
@ 2018-06-08 18:44         ` Pedro Alves
  2018-06-08 19:28           ` Pedro Alves
  2018-06-08 21:21           ` Sergio Durigan Junior
  0 siblings, 2 replies; 27+ messages in thread
From: Pedro Alves @ 2018-06-08 18:44 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On 06/08/2018 06:47 PM, Sergio Durigan Junior wrote:
> On Friday, June 08 2018, Pedro Alves wrote:

>>>> Does connecting with "localhost6:port" default to IPv6, BTW?
>>>> At least fedora includes "localhost6" in /etc/hosts.
>>>
>>> Using "localhost6:port" works, but it doesn't default to IPv6.  Here's
>>> what I see on the gdbserver side:
>>>
>>>   $ ./gdb/gdbserver/gdbserver --once localhost6:1234 a.out
>>>   Process /path/to/a.out created; pid = 7742
>>>   Listening on port 1234
>>>   Remote debugging from host ::ffff:127.0.0.1, port 39196
>>>
>>> This means that the connection came using IPv4; it works because IPv6
>>> sockets also listen for IPv4 connection on Linux (one can change this
>>> behaviour by setting the "IPV6_V6ONLY" socket option).
>>>
>>> This happens because I've made a decision to default to AF_INET (instead
>>> of AF_UNSPEC) when no prefix has been given.  This basically means that,
>>> at least for now, we assume that an unknown (i.e., not prefixed)
>>> address/hostname is IPv4.  I've made this decision thinking about the
>>> convenience of the user: when AF_UNSPEC is used (and the user hasn't
>>> specified any prefix), getaddrinfo will return a linked list of possible
>>> addresses that we should try to connect to, which usually means an IPv6
>>> and an IPv4 address, in that order.  Usually this is fine, because (as I
>>> said) IPv6 sockets can also listen for IPv4 connections.  However, if
>>> you start gdbserver with an explicit IPv4 address:
>>>
>>>   $ ./gdb/gdbserver/gdbserver --once 127.0.0.1:1234 a.out
>>>
>>> and try to connect GDB to it using an "ambiguous" hostname:
>>>
>>>   $ ./gdb/gdb -ex 'target remote localhost:1234' a.out
>>>
>>> you will notice that GDB will take a somewhat long time trying to
>>> connect (to the IPv6 address, because of AF_UNSPEC), and then it will
>>> error out saying that the connection timed out:
>>>
>>>   tcp:localhost:1234: Connection timed out.
>>
>> How do other tools handle this?
> 
> Just like GDB.

Well, it sounds like they do the AF_UNSPEC thing, instead of
defaulting to AF_INET.

> 
> 
>> For example, with ping, I get:
>>
>>  $ ping localhost
>>  PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
>>  64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.048 ms
>>  ^C
>>
>>  $ ping localhost6
>>  PING localhost6(localhost6.localdomain6 (::1)) 56 data bytes
>>  64 bytes from localhost6.localdomain6 (::1): icmp_seq=1 ttl=64 time=0.086 ms
>>  ^C
> 
> And I get:
> 
>   $ ping localhost
>   PING localhost(localhost (::1)) 56 data bytes
>   64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.050 ms
>   ^C
> 
>   $ ping localhost6
>   PING localhost6(localhost (::1)) 56 data bytes
>   64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.089 ms
>   ^C
> 
> Maybe your /etc/hosts is different than mine:
> 
>   127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
>   ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

Looks like it:

$ cat /etc/hosts
127.0.0.1               localhost.localdomain localhost
::1             localhost6.localdomain6 localhost6

This is on F27 (upgraded a few times).  I don't remember if I ever
changed this manually.

> 
>> how does ping instantly know without visible delay that "localhost"
>> resolves to an IPv4 address, and that "localhost6" resolves to
>> an IPv6 address?
> 
> It doesn't.  It just tries to connect to the first entry returned by
> getaddrinfo:
> 
>   https://github.com/iputils/iputils/blob/master/ping.c#L518
> 
> In my case, since both IPv4 and IPv6 localhost addresses are valid, it
> just connects to the first one, which is the IPv6.

OK.

> Using telnet allows one to see the algorithm I explained before working:

OK.

> The "getaddrinfo loop" is a well known way to implement IPv6 support on
> IPv4-only tools.  I think it is totally fine to iterate through the
> possible addresses and try each one until we have success, but our
> problem is that we implemented a retry mechanism on top of that, so when
> we get "connection refused" GDB won't just try the next address, but
> will keep retrying the refused one...  That is the problem.

Yes.

> I don't know why would anyone use different hostnames on both GDB and
> gdbserver, I just stated the fact that if someone does it, she will have
> problems.  And yes, you'd get the same problem if localhost always only
> resolved to IPv6.  The difference is that the tools you're using for
> your example don't implement retry (or at least not the way we do), so
> you don't have huge delays when they can't connect to an address.
> 
> It is clear to me, after investigating this, that the problem is our
> retry mechanism.  

Agreed, it's clear to me too.

> We can either adjust it to a lower delay, get rid of
> it, or leave it as is and assume that unprefixed addresses are IPv4.  I
> fail to see what else we're missing.

The "assume unprefixed addresses are IPv4" seems like the worse
option to me, as it's a work around.  Let's tackle the real issue
instead.

We could consider for example more verbose progress indication,
or cycling the whole "getaddrinfo loop" before waiting to retry instead
of waiting after each individual connection failure.

> 
>>>>> +  char *orig_name = strdup (name);
>>>>
>>>> Do we need a deep copy?  And if we do, how about
>>>> using std::string to avoid having to call free further
>>>> down?
>>>
>>> This is gdbserver/gdbreplay.c, where apparently we don't have access to
>>> a lot of our regular facilities on GDB.  For example, I was trying to
>>> use std::string, its methods, and other stuff here (even i18n
>>> functions), but the code won't compile, and as far as I have researched
>>> this is intentional, because gdbreplay needs to be a very small and
>>> simple program.  
>>
>> What did you find that gave you that impression?  There's no reason
>> that gdbreplay needs to be small or simple.  Certainly doesn't need
>> to be smaller than gdbserver.
> 
> First, the way it is written.  It doesn't use any of our facilities
> (e.g., i18n, strdup instead of xstrdup), and it seems to be treated in a
> "special" way, because it is a separate program.  I found this message:
> 
>   https://sourceware.org/ml/gdb/2008-06/msg00117.html
> 
>   > I've tried to find information in the doc about gdbreplay without luck.
>   > Really quickly, does gdbreplay, as its name suggest, allow to record an
>   > re-run an application session? 
> 
>   Yes, exactly -- but with rather stringent limits.
>   In a nutshell, during the replay session, you must give
>   EXACTLY the same sequence of gdb commands as were given
>   during the record session.  gdbreplay will prompt you for
>   the next command, but if you do *anything* different, 
>   it will throw up its hands and quit.
> 
> And it seems to imply that gdbreplay is a very limited program.  

The "stringent limits" refers to what you can do from gdb
when connected to gdbreplay, not to gdbreplay's running environment.

The tool is useful for support, to e.g., reproduce bugs that only
trigger against remote stubs / embedded probes that the user has
access to, other than gdbserver.  You ask the user to use "set remotelogfile"
to record the remote protocol traffic against the user's remote stub, and
then the user reproduces the bug.  The user sends you the resulting
remote protocol log file, and you load it against gdbreplay.  Then, using
the same gdb version and same program binary the user used, you connect
to gdbreplay, and use the exact same set of gdb commands the user used.
Given those constraints, gdb should send the exact same remote protocol
packets that the user's gdb sent to the users stub.  And since gdbreplay
is replaying the remote protocol traffic the original stub sent, you'll be
able to reproduce the bug locally.  If you use a different set of commands,
then gdb will send different packets, and the log that gdbreplay is
replaying of course breaks down.

So gdbreplay runs on the host computer.  There's no need to care about
making it super tiny of anything like that.


And> 
> Jan's first patch (back in 2006) implementing IPv6 also duplicated code
> on gdbreplay.  I admit I may have read too much between the lines here,
> but I just assumed that this was just the way things were.
> 
>>> at least that's what I understood from our
>>> archives/documentation.  I did not feel confident reworking gdbreplay to
>>> make it "modern", so I decided to implement things "the old way".
>>
>> Seems like adding to technical debt to be honest.  Did you hit some
>> unsurmountable problem, or would just a little bit of fixing here and
>> there be doable?
> 
> I don't know if it's unsurmountable.  I know I had trouble getting i18n
> and trying to include a few headers here and there, but I haven't tried
> very hard to work around it.  I just decided to "add to the technical
> debt".
> 
> I'll take a better look at this.
Thanks.

Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 18:44         ` Pedro Alves
@ 2018-06-08 19:28           ` Pedro Alves
  2018-06-08 19:51             ` Pedro Alves
  2018-06-08 21:21           ` Sergio Durigan Junior
  1 sibling, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-08 19:28 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On 06/08/2018 07:44 PM, Pedro Alves wrote:

>> Jan's first patch (back in 2006) implementing IPv6 also duplicated code
>> on gdbreplay.  I admit I may have read too much between the lines here,
>> but I just assumed that this was just the way things were.
>>
>>>> at least that's what I understood from our
>>>> archives/documentation.  I did not feel confident reworking gdbreplay to
>>>> make it "modern", so I decided to implement things "the old way".
>>> Seems like adding to technical debt to be honest.  Did you hit some
>>> unsurmountable problem, or would just a little bit of fixing here and
>>> there be doable?
>> I don't know if it's unsurmountable.  I know I had trouble getting i18n
>> and trying to include a few headers here and there, but I haven't tried
>> very hard to work around it.  I just decided to "add to the technical
>> debt".
>>
>> I'll take a better look at this.
> Thanks.

I took a quick look at it.  I'll send a patch.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 19:28           ` Pedro Alves
@ 2018-06-08 19:51             ` Pedro Alves
  2018-06-08 20:43               ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-08 19:51 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On 06/08/2018 08:28 PM, Pedro Alves wrote:

> I took a quick look at it.  I'll send a patch.

Done <https://sourceware.org/ml/gdb-patches/2018-06/msg00232.html>.

I first wrote that on top of your patch, and made sure that
including "netstuff.h" worked.  Hopefully all you'll need to
do is add netstuff.o in the Makefile and whatever dependencies
that might have (hopefully none or not many).

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 19:51             ` Pedro Alves
@ 2018-06-08 20:43               ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-08 20:43 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Friday, June 08 2018, Pedro Alves wrote:

> On 06/08/2018 08:28 PM, Pedro Alves wrote:
>
>> I took a quick look at it.  I'll send a patch.
>
> Done <https://sourceware.org/ml/gdb-patches/2018-06/msg00232.html>.
>
> I first wrote that on top of your patch, and made sure that
> including "netstuff.h" worked.  Hopefully all you'll need to
> do is add netstuff.o in the Makefile and whatever dependencies
> that might have (hopefully none or not many).

Thanks, much appreciated, Pedro.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 18:44         ` Pedro Alves
  2018-06-08 19:28           ` Pedro Alves
@ 2018-06-08 21:21           ` Sergio Durigan Junior
  2018-06-08 21:51             ` Pedro Alves
  1 sibling, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-08 21:21 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Friday, June 08 2018, Pedro Alves wrote:

> On 06/08/2018 06:47 PM, Sergio Durigan Junior wrote:
>> On Friday, June 08 2018, Pedro Alves wrote:
>
>>>>> Does connecting with "localhost6:port" default to IPv6, BTW?
>>>>> At least fedora includes "localhost6" in /etc/hosts.
>>>>
>>>> Using "localhost6:port" works, but it doesn't default to IPv6.  Here's
>>>> what I see on the gdbserver side:
>>>>
>>>>   $ ./gdb/gdbserver/gdbserver --once localhost6:1234 a.out
>>>>   Process /path/to/a.out created; pid = 7742
>>>>   Listening on port 1234
>>>>   Remote debugging from host ::ffff:127.0.0.1, port 39196
>>>>
>>>> This means that the connection came using IPv4; it works because IPv6
>>>> sockets also listen for IPv4 connection on Linux (one can change this
>>>> behaviour by setting the "IPV6_V6ONLY" socket option).
>>>>
>>>> This happens because I've made a decision to default to AF_INET (instead
>>>> of AF_UNSPEC) when no prefix has been given.  This basically means that,
>>>> at least for now, we assume that an unknown (i.e., not prefixed)
>>>> address/hostname is IPv4.  I've made this decision thinking about the
>>>> convenience of the user: when AF_UNSPEC is used (and the user hasn't
>>>> specified any prefix), getaddrinfo will return a linked list of possible
>>>> addresses that we should try to connect to, which usually means an IPv6
>>>> and an IPv4 address, in that order.  Usually this is fine, because (as I
>>>> said) IPv6 sockets can also listen for IPv4 connections.  However, if
>>>> you start gdbserver with an explicit IPv4 address:
>>>>
>>>>   $ ./gdb/gdbserver/gdbserver --once 127.0.0.1:1234 a.out
>>>>
>>>> and try to connect GDB to it using an "ambiguous" hostname:
>>>>
>>>>   $ ./gdb/gdb -ex 'target remote localhost:1234' a.out
>>>>
>>>> you will notice that GDB will take a somewhat long time trying to
>>>> connect (to the IPv6 address, because of AF_UNSPEC), and then it will
>>>> error out saying that the connection timed out:
>>>>
>>>>   tcp:localhost:1234: Connection timed out.
>>>
>>> How do other tools handle this?
>> 
>> Just like GDB.
>
> Well, it sounds like they do the AF_UNSPEC thing, instead of
> defaulting to AF_INET.

Yeah.  Sorry, I was comparing with the GDB I currently have here, which
uses AF_UNSPEC because I was doing a few tests and changes.

>>> For example, with ping, I get:
>>>
>>>  $ ping localhost
>>>  PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
>>>  64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.048 ms
>>>  ^C
>>>
>>>  $ ping localhost6
>>>  PING localhost6(localhost6.localdomain6 (::1)) 56 data bytes
>>>  64 bytes from localhost6.localdomain6 (::1): icmp_seq=1 ttl=64 time=0.086 ms
>>>  ^C
>> 
>> And I get:
>> 
>>   $ ping localhost
>>   PING localhost(localhost (::1)) 56 data bytes
>>   64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.050 ms
>>   ^C
>> 
>>   $ ping localhost6
>>   PING localhost6(localhost (::1)) 56 data bytes
>>   64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.089 ms
>>   ^C
>> 
>> Maybe your /etc/hosts is different than mine:
>> 
>>   127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
>>   ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
>
> Looks like it:
>
> $ cat /etc/hosts
> 127.0.0.1               localhost.localdomain localhost
> ::1             localhost6.localdomain6 localhost6
>
> This is on F27 (upgraded a few times).  I don't remember if I ever
> changed this manually.

I've never changed mine either, but this is from a fresh F27 install.

>> We can either adjust it to a lower delay, get rid of
>> it, or leave it as is and assume that unprefixed addresses are IPv4.  I
>> fail to see what else we're missing.
>
> The "assume unprefixed addresses are IPv4" seems like the worse
> option to me, as it's a work around.  Let's tackle the real issue
> instead.
>
> We could consider for example more verbose progress indication,
> or cycling the whole "getaddrinfo loop" before waiting to retry instead
> of waiting after each individual connection failure.

A more verbose indication would be nice, as well as a way to control how
many retries GDB should perform.

I'm not sure about cycling the whole loop before waiting to retry...  I
mean, it works, but I'm not sure it's what the user would expect from a
"retry" mechanism.  I would expect GDB to "retry this address X times,
and then go to the next", instead of "retry all the addresses in a
loop".  But that can be documented, sure.

On a side note, I have to ask: why not decrease the number of retries
to, say, 5?  This would still add a delay, but it'd be much shorter.

>> 
>>>>>> +  char *orig_name = strdup (name);
>>>>>
>>>>> Do we need a deep copy?  And if we do, how about
>>>>> using std::string to avoid having to call free further
>>>>> down?
>>>>
>>>> This is gdbserver/gdbreplay.c, where apparently we don't have access to
>>>> a lot of our regular facilities on GDB.  For example, I was trying to
>>>> use std::string, its methods, and other stuff here (even i18n
>>>> functions), but the code won't compile, and as far as I have researched
>>>> this is intentional, because gdbreplay needs to be a very small and
>>>> simple program.  
>>>
>>> What did you find that gave you that impression?  There's no reason
>>> that gdbreplay needs to be small or simple.  Certainly doesn't need
>>> to be smaller than gdbserver.
>> 
>> First, the way it is written.  It doesn't use any of our facilities
>> (e.g., i18n, strdup instead of xstrdup), and it seems to be treated in a
>> "special" way, because it is a separate program.  I found this message:
>> 
>>   https://sourceware.org/ml/gdb/2008-06/msg00117.html
>> 
>>   > I've tried to find information in the doc about gdbreplay without luck.
>>   > Really quickly, does gdbreplay, as its name suggest, allow to record an
>>   > re-run an application session? 
>> 
>>   Yes, exactly -- but with rather stringent limits.
>>   In a nutshell, during the replay session, you must give
>>   EXACTLY the same sequence of gdb commands as were given
>>   during the record session.  gdbreplay will prompt you for
>>   the next command, but if you do *anything* different, 
>>   it will throw up its hands and quit.
>> 
>> And it seems to imply that gdbreplay is a very limited program.  
>
> The "stringent limits" refers to what you can do from gdb
> when connected to gdbreplay, not to gdbreplay's running environment.

Right, I got that.  As I said, I think I read too much between the lines
here.

> The tool is useful for support, to e.g., reproduce bugs that only
> trigger against remote stubs / embedded probes that the user has
> access to, other than gdbserver.  You ask the user to use "set remotelogfile"
> to record the remote protocol traffic against the user's remote stub, and
> then the user reproduces the bug.  The user sends you the resulting
> remote protocol log file, and you load it against gdbreplay.  Then, using
> the same gdb version and same program binary the user used, you connect
> to gdbreplay, and use the exact same set of gdb commands the user used.
> Given those constraints, gdb should send the exact same remote protocol
> packets that the user's gdb sent to the users stub.  And since gdbreplay
> is replaying the remote protocol traffic the original stub sent, you'll be
> able to reproduce the bug locally.  If you use a different set of commands,
> then gdb will send different packets, and the log that gdbreplay is
> replaying of course breaks down.
> 
> So gdbreplay runs on the host computer.  There's no need to care about
> making it super tiny of anything like that.

We should add this to a wiki or to our official docs.  I think it's the
more complete explanation I've read about gdbreplay so far.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 21:21           ` Sergio Durigan Junior
@ 2018-06-08 21:51             ` Pedro Alves
  2018-06-08 22:01               ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-08 21:51 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On 06/08/2018 10:21 PM, Sergio Durigan Junior wrote:
> On Friday, June 08 2018, Pedro Alves wrote:

>>> We can either adjust it to a lower delay, get rid of
>>> it, or leave it as is and assume that unprefixed addresses are IPv4.  I
>>> fail to see what else we're missing.
>>
>> The "assume unprefixed addresses are IPv4" seems like the worse
>> option to me, as it's a work around.  Let's tackle the real issue
>> instead.
>>
>> We could consider for example more verbose progress indication,
>> or cycling the whole "getaddrinfo loop" before waiting to retry instead
>> of waiting after each individual connection failure.
> 
> A more verbose indication would be nice, as well as a way to control how
> many retries GDB should perform.
> 
> I'm not sure about cycling the whole loop before waiting to retry...  I
> mean, it works, but I'm not sure it's what the user would expect from a
> "retry" mechanism.  I would expect GDB to "retry this address X times,
> and then go to the next", instead of "retry all the addresses in a
> loop".  But that can be documented, sure.

Cycling the whole loop seems to me the best option.  The retry mechanism
exists because:

 @item set tcp auto-retry on
 @cindex auto-retry, for remote TCP target
 Enable auto-retry for remote TCP connections.  This is useful if the remote
 debugging agent is launched in parallel with @value{GDBN}; there is a race
 condition because the agent may not become ready to accept the connection
 before @value{GDBN} attempts to connect.  When auto-retry is
 enabled, if the initial attempt to connect fails, @value{GDBN} reattempts
 to establish the connection using the timeout specified by 
 @code{set tcp connect-timeout}.

If we cycle the whole loop before retrying we end up with a tiny tiny race
window where gdb may have tried IPv6, that failing because gdbserver was not
listening yet, and then gdb trying IPv4 and that succeeding.  In that rare
scenario, if gdb had started looping just a fraction of a second earlier, it
would have connected with IPv6 successfully.  But, so what?  It will have connected
successfully anyway, and IPv6 vs IPv4 will hardly make a real difference.
Users that really really really want to ensure to get IPv6 or IPv4 should use
the "tcp6:" or "tcp4:" prefixes.  So I'm not seeing any downside the whole loop
approach.

> 
> On a side note, I have to ask: why not decrease the number of retries
> to, say, 5?  This would still add a delay, but it'd be much shorter.

Because the solution above sounds much better to me.  No delay.

> We should add this to a wiki or to our official docs.  I think it's the
> more complete explanation I've read about gdbreplay so far.
That might be a good idea.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] Implement IPv6 support for GDB/gdbserver
  2018-06-08 21:51             ` Pedro Alves
@ 2018-06-08 22:01               ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-08 22:01 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki

On Friday, June 08 2018, Pedro Alves wrote:

> On 06/08/2018 10:21 PM, Sergio Durigan Junior wrote:
>> On Friday, June 08 2018, Pedro Alves wrote:
>
>>>> We can either adjust it to a lower delay, get rid of
>>>> it, or leave it as is and assume that unprefixed addresses are IPv4.  I
>>>> fail to see what else we're missing.
>>>
>>> The "assume unprefixed addresses are IPv4" seems like the worse
>>> option to me, as it's a work around.  Let's tackle the real issue
>>> instead.
>>>
>>> We could consider for example more verbose progress indication,
>>> or cycling the whole "getaddrinfo loop" before waiting to retry instead
>>> of waiting after each individual connection failure.
>> 
>> A more verbose indication would be nice, as well as a way to control how
>> many retries GDB should perform.
>> 
>> I'm not sure about cycling the whole loop before waiting to retry...  I
>> mean, it works, but I'm not sure it's what the user would expect from a
>> "retry" mechanism.  I would expect GDB to "retry this address X times,
>> and then go to the next", instead of "retry all the addresses in a
>> loop".  But that can be documented, sure.
>
> Cycling the whole loop seems to me the best option.  The retry mechanism
> exists because:
>
>  @item set tcp auto-retry on
>  @cindex auto-retry, for remote TCP target
>  Enable auto-retry for remote TCP connections.  This is useful if the remote
>  debugging agent is launched in parallel with @value{GDBN}; there is a race
>  condition because the agent may not become ready to accept the connection
>  before @value{GDBN} attempts to connect.  When auto-retry is
>  enabled, if the initial attempt to connect fails, @value{GDBN} reattempts
>  to establish the connection using the timeout specified by 
>  @code{set tcp connect-timeout}.
>
> If we cycle the whole loop before retrying we end up with a tiny tiny race
> window where gdb may have tried IPv6, that failing because gdbserver was not
> listening yet, and then gdb trying IPv4 and that succeeding.  In that rare
> scenario, if gdb had started looping just a fraction of a second earlier, it
> would have connected with IPv6 successfully.  But, so what?  It will have connected
> successfully anyway, and IPv6 vs IPv4 will hardly make a real difference.
> Users that really really really want to ensure to get IPv6 or IPv4 should use
> the "tcp6:" or "tcp4:" prefixes.  So I'm not seeing any downside the whole loop
> approach.

OK, I don't really mind enough to argue.  I'll implement it this way.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v2] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
                   ` (2 preceding siblings ...)
  2018-06-06 12:26 ` Pedro Alves
@ 2018-06-15  0:25 ` Sergio Durigan Junior
  2018-06-15  7:12   ` Eli Zaretskii
  2018-06-20 15:24   ` Pedro Alves
  2018-07-07 20:47 ` [PATCH v3] " Sergio Durigan Junior
  2018-07-11 19:16 ` [PATCH v4] " Sergio Durigan Junior
  5 siblings, 2 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-15  0:25 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes, Sergio Durigan Junior

Changes from v1:

- s/hostnames/addresses/ on NEWS.

- Simplify functions on netstuff.c.  Add new defines for
  GDB_NI_MAX_ADDR and GDB_NI_MAX_PORT.  Make
  parse_hostname_without_prefix return a struct parsed_hostname.

- Use AF_UNSPEC instead of AF_INET by default on unprefixed
  connections.

- Simplify and modernize things on gdbreplay.c.

- Implement new GDB_TEST_SOCKETHOST mechanism for testing things with
  any type of hostname/address.

- Simplify things on boards/*.exp because of the above.

- Rewrite net_open to support multiple sockets/connections with
  timeout/retry.

- Improve IPv6 example on documentation.


This patch implements IPv6 support for both GDB and gdbserver.  Based
on my research, it is the fourth attempt to do that since 2006.  Since
I used ideas from all of the previous patches, I also added their
authors's names on the ChangeLogs as a way to recognize their
efforts.  For reference sake, you can find the previous attempts at:

  https://sourceware.org/ml/gdb-patches/2006-09/msg00192.html

  https://sourceware.org/ml/gdb-patches/2014-02/msg00248.html

  https://sourceware.org/ml/gdb-patches/2016-02/msg00226.html

The basic idea behind the patch is to start using the new
'getaddrinfo'/'getnameinfo' calls, which are responsible for
translating names and addresses in a protocol-independent way.  This
means that if we ever have a new version of the IP protocol, we won't
need to change the code again.

The function 'getaddrinfo' returns a linked list of possible addresses
to connect to.  Dealing with multiple addresses proved to be a hard
task with the current TCP auto-retry mechanism implemented on
ser-tcp:net_open.  For example, when gdbserver listened only on an
IPv4 socket:

  $ ./gdbserver --once 127.0.0.1:1234 ./a.out

and GDB was instructed to try to connect to both IPv6 and IPv4
sockets:

  $ ./gdb -ex 'target extended-remote localhost:1234' ./a.out

the user would notice a somewhat big delay before GDB was able to
connect to the IPv4 socket.  This happened because GDB was trying to
connect to the IPv6 socket first, and had to wait until the connection
timed out before it tried to connect to the IPv4 socket.

For that reason, I had to rewrite the main loop and implement a new
method for handling multiple connections.  The main idea is:

  1) We open sockets and perform 'connect's for all of the possible
  addresses returned by 'getaddrinfo'.

  2) If we have a successful 'connect', we just use that connection.

  3) If we don't have a successfull 'connect', but if we've got a
  EINPROGRESS (meaning that the connection is in progress), we add the
  socket to a poll of sockets that are going to be handled later.

  4) Once we finish iterating over the possible addresses, we perform
  a 'select' on the poll of sockets that did not connect
  successfully.  We do that until we have a timeout/interrupt (in
  which case we just bail out), or until 'select' gives us a set of
  sockets that are ready to be handled.

  5) We take the set of sockets returned by 'select' and check which
  one doesn't have an error associated with it.  If we find one, it
  means a connection has succeeded and we just use it.

  6) If we don't find any successful connections, we wait for a while,
  check to see if there was a timeout/interruption (in which case we
  bail out), and then go back to (1).

After multiple tests, I was able to connect without delay on the
scenario described above, and was also able to connect in all other
types of scenarios.

I also implemented some hostname parsing functions which are used to
help GDB and gdbserver to parse hostname strings provided by the user.
These new functions are living inside common/netstuff.[ch].  I've had
to do that since IPv6 introduces a new URL scheme, which defines that
square brackets can be used to enclose the host part and differentiate
it from the port (e.g., "[::1]:1234" means "host ::1, port 1234").  I
spent some time thinking about a reasonable way to interpret what the
user wants, and I came up with the following:

  - If the user has provided a prefix that doesn't specify the protocol
    version (i.e., "tcp:" or "udp:"), or if the user has not provided
    any prefix, don't make any assumptions (i.e., assume AF_UNSPEC when
    dealing with 'getaddrinfo') *unless* the host starts with "[" (in
    which case, assume it's an IPv6 host).

  - If the user has provided a prefix that does specify the protocol
    version (i.e., "tcp4:", "tcp6:", "udp4:" or "udp6:"), then respect
    that.

This method doesn't follow strictly what RFC 2732 proposes (that
literal IPv6 addresses should be provided enclosed in "[" and "]")
because IPv6 addresses still can be provided without square brackets
in our case, but since we have prefixes to specify protocol versions I
think this is not an issue.

Another thing worth mentioning is the new 'GDB_TEST_SOCKETHOST'
testcase parameter, which makes it possible to specify the
hostname (without the port) to be used when testing GDB and
gdbserver.  For example, to run IPv6 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'

Or, to run IPv4 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp4:127.0.0.1'

This required a few changes on the gdbserver-base.exp, and also a
minimal adjustment on gdb.server/run-without-local-binary.exp.

This patch has been regression-tested on BuildBot and locally, and
also built using a x86_64-w64-mingw32 GCC, and no problems were found.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (COMMON_SFILES): Add 'common/netstuff.c'.
	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
	* NEWS (Changes since GDB 8.1): Mention IPv6 support.
	* common/netstuff.c: New file.
	* common/netstuff.h: New file.
	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
	(gdb_socket): New class.
	(wait_for_connect): New parameters 'socket_poll' and
	'sockets_ready'.  Perform 'select' on sockets from
	'socket_poll'.
	(socket_error_p): New function.
	(gdb_connect): New function, with code from 'net_open'.
	(net_open): Rewrite main loop to deal with multiple
	sockets/addresses.  Handle IPv6-style hostnames; implement
	support for IPv6 connections.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
	(OBS): Add 'common/netstuff.o'.
	(GDBREPLAY_OBS): Likewise.
	* gdbreplay.c: Include 'wspiapi.h' and 'netstuff.h'.
	(remote_open): Implement support for IPv6
	connections.
	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
	and 'wspiapi.h'.
	(handle_accept_event): Accept connections from IPv6 sources.
	(remote_prepare): Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	(remote_open): Implement support for printing connections from
	IPv6 sources.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* README (Testsuite Parameters): Mention new 'GDB_TEST_SOCKETHOST'
	parameter.
	* boards/native-extended-gdbserver.exp: Do not set 'sockethost'
	by default.
	* boards/native-gdbserver.exp: Likewise.
	* gdb.server/run-without-local-binary.exp: Improve regexp used
	for detecting when a remote debugging connection succeeds.
	* lib/gdbserver-support.exp (gdbserver_default_get_comm_port):
	Do not prefix the port number with ":".
	(gdbserver_start): New global GDB_TEST_SOCKETHOST.  Implement
	support for detecting and using it.  Add '$debughost_gdbserver'
	to the list of arguments used to start gdbserver.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* gdb.texinfo (Remote Connection Commands): Add explanation
	about new IPv6 support.  Add new connection prefixes.
---
 gdb/Makefile.in                                    |   2 +
 gdb/NEWS                                           |   4 +
 gdb/common/netstuff.c                              | 126 ++++++
 gdb/common/netstuff.h                              |  76 ++++
 gdb/doc/gdb.texinfo                                |  49 ++-
 gdb/gdbserver/Makefile.in                          |   3 +
 gdb/gdbserver/gdbreplay.c                          | 129 ++++--
 gdb/gdbserver/remote-utils.c                       | 118 ++++--
 gdb/ser-tcp.c                                      | 446 ++++++++++++++-------
 gdb/testsuite/README                               |  14 +
 gdb/testsuite/boards/native-extended-gdbserver.exp |   2 -
 gdb/testsuite/boards/native-gdbserver.exp          |   1 -
 .../gdb.server/run-without-local-binary.exp        |   2 +-
 gdb/testsuite/lib/gdbserver-support.exp            |  25 +-
 14 files changed, 784 insertions(+), 213 deletions(-)
 create mode 100644 gdb/common/netstuff.c
 create mode 100644 gdb/common/netstuff.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 354a6361b7..81042c1988 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -963,6 +963,7 @@ COMMON_SFILES = \
 	common/job-control.c \
 	common/gdb_tilde_expand.c \
 	common/gdb_vecs.c \
+	common/netstuff.c \
 	common/new-op.c \
 	common/pathstuff.c \
 	common/print-utils.c \
@@ -1444,6 +1445,7 @@ HFILES_NO_SRCDIR = \
 	common/gdb_vecs.h \
 	common/gdb_wait.h \
 	common/common-inferior.h \
+	common/netstuff.h \
 	common/host-defs.h \
 	common/pathstuff.h \
 	common/print-utils.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index 13da2f1d4e..9d4aecc01a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 8.1
 
+* GDB and GDBserver now support IPv6 connections.  IPv6 addresses
+  can be passed using the '[ADDRESS]:PORT' notation, or the regular
+  'ADDRESS:PORT' method.
+
 * The endianness used with the 'set endian auto' mode in the absence of
   an executable selected for debugging is now the last endianness chosen
   either by one of the 'set endian big' and 'set endian little' commands
diff --git a/gdb/common/netstuff.c b/gdb/common/netstuff.c
new file mode 100644
index 0000000000..0f392ea599
--- /dev/null
+++ b/gdb/common/netstuff.c
@@ -0,0 +1,126 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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"
+
+#ifdef USE_WIN32API
+#include <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+/* See common/netstuff.h.  */
+
+scoped_free_addrinfo::~scoped_free_addrinfo ()
+{
+  freeaddrinfo (m_res);
+}
+
+/* See common/netstuff.h.  */
+
+parsed_hostname
+parse_hostname_without_prefix (std::string hostname, struct addrinfo *hint)
+{
+  parsed_hostname ret;
+
+  if (hint->ai_family != AF_INET && hostname[0] == '[')
+    {
+      /* IPv6 addresses can be written as '[ADDR]:PORT', and we
+	 support this notation.  */
+      size_t close_bracket_pos = hostname.find_first_of (']');
+
+      if (close_bracket_pos == std::string::npos)
+	error (_("Missing close bracket in hostname '%s'"),
+	       hostname.c_str ());
+
+      /* Erase both '[' and ']'.  */
+      hostname.erase (0, 1);
+      hostname.erase (close_bracket_pos - 1, 1);
+
+      hint->ai_family = AF_INET6;
+    }
+
+  /* The length of the hostname part.  */
+  size_t host_len;
+  size_t last_colon_pos = hostname.find_last_of (':');
+
+  if (last_colon_pos != std::string::npos)
+    {
+      /* The user has provided a port.  */
+      host_len = last_colon_pos;
+      ret.port_str = hostname.substr (last_colon_pos + 1);
+    }
+  else
+    host_len = hostname.size ();
+
+  ret.host_str = hostname.substr (0, host_len);
+
+  /* Default hostname is localhost.  */
+  if (ret.host_str.empty ())
+    ret.host_str = "localhost";
+
+  return ret;
+}
+
+/* See common/netstuff.h.  */
+
+parsed_hostname
+parse_hostname (const char *hostname, 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 struct host_prefix prefix : prefixes)
+    if (startswith (hostname, prefix.prefix))
+      {
+	hostname += 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_hostname_without_prefix (hostname, hint);
+}
diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
new file mode 100644
index 0000000000..687ff532b8
--- /dev/null
+++ b/gdb/common/netstuff.h
@@ -0,0 +1,76 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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/>.  */
+
+#ifndef NETSTUFF_H
+#define NETSTUFF_H
+
+#include <string>
+#include "common/gdb_string_view.h"
+
+/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
+#define GDB_NI_MAX_ADDR 64
+#define GDB_NI_MAX_PORT 16
+
+/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
+
+class scoped_free_addrinfo
+{
+public:
+  /* Default constructor.  */
+  scoped_free_addrinfo (struct addrinfo *ainfo)
+    : m_res (ainfo)
+  {
+  }
+
+  /* Destructor responsible for free'ing M_RES by calling
+     'freeaddrinfo'.  */
+  ~scoped_free_addrinfo ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
+
+private:
+  /* The addrinfo resource.  */
+  struct addrinfo *m_res;
+};
+
+/* The struct we return after parsing the hostname.  */
+
+struct parsed_hostname
+{
+  /* The hostname.  */
+  std::string host_str;
+
+  /* The port, if any.  */
+  std::string port_str;
+};
+
+
+/* Parse HOSTNAME (which is a string in the form of "ADDR:PORT") and
+   return a 'parsed_hostname' structure with the proper fields filled
+   in.  Also adjust HINT accordingly.  */
+extern parsed_hostname parse_hostname_without_prefix (std::string hostname,
+						      struct addrinfo *hint);
+
+/* Parse HOSTNAME (which is a string in the form of
+   "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_hostname'
+   structure with the proper fields filled in.  Also adjust HINT
+   accordingly.  */
+extern parsed_hostname parse_hostname (const char *hostname,
+				       struct addrinfo *hint);
+
+#endif /* ! NETSTUFF_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a6bad13d9d..55b48309a8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20509,16 +20509,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
 @code{target} command.
 
 @item target remote @code{@var{host}:@var{port}}
+@itemx target remote @code{@var{[host]}:@var{port}}
 @itemx target remote @code{tcp:@var{host}:@var{port}}
+@itemx target remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target remote @code{tcp4:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{[host]}:@var{port}}
 @itemx target extended-remote @code{@var{host}:@var{port}}
+@itemx target extended-remote @code{@var{[host]}:@var{port}}
 @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{[host]}:@var{port}}
 @cindex @acronym{TCP} port, @code{target remote}
 Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
-The @var{host} may be either a host name or a numeric @acronym{IP}
-address; @var{port} must be a decimal number.  The @var{host} could be
-the target machine itself, if it is directly connected to the net, or
-it might be a terminal server which in turn has a serial line to the
-target.
+The @var{host} may be either a host name, a numeric @acronym{IPv4}
+address, or a numeric @acronym{IPv6} address (with or without the
+square brackets to separate the address from the port); @var{port}
+must be a decimal number.  The @var{host} could be the target machine
+itself, if it is directly connected to the net, or it might be a
+terminal server which in turn has a serial line to the target.
 
 For example, to connect to port 2828 on a terminal server named
 @code{manyfarms}:
@@ -20527,6 +20538,26 @@ For example, to connect to port 2828 on a terminal server named
 target remote manyfarms:2828
 @end smallexample
 
+To connect to port 2828 on a terminal server whose address is
+@code{2001:0db8:85a3:0000:0000:8a2e:0370:7334}, you can either use the
+square bracket syntax:
+
+@smallexample
+target remote [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2828
+@end smallexample
+
+@noindent
+or explicitly specify the @acronym{IPv6} protocol:
+
+@smallexample
+target remote tcp6:2001:0db8:85a3:0000:0000:8a2e:0370:7334:2828
+@end smallexample
+
+This last example may be confusing to the reader, because there is no
+visible separation between the hostname and the port number.
+Therefore, we recommend the user to provide @acronym{IPv6} addresses
+using square brackets for clarity.
+
 If your remote target is actually running on the same machine as your
 debugger session (e.g.@: a simulator for your target running on the
 same host), you can omit the hostname.  For example, to connect to
@@ -20540,7 +20571,15 @@ target remote :1234
 Note that the colon is still required here.
 
 @item target remote @code{udp:@var{host}:@var{port}}
+@itemx target remote @code{udp:@var{[host]}:@var{port}}
+@itemx target remote @code{udp4:@var{host}:@var{port}}
+@itemx target remote @code{udp6:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp:@var{host}:@var{port}}
 @itemx target extended-remote @code{udp:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{[host]}:@var{port}}
 @cindex @acronym{UDP} port, @code{target remote}
 Debug using @acronym{UDP} packets to @var{port} on @var{host}.  For example, to
 connect to @acronym{UDP} port 2828 on a terminal server named @code{manyfarms}:
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index cf04b7d7b5..2ae5e6ec00 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -211,6 +211,7 @@ SFILES = \
 	$(srcdir)/common/job-control.c \
 	$(srcdir)/common/gdb_tilde_expand.c \
 	$(srcdir)/common/gdb_vecs.c \
+	$(srcdir)/common/netstuff.c \
 	$(srcdir)/common/new-op.c \
 	$(srcdir)/common/pathstuff.c \
 	$(srcdir)/common/print-utils.c \
@@ -253,6 +254,7 @@ OBS = \
 	common/format.o \
 	common/gdb_tilde_expand.o \
 	common/gdb_vecs.o \
+	common/netstuff.o \
 	common/new-op.o \
 	common/pathstuff.o \
 	common/print-utils.o \
@@ -289,6 +291,7 @@ GDBREPLAY_OBS = \
 	common/common-exceptions.o \
 	common/common-utils.o \
 	common/errors.o \
+	common/netstuff.o \
 	common/print-utils.o \
 	gdbreplay.o \
 	utils.o \
diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
index a4bc892462..8afd46b31a 100644
--- a/gdb/gdbserver/gdbreplay.c
+++ b/gdb/gdbserver/gdbreplay.c
@@ -46,8 +46,11 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
+#include "netstuff.h"
+
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
@@ -142,56 +145,108 @@ remote_close (void)
 static void
 remote_open (char *name)
 {
-  if (!strchr (name, ':'))
+  char *last_colon = strrchr (name, ':');
+
+  if (last_colon == NULL)
     {
       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
       fflush (stderr);
       exit (1);
     }
-  else
-    {
+
 #ifdef USE_WIN32API
-      static int winsock_initialized;
+  static int winsock_initialized;
 #endif
-      char *port_str;
-      int port;
-      struct sockaddr_in sockaddr;
-      socklen_t tmp;
-      int tmp_desc;
+  char *port_str;
+  int tmp;
+  int tmp_desc;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
 
-      port_str = strchr (name, ':');
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
 
-      port = atoi (port_str + 1);
+  parsed_hostname parsed = parse_hostname (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
 
 #ifdef USE_WIN32API
-      if (!winsock_initialized)
-	{
-	  WSADATA wsad;
+  if (!winsock_initialized)
+    {
+      WSADATA wsad;
 
-	  WSAStartup (MAKEWORD (1, 0), &wsad);
-	  winsock_initialized = 1;
-	}
+      WSAStartup (MAKEWORD (1, 0), &wsad);
+      winsock_initialized = 1;
+    }
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
-      if (tmp_desc == -1)
-	perror_with_name ("Can't open socket");
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
 
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-		  sizeof (tmp));
+  if (r != 0)
+    {
+      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+	       parsed.host_str.c_str (), parsed.port_str.c_str (),
+	       gai_strerror (r));
+      fflush (stderr);
+      exit (1);
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  struct addrinfo *p;
+
+  for (p = ainfo; p != NULL; p = p->ai_next)
+    {
+      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
 
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      sockaddr.sin_addr.s_addr = INADDR_ANY;
+      if (tmp_desc >= 0)
+	break;
+    }
+
+  if (p == NULL)
+    perror_with_name ("Cannot open socket");
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-	  || listen (tmp_desc, 1))
-	perror_with_name ("Can't bind address");
+  /* Allow rapid reuse of this port. */
+  tmp = 1;
+  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+	      sizeof (tmp));
+
+  switch (p->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+      exit (1);
+    }
+
+  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+    perror_with_name ("Can't bind address");
+
+  if (p->ai_socktype == SOCK_DGRAM)
+    remote_desc = tmp_desc;
+  else
+    {
+      struct sockaddr_storage sockaddr;
+      socklen_t sockaddrsize = sizeof (sockaddr);
+      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+      if (listen (tmp_desc, 1) != 0)
+	perror_with_name ("Can't listen on socket");
+
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+			    &sockaddrsize);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
       if (remote_desc == -1)
 	perror_with_name ("Accept failed");
 
@@ -206,6 +261,16 @@ remote_open (char *name)
       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
 		  (char *) &tmp, sizeof (tmp));
 
+      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+	{
+	  fprintf (stderr, "Remote debugging from host %s, port %s\n",
+		   orig_host, orig_port);
+	  fflush (stderr);
+	}
+
 #ifndef USE_WIN32API
       close (tmp_desc);		/* No longer need this */
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 967b2f0ebb..0388c67fba 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -26,6 +26,8 @@
 #include "dll.h"
 #include "rsp-low.h"
 #include "gdbthread.h"
+#include "netstuff.h"
+#include "filestuff.h"
 #include <ctype.h>
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -63,6 +65,7 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
 #if __QNX__
@@ -151,19 +154,18 @@ enable_async_notification (int fd)
 static int
 handle_accept_event (int err, gdb_client_data client_data)
 {
-  struct sockaddr_in sockaddr;
-  socklen_t tmp;
+  struct sockaddr_storage sockaddr;
+  socklen_t len = sizeof (sockaddr);
 
   if (debug_threads)
     debug_printf ("handling possible accept event\n");
 
-  tmp = sizeof (sockaddr);
-  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
+  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
   if (remote_desc == -1)
     perror_with_name ("Accept failed");
 
   /* Enable TCP keep alive process. */
-  tmp = 1;
+  socklen_t tmp = 1;
   setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
 	      (char *) &tmp, sizeof (tmp));
 
@@ -192,8 +194,19 @@ handle_accept_event (int err, gdb_client_data client_data)
   delete_file_handler (listen_desc);
 
   /* Convert IP address to string.  */
-  fprintf (stderr, "Remote debugging from host %s\n",
-	   inet_ntoa (sockaddr.sin_addr));
+  char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV);
+
+  if (r != 0)
+    fprintf (stderr, _("Could not obtain remote address: %s\n"),
+	     gai_strerror (r));
+  else
+    fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
+	     orig_host, orig_port);
 
   enable_async_notification (remote_desc);
 
@@ -222,10 +235,7 @@ remote_prepare (const char *name)
 #ifdef USE_WIN32API
   static int winsock_initialized;
 #endif
-  int port;
-  struct sockaddr_in sockaddr;
   socklen_t tmp;
-  char *port_end;
 
   remote_is_stdio = 0;
   if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
@@ -238,17 +248,24 @@ remote_prepare (const char *name)
       return;
     }
 
-  port_str = strchr (name, ':');
-  if (port_str == NULL)
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_hostname parsed = parse_hostname_without_prefix (name, &hint);
+
+  if (parsed.port_str.empty ())
     {
       cs.transport_is_reliable = 0;
       return;
     }
 
-  port = strtoul (port_str + 1, &port_end, 10);
-  if (port_str[1] == '\0' || *port_end != '\0')
-    error ("Bad port argument: %s", name);
-
 #ifdef USE_WIN32API
   if (!winsock_initialized)
     {
@@ -259,8 +276,26 @@ remote_prepare (const char *name)
     }
 #endif
 
-  listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (listen_desc == -1)
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
+
+  if (r != 0)
+    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+
+  scoped_free_addrinfo freeaddrinfo (ainfo);
+
+  struct addrinfo *iter;
+
+  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+    {
+      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+					iter->ai_protocol);
+
+      if (listen_desc >= 0)
+	break;
+    }
+
+  if (iter == NULL)
     perror_with_name ("Can't open socket");
 
   /* Allow rapid reuse of this port. */
@@ -268,14 +303,25 @@ remote_prepare (const char *name)
   setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
 	      sizeof (tmp));
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  sockaddr.sin_addr.s_addr = INADDR_ANY;
+  switch (iter->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
+    }
 
-  if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-      || listen (listen_desc, 1))
+  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
     perror_with_name ("Can't bind address");
 
+  if (listen (listen_desc, 1) != 0)
+    perror_with_name ("Can't listen on socket");
+
   cs.transport_is_reliable = 1;
 }
 
@@ -350,18 +396,24 @@ remote_open (const char *name)
 #endif /* USE_WIN32API */
   else
     {
-      int port;
-      socklen_t len;
-      struct sockaddr_in sockaddr;
-
-      len = sizeof (sockaddr);
-      if (getsockname (listen_desc,
-		       (struct sockaddr *) &sockaddr, &len) < 0
-	  || len < sizeof (sockaddr))
+      char listen_port[16];
+      struct sockaddr_storage sockaddr;
+      socklen_t len = sizeof (sockaddr);
+
+      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
 	perror_with_name ("Can't determine port");
-      port = ntohs (sockaddr.sin_port);
 
-      fprintf (stderr, "Listening on port %d\n", port);
+      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+			   NULL, 0,
+			   listen_port, sizeof (listen_port),
+			   NI_NUMERICSERV);
+
+      if (r != 0)
+	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
+		 gai_strerror (r));
+      else
+	fprintf (stderr, _("Listening on port %s\n"), listen_port);
+
       fflush (stderr);
 
       /* Register the event loop handler.  */
diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index 23ef3b04b8..44b6d89cda 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -25,6 +25,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
 #include "filestuff.h"
+#include "netstuff.h"
 
 #include <sys/types.h>
 
@@ -39,6 +40,7 @@
 
 #ifdef USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #ifndef ETIMEDOUT
 #define ETIMEDOUT WSAETIMEDOUT
 #endif
@@ -81,12 +83,69 @@ static unsigned int tcp_retry_limit = 15;
 
 #define POLL_INTERVAL 5
 
-/* Helper function to wait a while.  If SCB is non-null, wait on its
-   file descriptor.  Otherwise just wait on a timeout, updating *POLLS.
-   Returns -1 on timeout or interrupt, otherwise the value of select.  */
+/* An abstraction of a socket, useful when we want to close the socket
+   fd automatically when exiting a context.  */
+
+class gdb_socket
+{
+public:
+  /* Default constructor.  */
+  gdb_socket (int sock, const struct addrinfo *ainfo)
+    : m_socket (sock),
+      m_released (false),
+      m_ainfo (ainfo)
+  {
+  }
+
+  /* Release a socket, i.e., make sure it doesn't get closed when our
+     destructor is called.  */
+  void release ()
+  {
+    m_released = true;
+  }
+
+  /* Return the socket associated with this object.  */
+  int get_socket () const
+  {
+    return m_socket;
+  }
+
+  /* Return the addrinfo structure associated with this object.  */
+  const struct addrinfo *get_addrinfo () const
+  {
+    return m_ainfo;
+  }
+
+  /* Destructor.  Make sure we close the socket if it hasn't been
+     released.  */
+  ~gdb_socket ()
+  {
+    if (!m_released)
+      close (m_socket);
+  }
+
+private:
+  /* The socket.  */
+  int m_socket;
+
+  /* Whether the socket has been released or not.  If it has, then we
+     don't close it when our destructor is called.  */
+  bool m_released;
+
+  /* The addrinfo structure associated with the socket.  */
+  const struct addrinfo *m_ainfo;
+};
+
+/* Helper function to wait a while.  If SOCKET_POLL is non-null, wait
+   on its file descriptors.  Otherwise just wait on a timeout, updating
+   *POLLS.  If SOCKET_POLL and SOCKETS_READY are both non-NULL, update
+   SOCKETS_READY with the value of the 'write' fd_set upon successful
+   completion of the 'select' call.  Return -1 on timeout or
+   interrupt, otherwise return the value of the 'select' call.  */
 
 static int
-wait_for_connect (struct serial *scb, unsigned int *polls)
+wait_for_connect (const std::vector<std::unique_ptr<gdb_socket>> *socket_poll,
+		  unsigned int *polls, fd_set *sockets_ready)
 {
   struct timeval t;
   int n;
@@ -120,24 +179,39 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
       t.tv_usec = 0;
     }
 
-  if (scb)
+  if (socket_poll != NULL)
     {
-      fd_set rset, wset, eset;
+      fd_set wset, eset;
+      int maxfd = 0;
+
+      FD_ZERO (&wset);
+      FD_ZERO (&eset);
+      for (const std::unique_ptr<gdb_socket> &ptr : *socket_poll)
+	{
+	  const gdb_socket *sock = ptr.get ();
+	  int s = sock->get_socket ();
+
+	  FD_SET (s, &wset);
+	  FD_SET (s, &eset);
+	  maxfd = std::max (maxfd, s);
+	}
 
-      FD_ZERO (&rset);
-      FD_SET (scb->fd, &rset);
-      wset = rset;
-      eset = rset;
-	  
       /* POSIX systems return connection success or failure by signalling
 	 wset.  Windows systems return success in wset and failure in
 	 eset.
-     
+
 	 We must call select here, rather than gdb_select, because
 	 the serial structure has not yet been initialized - the
 	 MinGW select wrapper will not know that this FD refers
 	 to a socket.  */
-      n = select (scb->fd + 1, &rset, &wset, &eset, &t);
+      n = select (maxfd + 1, NULL, &wset, &eset, &t);
+
+      if (n > 0 && sockets_ready != NULL)
+	{
+	  /* We're just interested in possible successes, so we just
+	     copy wset here.  */
+	  *sockets_ready = wset;
+	}
     }
   else
     /* Use gdb_select here, since we have no file descriptors, and on
@@ -153,171 +227,271 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
   return n;
 }
 
-/* Open a tcp socket.  */
+/* Return TRUE if there is an error associated with socket SOCK, or
+   FALSE otherwise.  If there's an error, set ERRNO accordingly.  */
 
-int
-net_open (struct serial *scb, const char *name)
+static bool
+socket_error_p (int sock)
 {
-  char hostname[100];
-  const char *port_str;
-  int n, port, tmp;
-  int use_udp;
-  struct hostent *hostent;
-  struct sockaddr_in sockaddr;
-#ifdef USE_WIN32API
-  u_long ioarg;
-#else
-  int ioarg;
-#endif
-  unsigned int polls = 0;
+  int res, err;
+  socklen_t len = sizeof (err);
 
-  use_udp = 0;
-  if (startswith (name, "udp:"))
-    {
-      use_udp = 1;
-      name = name + 4;
-    }
-  else if (startswith (name, "tcp:"))
-    name = name + 4;
+  /* On Windows, the fourth parameter to getsockopt is a "char *";
+     on UNIX systems it is generally "void *".  The cast to "char *"
+     is OK everywhere, since in C++ any data pointer type can be
+     implicitly converted to "void *".  */
+  res = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
 
-  port_str = strchr (name, ':');
+  if (err != 0)
+    errno = err;
 
-  if (!port_str)
-    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
-						       happen.  */
+  return (res < 0 || err != 0) ? true : false;
+}
 
-  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
-  strncpy (hostname, name, tmp);	/* Don't want colon.  */
-  hostname[tmp] = '\000';	/* Tie off host name.  */
-  port = atoi (port_str + 1);
+/* Helper structure containing a pair of socket and addrinfo.  */
 
-  /* Default hostname is localhost.  */
-  if (!hostname[0])
-    strcpy (hostname, "localhost");
+struct gdb_connect_info
+{
+  int socket;
 
-  hostent = gethostbyname (hostname);
-  if (!hostent)
-    {
-      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
-      errno = ENOENT;
-      return -1;
-    }
+  const struct addrinfo *ainfo;
+};
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
-	  sizeof (struct in_addr));
+/* Iterate over the entries of AINFO and try to open a socket and
+   perform a 'connect' on each one.  If there's a success, return the
+   socket and the associated 'struct addrinfo' that succeeded.
+   Otherwise, return a special instance of gdb_connect_info whose
+   SOCKET is -1 and AINFO is NULL.
+
+   Sockets that are opened are marked as non-blocking.  When a socket
+   fails to connect (i.e., when 'connect' returns -1 and ERRNO is set
+   to EINPROGRESS), we add this socket (along with its associated
+   'struct addrinfo') into SOCKET_POLL.  The caller can then use
+   SOCKET_POLL to perform a 'select' on the sockets and check if any
+   of them succeeded.  */
+
+static gdb_connect_info
+gdb_connect (const struct addrinfo *ainfo,
+	     std::vector<std::unique_ptr<gdb_socket>> &socket_poll)
+{
+  gdb_connect_info ret;
+#ifdef USE_WIN32API
+  u_long ioarg;
+#else
+  int ioarg;
+#endif
 
- retry:
+  ret.socket = -1;
+  ret.ainfo = NULL;
 
-  if (use_udp)
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
-  else
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
+  for (const struct addrinfo *cur_ainfo = ainfo;
+       cur_ainfo != NULL;
+       cur_ainfo = cur_ainfo->ai_next)
+    {
+      int sock = gdb_socket_cloexec (cur_ainfo->ai_family,
+				     cur_ainfo->ai_socktype,
+				     cur_ainfo->ai_protocol);
 
-  if (scb->fd == -1)
-    return -1;
-  
-  /* Set socket nonblocking.  */
-  ioarg = 1;
-  ioctl (scb->fd, FIONBIO, &ioarg);
+      if (sock < 0)
+	continue;
 
-  /* Use Non-blocking connect.  connect() will return 0 if connected
-     already.  */
-  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
+      /* Set socket nonblocking.  */
+      ioarg = 1;
+      ioctl (sock, FIONBIO, &ioarg);
 
-  if (n < 0)
-    {
+      /* Use Non-blocking connect.  connect() will return 0 if
+	 connected already.  */
+      if (connect (sock, cur_ainfo->ai_addr, cur_ainfo->ai_addrlen) == 0)
+	{
+	  if (!socket_error_p (sock))
+	    {
+	      /* Connection succeeded, we can stop trying.  */
+	      ret.socket = sock;
+	      ret.ainfo = cur_ainfo;
+	      break;
+	    }
+	  else
+	    {
+	      /* There was an error with the socket.  Just try the
+		 next one.  */
+	      close (sock);
+	      continue;
+	    }
+	}
+      else
+	{
 #ifdef USE_WIN32API
-      int err = WSAGetLastError();
+	  int err = WSAGetLastError();
 #else
-      int err = errno;
+	  int err = errno;
 #endif
 
-      /* Maybe we're waiting for the remote target to become ready to
-	 accept connections.  */
-      if (tcp_auto_retry
+	  if (tcp_auto_retry
 #ifdef USE_WIN32API
-	  && err == WSAECONNREFUSED
+	      /* Under Windows, calling "connect" with a
+		 non-blocking socket results in WSAEWOULDBLOCK,
+		 not WSAEINPROGRESS.  */
+	      && err == WSAEWOULDBLOCK
 #else
-	  && err == ECONNREFUSED
+	      && err == EINPROGRESS
 #endif
-	  && wait_for_connect (NULL, &polls) >= 0)
-	{
-	  close (scb->fd);
-	  goto retry;
+	      )
+	    {
+	      /* If we have an "INPROGRESS" error, add the socket
+		 to the poll of sockets we have to perform a
+		 'select' on.  */
+	      socket_poll.push_back
+		(std::unique_ptr<gdb_socket>
+		 (new gdb_socket (sock, cur_ainfo)));
+	    }
 	}
+    }
+  return ret;
+}
 
-      if (
+/* Open a tcp socket.  */
+
+int
+net_open (struct serial *scb, const char *name)
+{
+  bool use_udp;
 #ifdef USE_WIN32API
-	  /* Under Windows, calling "connect" with a non-blocking socket
-	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
-	  err != WSAEWOULDBLOCK
+  u_long ioarg;
 #else
-	  err != EINPROGRESS
+  int ioarg;
 #endif
-	  )
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_hostname parsed = parse_hostname (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
+
+  int r = getaddrinfo (parsed.host_str.c_str (),
+		       parsed.port_str.c_str (), &hint, &ainfo);
+
+  if (r != 0)
+    {
+      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
+			  name, gai_strerror (r));
+      errno = ENOENT;
+      return -1;
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  const struct addrinfo *cur_ainfo;
+  bool got_connection = false;
+  unsigned int polls = 0;
+
+  /* Assume the worst.  */
+  scb->fd = -1;
+
+  while (!got_connection)
+    {
+      /* A poll of sockets.  This poll will store the sockets that
+	 "error'd" with EINPROGRESS, meaning that we will perform a
+	 'select' on them.  */
+      std::vector<std::unique_ptr<gdb_socket>> socket_poll;
+      /* Try to connect.  This function will return a pair of socket
+	 and addrinfo.  If the connection succeeded, the socket will
+	 have a positive value and the addrinfo will not be NULL.  */
+      gdb_connect_info gci = gdb_connect (ainfo, socket_poll);
+
+      if (gci.socket > -1)
 	{
-	  errno = err;
-	  net_close (scb);
-	  return -1;
+	  /* It seems we've got a successful connection in our loop,
+	     so let's just stop.  */
+	  gdb_assert (gci.ainfo != NULL);
+	  scb->fd = gci.socket;
+	  cur_ainfo = gci.ainfo;
+	  got_connection = true;
+	  break;
 	}
 
-      /* Looks like we need to wait for the connect.  */
-      do 
+      if (!socket_poll.empty ())
 	{
-	  n = wait_for_connect (scb, &polls);
-	} 
-      while (n == 0);
-      if (n < 0)
+	  /* Perform 'select' on the poll of sockets.  */
+	  fd_set sockets_ready;
+	  int n;
+
+	  /* Wait until some of the sockets are ready (or until we
+	     have a timeout/interruption, whatever comes first).  */
+	  do
+	    {
+	      n = wait_for_connect (&socket_poll, &polls, &sockets_ready);
+	    }
+	  while (n == 0);
+
+	  if (n < 0)
+	    {
+	      /* We probably got a timeout/interruption, or maybe
+		 'select' error'd out on us.  Either way, we should
+		 bail out.  */
+	      break;
+	    }
+
+	  for (const std::unique_ptr<gdb_socket> &ptr : socket_poll)
+	    {
+	      /* Here we iterate over our list of sockets.  For each
+		 one, we check if it's marked as "ready" (by
+		 'select'), and if there's no error associated with
+		 it.  If everything is OK, it means we've got a
+		 successful connection.  */
+	      gdb_socket *sock = ptr.get ();
+	      int s = sock->get_socket ();
+
+	      if (FD_ISSET (s, &sockets_ready) && !socket_error_p (s))
+		{
+		  /* We got a connection.  */
+		  scb->fd = s;
+		  cur_ainfo = sock->get_addrinfo ();
+		  /* Release it so that it doesn't get closed when the
+		     destructor is called.  */
+		  sock->release ();
+		  got_connection = true;
+		  break;
+		}
+	    }
+	}
+
+      if (got_connection)
 	{
-	  net_close (scb);
-	  return -1;
+	  /* Maybe we've been able to establish a connection.  If so,
+	     just break.  */
+	  break;
 	}
+
+      /* Let's wait a bit.  */
+      if (wait_for_connect (NULL, &polls, NULL) < 0)
+	break;
     }
 
-  /* Got something.  Is it an error?  */
-  {
-    int res, err;
-    socklen_t len;
-
-    len = sizeof (err);
-    /* On Windows, the fourth parameter to getsockopt is a "char *";
-       on UNIX systems it is generally "void *".  The cast to "char *"
-       is OK everywhere, since in C++ any data pointer type can be
-       implicitly converted to "void *".  */
-    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
-    if (res < 0 || err)
-      {
-	/* Maybe the target still isn't ready to accept the connection.  */
-	if (tcp_auto_retry
-#ifdef USE_WIN32API
-	    && err == WSAECONNREFUSED
-#else
-	    && err == ECONNREFUSED
-#endif
-	    && wait_for_connect (NULL, &polls) >= 0)
-	  {
-	    close (scb->fd);
-	    goto retry;
-	  }
-	if (err)
-	  errno = err;
-	net_close (scb);
-	return -1;
-      }
-  } 
+  if (!got_connection)
+    {
+      net_close (scb);
+      return -1;
+    }
 
   /* Turn off nonblocking.  */
   ioarg = 0;
   ioctl (scb->fd, FIONBIO, &ioarg);
 
-  if (use_udp == 0)
+  if (cur_ainfo->ai_socktype == IPPROTO_TCP)
     {
       /* Disable Nagle algorithm.  Needed in some cases.  */
-      tmp = 1;
+      int tmp = 1;
+
       setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
-		  (char *)&tmp, sizeof (tmp));
+		  (char *) &tmp, sizeof (tmp));
     }
 
 #ifdef SIGPIPE
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 4475ac21a9..55abfb3254 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -259,6 +259,20 @@ This make (not runtest) variable is used to specify whether the
 testsuite preloads the read1.so library into expect.  Any non-empty
 value means true.  See "Race detection" below.
 
+GDB_TEST_SOCKETHOST
+
+This variable can provide the hostname/address that should be used
+when performing GDBserver-related tests.  This is useful in some
+situations, e.g., when you want to test the IPv6 connectivity of GDB
+and GDBserver, or when using a different hostname/address is needed.
+For example, to make GDB and GDBserver use IPv6-only connections, you
+can do:
+
+	make check TESTS="gdb.server/*.exp" RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'
+
+Note that only a hostname/address can be provided, without a port
+number.
+
 Race detection
 **************
 
diff --git a/gdb/testsuite/boards/native-extended-gdbserver.exp b/gdb/testsuite/boards/native-extended-gdbserver.exp
index df949994fd..482e4e3c14 100644
--- a/gdb/testsuite/boards/native-extended-gdbserver.exp
+++ b/gdb/testsuite/boards/native-extended-gdbserver.exp
@@ -24,8 +24,6 @@ load_generic_config "extended-gdbserver"
 load_board_description "gdbserver-base"
 load_board_description "local-board"
 
-set_board_info sockethost "localhost:"
-
 # We will be using the extended GDB remote protocol.
 set_board_info gdb_protocol "extended-remote"
 
diff --git a/gdb/testsuite/boards/native-gdbserver.exp b/gdb/testsuite/boards/native-gdbserver.exp
index ef9316007e..1dee3df4f1 100644
--- a/gdb/testsuite/boards/native-gdbserver.exp
+++ b/gdb/testsuite/boards/native-gdbserver.exp
@@ -30,7 +30,6 @@ set_board_info gdb,do_reload_on_run 1
 # There's no support for argument-passing (yet).
 set_board_info noargs 1
 
-set_board_info sockethost "localhost:"
 set_board_info use_gdb_stub 1
 set_board_info exit_is_reliable 1
 
diff --git a/gdb/testsuite/gdb.server/run-without-local-binary.exp b/gdb/testsuite/gdb.server/run-without-local-binary.exp
index 1665ca9912..6ba3e711d9 100644
--- a/gdb/testsuite/gdb.server/run-without-local-binary.exp
+++ b/gdb/testsuite/gdb.server/run-without-local-binary.exp
@@ -53,7 +53,7 @@ save_vars { GDBFLAGS } {
     set use_gdb_stub 0
 
     gdb_test "target ${gdbserver_protocol} ${gdbserver_gdbport}" \
-	"Remote debugging using $gdbserver_gdbport" \
+	"Remote debugging using [string_to_regexp $gdbserver_gdbport]" \
 	"connect to gdbserver"
 
     gdb_test "run" \
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 46e4f77922..91e3a98543 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -211,7 +211,7 @@ proc gdbserver_default_get_remote_address { host port } {
 # Default routine to compute the "comm" argument for gdbserver.
 
 proc gdbserver_default_get_comm_port { port } {
-    return ":$port"
+    return "$port"
 }
 
 # Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
@@ -221,6 +221,7 @@ proc gdbserver_default_get_comm_port { port } {
 
 proc gdbserver_start { options arguments } {
     global portnum
+    global GDB_TEST_SOCKETHOST
 
     # Port id -- either specified in baseboard file, or managed here.
     if [target_info exists gdb,socketport] {
@@ -231,10 +232,22 @@ proc gdbserver_start { options arguments } {
     }
 
     # Extract the local and remote host ids from the target board struct.
-    if [target_info exists sockethost] {
+    if { [info exists GDB_TEST_SOCKETHOST] } {
+	# The user is not supposed to provide a port number, just a
+	# hostname/address, therefore we add the trailing ":" here.
+	set debughost "${GDB_TEST_SOCKETHOST}:"
+	# Espace open and close square brackets.
+	set debughost_tmp [string map { [ \\[ ] \\] } $debughost]
+	# We need a "gdbserver" version of the debughost, which will
+	# have the possible connection prefix stripped.  This is
+	# because gdbserver currently doesn't recognize the prefixes.
+	regsub -all "^\(tcp:|udp:|tcp4:|udp4:|tcp6:|udp6:\)" $debughost_tmp "" debughost_gdbserver
+    } elseif [target_info exists sockethost] {
 	set debughost [target_info sockethost]
+	set debughost_gdbserver $debughost
     } else {
 	set debughost "localhost:"
+	set debughost_gdbserver $debughost
     }
 
     # Some boards use a different value for the port that is passed to
@@ -277,8 +290,14 @@ proc gdbserver_start { options arguments } {
 	if { $options != "" } {
 	    append gdbserver_command " $options"
 	}
+	if { $debughost_gdbserver != "" } {
+	    append gdbserver_command " $debughost_gdbserver"
+	}
 	if { $portnum != "" } {
-	    append gdbserver_command " [$get_comm_port $portnum]"
+	    if { $debughost_gdbserver == "" } {
+		append gdbserver_command " "
+	    }
+	    append gdbserver_command "[$get_comm_port $portnum]"
 	}
 	if { $arguments != "" } {
 	    append gdbserver_command " $arguments"
-- 
2.14.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] Implement IPv6 support for GDB/gdbserver
  2018-06-15  0:25 ` [PATCH v2] " Sergio Durigan Junior
@ 2018-06-15  7:12   ` Eli Zaretskii
  2018-06-20 15:24   ` Pedro Alves
  1 sibling, 0 replies; 27+ messages in thread
From: Eli Zaretskii @ 2018-06-15  7:12 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: gdb-patches, palves, jan.kratochvil, fercerpav, sekiriki,
	armandsmailings

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Jan Kratochvil <jan.kratochvil@redhat.com>,
> 	Paul Fertser <fercerpav@gmail.com>,
> 	Tsutomu Seki <sekiriki@gmail.com>,
> 	Armand Scholtes <armandsmailings@home.nl>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Thu, 14 Jun 2018 20:24:27 -0400
> 
> Changes from v1:
> 
> - s/hostnames/addresses/ on NEWS.
> 
> - Simplify functions on netstuff.c.  Add new defines for
>   GDB_NI_MAX_ADDR and GDB_NI_MAX_PORT.  Make
>   parse_hostname_without_prefix return a struct parsed_hostname.
> 
> - Use AF_UNSPEC instead of AF_INET by default on unprefixed
>   connections.
> 
> - Simplify and modernize things on gdbreplay.c.
> 
> - Implement new GDB_TEST_SOCKETHOST mechanism for testing things with
>   any type of hostname/address.
> 
> - Simplify things on boards/*.exp because of the above.
> 
> - Rewrite net_open to support multiple sockets/connections with
>   timeout/retry.
> 
> - Improve IPv6 example on documentation.

Thanks, the documentation parts are okay.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] Implement IPv6 support for GDB/gdbserver
  2018-06-15  0:25 ` [PATCH v2] " Sergio Durigan Junior
  2018-06-15  7:12   ` Eli Zaretskii
@ 2018-06-20 15:24   ` Pedro Alves
  2018-06-21  4:54     ` Sergio Durigan Junior
  1 sibling, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-06-20 15:24 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki,
	Armand Scholtes

On 06/15/2018 01:24 AM, Sergio Durigan Junior wrote:
> Changes from v1:
> 
> - s/hostnames/addresses/ on NEWS.
> 
> - Simplify functions on netstuff.c.  Add new defines for
>   GDB_NI_MAX_ADDR and GDB_NI_MAX_PORT.  Make
>   parse_hostname_without_prefix return a struct parsed_hostname.
> 
> - Use AF_UNSPEC instead of AF_INET by default on unprefixed
>   connections.
> 
> - Simplify and modernize things on gdbreplay.c.
> 
> - Implement new GDB_TEST_SOCKETHOST mechanism for testing things with
>   any type of hostname/address.
> 
> - Simplify things on boards/*.exp because of the above.

Any thoughts on a gdb.server/ smoke test that connects with
tcpv4, tcpv6, etc?  I still think it's well worth it, though
I'm OK with not having it in this patch.

> 
>   $ ./gdb -ex 'target extended-remote localhost:1234' ./a.out
> 
> the user would notice a somewhat big delay before GDB was able to
> connect to the IPv4 socket.  This happened because GDB was trying to
> connect to the IPv6 socket first, and had to wait until the connection
> timed out before it tried to connect to the IPv4 socket.

Let me try to clarify a detail here -- AFAICS, this auto-retry scenario,
does not kick in when at the socket level gdb gets a time out (ETIMEDOUT).
AFAICT, the auto-retry mechanism only kicks in when the connection is
actively refused with ECONNREFUSED (because the agent had not had enough
time to start up and start listening yet).

Note that in current master, tcp_auto_retry is _only_ checked in
the ECONNREFUSED cases:

      /* Maybe we're waiting for the remote target to become ready to
	 accept connections.  */
      if (tcp_auto_retry
#ifdef USE_WIN32API
	  && err == WSAECONNREFUSED
#else
	  && err == ECONNREFUSED
#endif
	  && wait_for_connect (NULL, &polls) >= 0)
	{
	  close (scb->fd);
	  goto retry;
	}
> 
> For that reason, I had to rewrite the main loop and implement a new
> method for handling multiple connections.  The main idea is:

So I'm surprised to see this.  I'm not sure connecting 
to multiple addresses at the same time and then discarding
all but one is a good idea.  E.g., once we have scox's
multi-client support in, this can truly manage to connect
multiple times to the same gdbserver.  It probably won't cause
an issue in practice with that agent, but I'm not sure, and
not sure about others.

In the previous discussions, I was thinking about something
simpler, something like this in pseudo code:

- refactor the net_open code that tries to connect, starting at the
  retry: label up until where we have a successful connect (excluded)
  into a try_connect function.  Tweak it to return immediately
  on ECONNREFUSED instead of waiting directly.  I.e., let the caller
  handle the wait + auto-retry logic.

- Then net_open would be something like:

  net_open ()
  {

    parse_connection_spec (....);

    addresses = getaddrinfo (....);

    do
      {
        number_refused = 0;
        foreach (address in addresses)
	  {
	    res = try_connect (address);
	    if (res == connected_ok)
	      break;
	    else if (res == ECONNREFUSED)
	      number_refused++; // don't wait for auto-retry until we've tried all addresses
	  }
      } while (tcp_auto_retry
	       && number_refused == number_addresses      // raced with server starting,
	       && wait_for_connect (NULL, &polls) >= 0);  // so wait and try again.

   /* Got connection.  */
   ...
 }


So basically we'd do the try-all-getaddrinfo-addresses loop that
everyone seems to do, with the "set tcp auto-retry on"
delay + retry logic preserving it's original intent of
being useful "if the remote debugging agent is launched in
parallel with GDB; there is a race condition because the agent may not
become ready to accept the connection before @value{GDBN} attempts to
connect.".


> diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
> new file mode 100644
> index 0000000000..687ff532b8
> --- /dev/null
> +++ b/gdb/common/netstuff.h
> @@ -0,0 +1,76 @@
> +/* Operations on network stuff.
> +   Copyright (C) 2018 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/>.  */
> +
> +#ifndef NETSTUFF_H
> +#define NETSTUFF_H
> +
> +#include <string>
> +#include "common/gdb_string_view.h"

In the end you're not using string_view (the copying
is still here), so please remove the unnecessary include.

> +
> +/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
> +#define GDB_NI_MAX_ADDR 64
> +#define GDB_NI_MAX_PORT 16
> +
> +/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
> +
> +class scoped_free_addrinfo
> +{
> +public:
> +  /* Default constructor.  */
> +  scoped_free_addrinfo (struct addrinfo *ainfo)
> +    : m_res (ainfo)
> +  {
> +  }
> +
> +  /* Destructor responsible for free'ing M_RES by calling
> +     'freeaddrinfo'.  */
> +  ~scoped_free_addrinfo ();
> +
> +  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
> +
> +private:
> +  /* The addrinfo resource.  */
> +  struct addrinfo *m_res;
> +};
> +
> +/* The struct we return after parsing the hostname.  */
> +
> +struct parsed_hostname
> +{
> +  /* The hostname.  */
> +  std::string host_str;
> +
> +  /* The port, if any.  */
> +  std::string port_str;
> +};
> +
> +
> +/* Parse HOSTNAME (which is a string in the form of "ADDR:PORT") and
> +   return a 'parsed_hostname' structure with the proper fields filled
> +   in.  Also adjust HINT accordingly.  */
> +extern parsed_hostname parse_hostname_without_prefix (std::string hostname,
> +						      struct addrinfo *hint);
> +
> +/* Parse HOSTNAME (which is a string in the form of
> +   "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_hostname'
> +   structure with the proper fields filled in.  Also adjust HINT
> +   accordingly.  */
> +extern parsed_hostname parse_hostname (const char *hostname,
> +				       struct addrinfo *hint);

After staring at these a couple times, I'm thinking that maybe
replacing the "hostname" in the function and struct names with
something else may be a bit clearer, since you're not just
parsing the host name, but also the port.

Maybe call the full protocol+address+port thing a
"connection spec", so you'd have:

 struct parsed_connection_spec (or just "struct connection_spec")
 parse_connection_spec
 parse_connection_spec_without_prefix

or:

 struct parsed_connspec (or just "struct connspec")
 parse_connspec
 parse_connspec_without_prefix


> +
> +#endif /* ! NETSTUFF_H */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index a6bad13d9d..55b48309a8 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -20509,16 +20509,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
>  @code{target} command.
>  
>  @item target remote @code{@var{host}:@var{port}}
> +@itemx target remote @code{@var{[host]}:@var{port}}
>  @itemx target remote @code{tcp:@var{host}:@var{port}}
> +@itemx target remote @code{tcp:@var{[host]}:@var{port}}
> +@itemx target remote @code{tcp4:@var{host}:@var{port}}
> +@itemx target remote @code{tcp6:@var{host}:@var{port}}
> +@itemx target remote @code{tcp6:@var{[host]}:@var{port}}
>  @itemx target extended-remote @code{@var{host}:@var{port}}
> +@itemx target extended-remote @code{@var{[host]}:@var{port}}
>  @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp:@var{[host]}:@var{port}}
> +@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
> +@itemx target extended-remote @code{tcp6:@var{[host]}:@var{port}}
>  @cindex @acronym{TCP} port, @code{target remote}
>  Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
> -The @var{host} may be either a host name or a numeric @acronym{IP}
> -address; @var{port} must be a decimal number.  The @var{host} could be
> -the target machine itself, if it is directly connected to the net, or
> -it might be a terminal server which in turn has a serial line to the
> -target.
> +The @var{host} may be either a host name, a numeric @acronym{IPv4}
> +address, or a numeric @acronym{IPv6} address (with or without the
> +square brackets to separate the address from the port); @var{port}
> +must be a decimal number.  The @var{host} could be the target machine
> +itself, if it is directly connected to the net, or it might be a
> +terminal server which in turn has a serial line to the target.
>  
>  For example, to connect to port 2828 on a terminal server named
>  @code{manyfarms}:
> @@ -20527,6 +20538,26 @@ For example, to connect to port 2828 on a terminal server named
>  target remote manyfarms:2828
>  @end smallexample
>  
> +To connect to port 2828 on a terminal server whose address is
> +@code{2001:0db8:85a3:0000:0000:8a2e:0370:7334}, you can either use the
> +square bracket syntax:
> +
> +@smallexample
> +target remote [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2828
> +@end smallexample
> +
> +@noindent
> +or explicitly specify the @acronym{IPv6} protocol:
> +
> +@smallexample
> +target remote tcp6:2001:0db8:85a3:0000:0000:8a2e:0370:7334:2828
> +@end smallexample
> +
> +This last example may be confusing to the reader, because there is no
> +visible separation between the hostname and the port number.
> +Therefore, we recommend the user to provide @acronym{IPv6} addresses
> +using square brackets for clarity.

Thanks, this example is better than in v1, though reading it I'm thinking
that it may be a good idea to explicitly say that GDB always interprets
the text after the last ":" as the port separator.  Is that right?
I.e., for GDB, there's no ambiguity at all?

Ideally the patch would include unit tests for these new parsing routines
covering cases like these.

> @@ -350,18 +396,24 @@ remote_open (const char *name)
>  #endif /* USE_WIN32API */
>    else
>      {
> -      int port;
> -      socklen_t len;
> -      struct sockaddr_in sockaddr;
> -
> -      len = sizeof (sockaddr);
> -      if (getsockname (listen_desc,
> -		       (struct sockaddr *) &sockaddr, &len) < 0
> -	  || len < sizeof (sockaddr))
> +      char listen_port[16];

GDB_NI_MAX_PORT ?


> +      struct sockaddr_storage sockaddr;
> +      socklen_t len = sizeof (sockaddr);
> +
> +      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
>  	perror_with_name ("Can't determine port");
> -      port = ntohs (sockaddr.sin_port);
>  
> -      fprintf (stderr, "Listening on port %d\n", port);
> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +			   NULL, 0,
> +			   listen_port, sizeof (listen_port),
> +			   NI_NUMERICSERV);
> +
> +      if (r != 0)
> +	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
> +		 gai_strerror (r));
> +      else
> +	fprintf (stderr, _("Listening on port %s\n"), listen_port);
> +
>        fflush (stderr);
>  
>        /* Register the event loop handler.  */
> diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
> index 23ef3b04b8..44b6d89cda 100644
> --- a/gdb/ser-tcp.c
> +++ b/gdb/ser-tcp.c
> @@ -25,6 +25,7 @@
>  #include "cli/cli-decode.h"
>  #include "cli/cli-setshow.h"
>  #include "filestuff.h"
> +#include "netstuff.h"
>  
>  #include <sys/types.h>
>  
> @@ -39,6 +40,7 @@
>  
>  #ifdef USE_WIN32API
>  #include <winsock2.h>
> +#include <wspiapi.h>
>  #ifndef ETIMEDOUT
>  #define ETIMEDOUT WSAETIMEDOUT
>  #endif
> @@ -81,12 +83,69 @@ static unsigned int tcp_retry_limit = 15;
>  
>  #define POLL_INTERVAL 5
>  
> -/* Helper function to wait a while.  If SCB is non-null, wait on its
> -   file descriptor.  Otherwise just wait on a timeout, updating *POLLS.
> -   Returns -1 on timeout or interrupt, otherwise the value of select.  */
> +/* An abstraction of a socket, useful when we want to close the socket
> +   fd automatically when exiting a context.  */
> +
> +class gdb_socket
> +{
> +public:
> +  /* Default constructor.  */

A "default constructor" is a constructor that can be called
with no arguments.  But this one cannot.

> +  gdb_socket (int sock, const struct addrinfo *ainfo)
> +    : m_socket (sock),
> +      m_released (false),
> +      m_ainfo (ainfo)
> +  {
> +  }
> +
> +  /* Release a socket, i.e., make sure it doesn't get closed when our
> +     destructor is called.  */
> +  void release ()
> +  {
> +    m_released = true;
> +  }
> +
> +  /* Return the socket associated with this object.  */
> +  int get_socket () const
> +  {
> +    return m_socket;
> +  }
> +
> +  /* Return the addrinfo structure associated with this object.  */
> +  const struct addrinfo *get_addrinfo () const
> +  {
> +    return m_ainfo;
> +  }

Nit: we don't tend to use a "get_" prefix in class getters.
Would "socket()" and "addrinfo()" work?

> +
> +  /* Destructor.  Make sure we close the socket if it hasn't been
> +     released.  */
> +  ~gdb_socket ()
> +  {
> +    if (!m_released)
> +      close (m_socket);
> +  }
> +
> +private:
> +  /* The socket.  */
> +  int m_socket;
> +
> +  /* Whether the socket has been released or not.  If it has, then we
> +     don't close it when our destructor is called.  */
> +  bool m_released;

Do we need m_released?  Wouldn't e.g., m_socket == -1 instead work?

> +
> +  /* The addrinfo structure associated with the socket.  */
> +  const struct addrinfo *m_ainfo;

So the class does not own m_ainfo?  Should be at least mentioned
in a comment.


I think it'd be good to use DISABLE_COPY_AND_ASSIGN explicitly
to make it clear the class is not meant to be copiable.


I think it should be movable, though.  See comment further below,
about vector of objects.


> +};
> +
> +/* Helper function to wait a while.  If SOCKET_POLL is non-null, wait
> +   on its file descriptors.  Otherwise just wait on a timeout, updating
> +   *POLLS.  If SOCKET_POLL and SOCKETS_READY are both non-NULL, update
> +   SOCKETS_READY with the value of the 'write' fd_set upon successful
> +   completion of the 'select' call.  Return -1 on timeout or
> +   interrupt, otherwise return the value of the 'select' call.  */
>  
>  static int
> -wait_for_connect (struct serial *scb, unsigned int *polls)
> +wait_for_connect (const std::vector<std::unique_ptr<gdb_socket>> *socket_poll,
> +		  unsigned int *polls, fd_set *sockets_ready)
>  {
>    struct timeval t;
>    int n;
> @@ -120,24 +179,39 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
>        t.tv_usec = 0;
>      }
>  
> -  if (scb)
> +  if (socket_poll != NULL)
>      {
> -      fd_set rset, wset, eset;
> +      fd_set wset, eset;
> +      int maxfd = 0;
> +
> +      FD_ZERO (&wset);
> +      FD_ZERO (&eset);
> +      for (const std::unique_ptr<gdb_socket> &ptr : *socket_poll)
> +	{
> +	  const gdb_socket *sock = ptr.get ();
> +	  int s = sock->get_socket ();

I don't see why you'd extract the raw pointer out of the
unique_ptr instead of dereferencing the unique_ptr directly, like:

      for (const std::unique_ptr<gdb_socket> &sock : *socket_poll)
	{
	  int s = sock->get_socket ();

?

This appears in several places in the patch.

But, why does the vector need to hold heap-allocated
gdb_socket instances instead of being a vector of
gdb_socket object, like:

  std::vector<gdb_socket>

?

I think you'd just need to add a move ctor (and move assign)
to gdb_socket.

Then you'd use emplace_back to push a new socket.  I.e., instead
of:

             socket_poll.push_back
               (std::unique_ptr<gdb_socket>
                (new gdb_socket (sock, cur_ainfo)));

you write:

             socket_poll.emplace_back (sock, cur_ainfo));

> +
> +	  FD_SET (s, &wset);
> +	  FD_SET (s, &eset);
> +	  maxfd = std::max (maxfd, s);
> +	}
>  
> -      FD_ZERO (&rset);
> -      FD_SET (scb->fd, &rset);
> -      wset = rset;
> -      eset = rset;
> -	  
>        /* POSIX systems return connection success or failure by signalling
>  	 wset.  Windows systems return success in wset and failure in
>  	 eset.
> -     
> +
>  	 We must call select here, rather than gdb_select, because
>  	 the serial structure has not yet been initialized - the
>  	 MinGW select wrapper will not know that this FD refers
>  	 to a socket.  */
> -      n = select (scb->fd + 1, &rset, &wset, &eset, &t);
> +      n = select (maxfd + 1, NULL, &wset, &eset, &t);
> +
> +      if (n > 0 && sockets_ready != NULL)
> +	{
> +	  /* We're just interested in possible successes, so we just
> +	     copy wset here.  */
> +	  *sockets_ready = wset;
> +	}
>      }
>    else
>      /* Use gdb_select here, since we have no file descriptors, and on
> @@ -153,171 +227,271 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
>    return n;
>  }
>  
> -/* Open a tcp socket.  */
> +/* Return TRUE if there is an error associated with socket SOCK, or
> +   FALSE otherwise.  If there's an error, set ERRNO accordingly.  */
>  
> -int
> -net_open (struct serial *scb, const char *name)
> +static bool
> +socket_error_p (int sock)
>  {
> -  char hostname[100];
> -  const char *port_str;
> -  int n, port, tmp;
> -  int use_udp;
> -  struct hostent *hostent;
> -  struct sockaddr_in sockaddr;
> -#ifdef USE_WIN32API
> -  u_long ioarg;
> -#else
> -  int ioarg;
> -#endif
> -  unsigned int polls = 0;
> +  int res, err;
> +  socklen_t len = sizeof (err);
>  
> -  use_udp = 0;
> -  if (startswith (name, "udp:"))
> -    {
> -      use_udp = 1;
> -      name = name + 4;
> -    }
> -  else if (startswith (name, "tcp:"))
> -    name = name + 4;
> +  /* On Windows, the fourth parameter to getsockopt is a "char *";
> +     on UNIX systems it is generally "void *".  The cast to "char *"
> +     is OK everywhere, since in C++ any data pointer type can be
> +     implicitly converted to "void *".  */
> +  res = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
>  
> -  port_str = strchr (name, ':');
> +  if (err != 0)
> +    errno = err;
>  
> -  if (!port_str)
> -    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
> -						       happen.  */
> +  return (res < 0 || err != 0) ? true : false;

You can just write:

 return (res < 0 || err != 0);

just like we write:

  if (expression)

instead of :

  if (expression == true)


> +}
>  
> -  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
> -  strncpy (hostname, name, tmp);	/* Don't want colon.  */
> -  hostname[tmp] = '\000';	/* Tie off host name.  */
> -  port = atoi (port_str + 1);
> +/* Helper structure containing a pair of socket and addrinfo.  */
>  
> -  /* Default hostname is localhost.  */
> -  if (!hostname[0])
> -    strcpy (hostname, "localhost");
> +struct gdb_connect_info
> +{
> +  int socket;
>  
> -  hostent = gethostbyname (hostname);
> -  if (!hostent)
> -    {
> -      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
> -      errno = ENOENT;
> -      return -1;
> -    }
> +  const struct addrinfo *ainfo;
> +};
>  
> -  sockaddr.sin_family = PF_INET;
> -  sockaddr.sin_port = htons (port);
> -  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
> -	  sizeof (struct in_addr));
> +/* Iterate over the entries of AINFO and try to open a socket and
> +   perform a 'connect' on each one.  If there's a success, return the
> +   socket and the associated 'struct addrinfo' that succeeded.
> +   Otherwise, return a special instance of gdb_connect_info whose
> +   SOCKET is -1 and AINFO is NULL.
> +
> +   Sockets that are opened are marked as non-blocking.  When a socket
> +   fails to connect (i.e., when 'connect' returns -1 and ERRNO is set
> +   to EINPROGRESS), we add this socket (along with its associated
> +   'struct addrinfo') into SOCKET_POLL.  The caller can then use
> +   SOCKET_POLL to perform a 'select' on the sockets and check if any
> +   of them succeeded.  */
> +
> +static gdb_connect_info
> +gdb_connect (const struct addrinfo *ainfo,
> +	     std::vector<std::unique_ptr<gdb_socket>> &socket_poll)
> +{
> +  gdb_connect_info ret;
> +#ifdef USE_WIN32API
> +  u_long ioarg;
> +#else
> +  int ioarg;
> +#endif
>  
> - retry:
> +  ret.socket = -1;
> +  ret.ainfo = NULL;
>  

Note you can write:

  gdb_connect_info ret = {-1, NULL};

Or add in-class initialization defaults:

 /* Helper structure containing a pair of socket and addrinfo.  */
 
 struct gdb_connect_info
 {
   int socket = -1;
 
   const struct addrinfo *ainfo = nullptr;
 };




> -  if (use_udp)
> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
> -  else
> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
> +  for (const struct addrinfo *cur_ainfo = ainfo;
> +       cur_ainfo != NULL;
> +       cur_ainfo = cur_ainfo->ai_next)
> +    {
> +      int sock = gdb_socket_cloexec (cur_ainfo->ai_family,
> +				     cur_ainfo->ai_socktype,
> +				     cur_ainfo->ai_protocol);
>  
> -  if (scb->fd == -1)
> -    return -1;
> -  
> -  /* Set socket nonblocking.  */
> -  ioarg = 1;
> -  ioctl (scb->fd, FIONBIO, &ioarg);
> +      if (sock < 0)
> +	continue;
>  
> -  /* Use Non-blocking connect.  connect() will return 0 if connected
> -     already.  */
> -  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
> +      /* Set socket nonblocking.  */
> +      ioarg = 1;
> +      ioctl (sock, FIONBIO, &ioarg);
>  
> -  if (n < 0)
> -    {
> +      /* Use Non-blocking connect.  connect() will return 0 if
> +	 connected already.  */
> +      if (connect (sock, cur_ainfo->ai_addr, cur_ainfo->ai_addrlen) == 0)
> +	{
> +	  if (!socket_error_p (sock))
> +	    {
> +	      /* Connection succeeded, we can stop trying.  */
> +	      ret.socket = sock;
> +	      ret.ainfo = cur_ainfo;
> +	      break;
> +	    }
> +	  else
> +	    {
> +	      /* There was an error with the socket.  Just try the
> +		 next one.  */
> +	      close (sock);
> +	      continue;
> +	    }
> +	}
> +      else
> +	{
>  #ifdef USE_WIN32API
> -      int err = WSAGetLastError();
> +	  int err = WSAGetLastError();
>  #else
> -      int err = errno;
> +	  int err = errno;
>  #endif
>  
> -      /* Maybe we're waiting for the remote target to become ready to
> -	 accept connections.  */
> -      if (tcp_auto_retry
> +	  if (tcp_auto_retry
>  #ifdef USE_WIN32API
> -	  && err == WSAECONNREFUSED
> +	      /* Under Windows, calling "connect" with a
> +		 non-blocking socket results in WSAEWOULDBLOCK,
> +		 not WSAEINPROGRESS.  */
> +	      && err == WSAEWOULDBLOCK
>  #else
> -	  && err == ECONNREFUSED
> +	      && err == EINPROGRESS
>  #endif
> -	  && wait_for_connect (NULL, &polls) >= 0)


Hard to read in the patch in the mail, but in the original code, we see
that tcp_auto_retry was _only_ checked if we got a ECONNREFUSED:

      /* Maybe we're waiting for the remote target to become ready to
	 accept connections.  */
      if (tcp_auto_retry
#ifdef USE_WIN32API
	  && err == WSAECONNREFUSED
#else
	  && err == ECONNREFUSED
#endif
	  && wait_for_connect (NULL, &polls) >= 0)
	{
	  close (scb->fd);
	  goto retry;
	}


while with your patch, we would check for tcp_auto_retry only when
we get EINPROGRESS:

	  if (tcp_auto_retry
#ifdef USE_WIN32API
	      /* Under Windows, calling "connect" with a
		 non-blocking socket results in WSAEWOULDBLOCK,
		 not WSAEINPROGRESS.  */
	      && err == WSAEWOULDBLOCK
#else
	      && err == EINPROGRESS
#endif
	      )
	    {
	      /* If we have an "INPROGRESS" error, add the socket
		 to the poll of sockets we have to perform a
		 'select' on.  */
	      socket_poll.push_back
		(std::unique_ptr<gdb_socket>
		 (new gdb_socket (sock, cur_ainfo)));
	    }


So I don't think I understand this.  AFAICS, you removed all references
to ECONNREFUSED.  Can you clarify?


> -	{
> -	  close (scb->fd);
> -	  goto retry;
> +	      )
> +	    {
> +	      /* If we have an "INPROGRESS" error, add the socket
> +		 to the poll of sockets we have to perform a
> +		 'select' on.  */
> +	      socket_poll.push_back
> +		(std::unique_ptr<gdb_socket>
> +		 (new gdb_socket (sock, cur_ainfo)));
> +	    }
>  	}
> +    }
> +  return ret;
> +}
>  
> -      if (
> +/* Open a tcp socket.  */
> +
> +int
> +net_open (struct serial *scb, const char *name)
> +{
> +  bool use_udp;
>  #ifdef USE_WIN32API
> -	  /* Under Windows, calling "connect" with a non-blocking socket
> -	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
> -	  err != WSAEWOULDBLOCK
> +  u_long ioarg;
>  #else
> -	  err != EINPROGRESS
> +  int ioarg;
>  #endif
> -	  )
> +  struct addrinfo hint;
> +  struct addrinfo *ainfo;
> +
> +  memset (&hint, 0, sizeof (hint));
> +  /* Assume no prefix will be passed, therefore we should use
> +     AF_UNSPEC.  */
> +  hint.ai_family = AF_UNSPEC;
> +  hint.ai_socktype = SOCK_STREAM;
> +  hint.ai_protocol = IPPROTO_TCP;
> +
> +  parsed_hostname parsed = parse_hostname (name, &hint);
> +
> +  if (parsed.port_str.empty ())
> +    error (_("Missing port on hostname '%s'"), name);
> +
> +  int r = getaddrinfo (parsed.host_str.c_str (),
> +		       parsed.port_str.c_str (), &hint, &ainfo);
> +
> +  if (r != 0)
> +    {
> +      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
> +			  name, gai_strerror (r));
> +      errno = ENOENT;
> +      return -1;
> +    }
> +
> +  scoped_free_addrinfo free_ainfo (ainfo);
> +
> +  const struct addrinfo *cur_ainfo;
> +  bool got_connection = false;
> +  unsigned int polls = 0;
> +
> +  /* Assume the worst.  */
> +  scb->fd = -1;
> +
> +  while (!got_connection)
> +    {
> +      /* A poll of sockets.  This poll will store the sockets that
> +	 "error'd" with EINPROGRESS, meaning that we will perform a
> +	 'select' on them.  */
> +      std::vector<std::unique_ptr<gdb_socket>> socket_poll;
> +      /* Try to connect.  This function will return a pair of socket
> +	 and addrinfo.  If the connection succeeded, the socket will
> +	 have a positive value and the addrinfo will not be NULL.  */
> +      gdb_connect_info gci = gdb_connect (ainfo, socket_poll);
> +
> +      if (gci.socket > -1)

That "> -1" reads as an unusual check, which gives pause.  I'd suggest:

     if (gci.socket != -1)

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2] Implement IPv6 support for GDB/gdbserver
  2018-06-20 15:24   ` Pedro Alves
@ 2018-06-21  4:54     ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-06-21  4:54 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes

On Wednesday, June 20 2018, Pedro Alves wrote:

> On 06/15/2018 01:24 AM, Sergio Durigan Junior wrote:
>> Changes from v1:
>> 
>> - s/hostnames/addresses/ on NEWS.
>> 
>> - Simplify functions on netstuff.c.  Add new defines for
>>   GDB_NI_MAX_ADDR and GDB_NI_MAX_PORT.  Make
>>   parse_hostname_without_prefix return a struct parsed_hostname.
>> 
>> - Use AF_UNSPEC instead of AF_INET by default on unprefixed
>>   connections.
>> 
>> - Simplify and modernize things on gdbreplay.c.
>> 
>> - Implement new GDB_TEST_SOCKETHOST mechanism for testing things with
>>   any type of hostname/address.
>> 
>> - Simplify things on boards/*.exp because of the above.
>
> Any thoughts on a gdb.server/ smoke test that connects with
> tcpv4, tcpv6, etc?  I still think it's well worth it, though
> I'm OK with not having it in this patch.

First, thanks for the review.

I'd like to do it, but I couldn't figure a way to do this smoke test.
Maybe you mean "just try to connect and see if GDB can successfully
parse the hostname"?  Sorry, I'm a bit lost here.

>> 
>>   $ ./gdb -ex 'target extended-remote localhost:1234' ./a.out
>> 
>> the user would notice a somewhat big delay before GDB was able to
>> connect to the IPv4 socket.  This happened because GDB was trying to
>> connect to the IPv6 socket first, and had to wait until the connection
>> timed out before it tried to connect to the IPv4 socket.
>
> Let me try to clarify a detail here -- AFAICS, this auto-retry scenario,
> does not kick in when at the socket level gdb gets a time out (ETIMEDOUT).
> AFAICT, the auto-retry mechanism only kicks in when the connection is
> actively refused with ECONNREFUSED (because the agent had not had enough
> time to start up and start listening yet).

Yes, that's correct.  When I wrote "... until the connection timed out",
I was referring to the auto-retry timeout.  Perhaps you'd like me to
further clarify this part of the commit message?

> Note that in current master, tcp_auto_retry is _only_ checked in
> the ECONNREFUSED cases:
>
>       /* Maybe we're waiting for the remote target to become ready to
> 	 accept connections.  */
>       if (tcp_auto_retry
> #ifdef USE_WIN32API
> 	  && err == WSAECONNREFUSED
> #else
> 	  && err == ECONNREFUSED
> #endif
> 	  && wait_for_connect (NULL, &polls) >= 0)
> 	{
> 	  close (scb->fd);
> 	  goto retry;
> 	}
>> 
>> For that reason, I had to rewrite the main loop and implement a new
>> method for handling multiple connections.  The main idea is:
>
> So I'm surprised to see this.  I'm not sure connecting 
> to multiple addresses at the same time and then discarding
> all but one is a good idea.

It's important to mention that we're just trying to connect to multiple
addresses in the case where getaddrinfo returns us these addresses.  It
is not because the *user* has provided multiple addresses.  And if
getaddrinfo just returns one address, we'll just try to connect to one
address.

>  E.g., once we have scox's
> multi-client support in, this can truly manage to connect
> multiple times to the same gdbserver.  It probably won't cause
> an issue in practice with that agent, but I'm not sure, and
> not sure about others.

Hm, but will the user be able to provide multiple addresses to connect
to via the CLI?  If not, then I guess nothing changes.  And if she will,
then we'll just have to call net_open for each address, I guess.

I'm in no way an expert in what scox is doing, so I may very well be
wrong, but at first thought I don't see a problem with my proposed
solution + his feature.

> In the previous discussions, I was thinking about something
> simpler,

To be completely honest, I have to say I tried something simpler at
first as well.  It's just that this code is a bit more complicated than
what I initially thought :-/.

> something like this in pseudo code:
>
> - refactor the net_open code that tries to connect, starting at the
>   retry: label up until where we have a successful connect (excluded)
>   into a try_connect function.  Tweak it to return immediately
>   on ECONNREFUSED instead of waiting directly.  I.e., let the caller
>   handle the wait + auto-retry logic.
>
> - Then net_open would be something like:
>
>   net_open ()
>   {
>
>     parse_connection_spec (....);
>
>     addresses = getaddrinfo (....);
>
>     do
>       {
>         number_refused = 0;
>         foreach (address in addresses)
> 	  {
> 	    res = try_connect (address);
> 	    if (res == connected_ok)
> 	      break;
> 	    else if (res == ECONNREFUSED)
> 	      number_refused++; // don't wait for auto-retry until we've tried all addresses
> 	  }
>       } while (tcp_auto_retry
> 	       && number_refused == number_addresses      // raced with server starting,
> 	       && wait_for_connect (NULL, &polls) >= 0);  // so wait and try again.
>
>    /* Got connection.  */
>    ...
>  }

Initially it seems OK, but I see at least one place where this might be
problematic.  For example, how is EINPROGRESS handled?  Given that our
sockets are all non-blocking in this code, 'connect' (almost) always
returns EINPROGRESS.  Currently, it's basically being handled by this
part:

      ...
      if (
#ifdef USE_WIN32API
	  /* Under Windows, calling "connect" with a non-blocking socket
	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
	  err != WSAEWOULDBLOCK
#else
	  err != EINPROGRESS
#endif
	  )
	{
	  errno = err;
	  net_close (scb);
	  return -1;
	}

      /* Looks like we need to wait for the connect.  */
      do 
	{
	  n = wait_for_connect (scb, &polls);
	} 
      while (n == 0);
      if (n < 0)
	{
	  net_close (scb);
	  return -1;
	}
      ...

Which means that GDB will wait_for_connect while the timeout is not
reached (n == 0).  From my tests, if you start gdbserver listening on an
IPv4-only socket, and then tell GDB to connect to "localhost", the IPv6
socket will still return EINPROGRESS, even though there are no IPv6
sockets listening on the other side.  This may be problematic because it
could make GDB wait unnecessarily.  But maybe 'select' will return right
away signalling that the IPv6 socket has an error (which means we'd have
to perform a getsockopt to obtain the errno), and everything will be OK.
I'd have to test, I guess.

Another thing to consider is, how many times do we loop if we get an
errno that is not ECONNREFUSED?  Worst case scenario, there may be an
error with something internal and we'd always get some other errno, in
which case we'd loop until all addresses have been tried, which is not
going to happen.  We could stipulate a maximum number of times, but
that'd be one more number to keep track of.

[ Coming back after writing the whole e-mail... ]

All in all, your proposal seems simpler.  As I said above, I want to
create a separate branch and test it.

> So basically we'd do the try-all-getaddrinfo-addresses loop that
> everyone seems to do, with the "set tcp auto-retry on"
> delay + retry logic preserving it's original intent of
> being useful "if the remote debugging agent is launched in
> parallel with GDB; there is a race condition because the agent may not
> become ready to accept the connection before @value{GDBN} attempts to
> connect.".

With my proposed implementation, this race condition is still present,
and the retry logic's meaning is preserved.

>> diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
>> new file mode 100644
>> index 0000000000..687ff532b8
>> --- /dev/null
>> +++ b/gdb/common/netstuff.h
>> @@ -0,0 +1,76 @@
>> +/* Operations on network stuff.
>> +   Copyright (C) 2018 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/>.  */
>> +
>> +#ifndef NETSTUFF_H
>> +#define NETSTUFF_H
>> +
>> +#include <string>
>> +#include "common/gdb_string_view.h"
>
> In the end you're not using string_view (the copying
> is still here), so please remove the unnecessary include.

Done.

>> +
>> +/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
>> +#define GDB_NI_MAX_ADDR 64
>> +#define GDB_NI_MAX_PORT 16
>> +
>> +/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
>> +
>> +class scoped_free_addrinfo
>> +{
>> +public:
>> +  /* Default constructor.  */
>> +  scoped_free_addrinfo (struct addrinfo *ainfo)
>> +    : m_res (ainfo)
>> +  {
>> +  }
>> +
>> +  /* Destructor responsible for free'ing M_RES by calling
>> +     'freeaddrinfo'.  */
>> +  ~scoped_free_addrinfo ();
>> +
>> +  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
>> +
>> +private:
>> +  /* The addrinfo resource.  */
>> +  struct addrinfo *m_res;
>> +};
>> +
>> +/* The struct we return after parsing the hostname.  */
>> +
>> +struct parsed_hostname
>> +{
>> +  /* The hostname.  */
>> +  std::string host_str;
>> +
>> +  /* The port, if any.  */
>> +  std::string port_str;
>> +};
>> +
>> +
>> +/* Parse HOSTNAME (which is a string in the form of "ADDR:PORT") and
>> +   return a 'parsed_hostname' structure with the proper fields filled
>> +   in.  Also adjust HINT accordingly.  */
>> +extern parsed_hostname parse_hostname_without_prefix (std::string hostname,
>> +						      struct addrinfo *hint);
>> +
>> +/* Parse HOSTNAME (which is a string in the form of
>> +   "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_hostname'
>> +   structure with the proper fields filled in.  Also adjust HINT
>> +   accordingly.  */
>> +extern parsed_hostname parse_hostname (const char *hostname,
>> +				       struct addrinfo *hint);
>
> After staring at these a couple times, I'm thinking that maybe
> replacing the "hostname" in the function and struct names with
> something else may be a bit clearer, since you're not just
> parsing the host name, but also the port.
>
> Maybe call the full protocol+address+port thing a
> "connection spec", so you'd have:
>
>  struct parsed_connection_spec (or just "struct connection_spec")
>  parse_connection_spec
>  parse_connection_spec_without_prefix
>
> or:
>
>  struct parsed_connspec (or just "struct connspec")
>  parse_connspec
>  parse_connspec_without_prefix

OK, no problem.  I prefer connection_spec, so I'll use that.

>> +
>> +#endif /* ! NETSTUFF_H */
>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index a6bad13d9d..55b48309a8 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -20509,16 +20509,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
>>  @code{target} command.
>>  
>>  @item target remote @code{@var{host}:@var{port}}
>> +@itemx target remote @code{@var{[host]}:@var{port}}
>>  @itemx target remote @code{tcp:@var{host}:@var{port}}
>> +@itemx target remote @code{tcp:@var{[host]}:@var{port}}
>> +@itemx target remote @code{tcp4:@var{host}:@var{port}}
>> +@itemx target remote @code{tcp6:@var{host}:@var{port}}
>> +@itemx target remote @code{tcp6:@var{[host]}:@var{port}}
>>  @itemx target extended-remote @code{@var{host}:@var{port}}
>> +@itemx target extended-remote @code{@var{[host]}:@var{port}}
>>  @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
>> +@itemx target extended-remote @code{tcp:@var{[host]}:@var{port}}
>> +@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
>> +@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
>> +@itemx target extended-remote @code{tcp6:@var{[host]}:@var{port}}
>>  @cindex @acronym{TCP} port, @code{target remote}
>>  Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
>> -The @var{host} may be either a host name or a numeric @acronym{IP}
>> -address; @var{port} must be a decimal number.  The @var{host} could be
>> -the target machine itself, if it is directly connected to the net, or
>> -it might be a terminal server which in turn has a serial line to the
>> -target.
>> +The @var{host} may be either a host name, a numeric @acronym{IPv4}
>> +address, or a numeric @acronym{IPv6} address (with or without the
>> +square brackets to separate the address from the port); @var{port}
>> +must be a decimal number.  The @var{host} could be the target machine
>> +itself, if it is directly connected to the net, or it might be a
>> +terminal server which in turn has a serial line to the target.
>>  
>>  For example, to connect to port 2828 on a terminal server named
>>  @code{manyfarms}:
>> @@ -20527,6 +20538,26 @@ For example, to connect to port 2828 on a terminal server named
>>  target remote manyfarms:2828
>>  @end smallexample
>>  
>> +To connect to port 2828 on a terminal server whose address is
>> +@code{2001:0db8:85a3:0000:0000:8a2e:0370:7334}, you can either use the
>> +square bracket syntax:
>> +
>> +@smallexample
>> +target remote [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2828
>> +@end smallexample
>> +
>> +@noindent
>> +or explicitly specify the @acronym{IPv6} protocol:
>> +
>> +@smallexample
>> +target remote tcp6:2001:0db8:85a3:0000:0000:8a2e:0370:7334:2828
>> +@end smallexample
>> +
>> +This last example may be confusing to the reader, because there is no
>> +visible separation between the hostname and the port number.
>> +Therefore, we recommend the user to provide @acronym{IPv6} addresses
>> +using square brackets for clarity.
>
> Thanks, this example is better than in v1, though reading it I'm thinking
> that it may be a good idea to explicitly say that GDB always interprets
> the text after the last ":" as the port separator.  Is that right?
> I.e., for GDB, there's no ambiguity at all?

Correct.

> Ideally the patch would include unit tests for these new parsing routines
> covering cases like these.

I will see to it.

>> @@ -350,18 +396,24 @@ remote_open (const char *name)
>>  #endif /* USE_WIN32API */
>>    else
>>      {
>> -      int port;
>> -      socklen_t len;
>> -      struct sockaddr_in sockaddr;
>> -
>> -      len = sizeof (sockaddr);
>> -      if (getsockname (listen_desc,
>> -		       (struct sockaddr *) &sockaddr, &len) < 0
>> -	  || len < sizeof (sockaddr))
>> +      char listen_port[16];
>
> GDB_NI_MAX_PORT ?

True, fixed.

>> +      struct sockaddr_storage sockaddr;
>> +      socklen_t len = sizeof (sockaddr);
>> +
>> +      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
>>  	perror_with_name ("Can't determine port");
>> -      port = ntohs (sockaddr.sin_port);
>>  
>> -      fprintf (stderr, "Listening on port %d\n", port);
>> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
>> +			   NULL, 0,
>> +			   listen_port, sizeof (listen_port),
>> +			   NI_NUMERICSERV);
>> +
>> +      if (r != 0)
>> +	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
>> +		 gai_strerror (r));
>> +      else
>> +	fprintf (stderr, _("Listening on port %s\n"), listen_port);
>> +
>>        fflush (stderr);
>>  
>>        /* Register the event loop handler.  */
>> diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
>> index 23ef3b04b8..44b6d89cda 100644
>> --- a/gdb/ser-tcp.c
>> +++ b/gdb/ser-tcp.c
>> @@ -25,6 +25,7 @@
>>  #include "cli/cli-decode.h"
>>  #include "cli/cli-setshow.h"
>>  #include "filestuff.h"
>> +#include "netstuff.h"
>>  
>>  #include <sys/types.h>
>>  
>> @@ -39,6 +40,7 @@
>>  
>>  #ifdef USE_WIN32API
>>  #include <winsock2.h>
>> +#include <wspiapi.h>
>>  #ifndef ETIMEDOUT
>>  #define ETIMEDOUT WSAETIMEDOUT
>>  #endif
>> @@ -81,12 +83,69 @@ static unsigned int tcp_retry_limit = 15;
>>  
>>  #define POLL_INTERVAL 5
>>  
>> -/* Helper function to wait a while.  If SCB is non-null, wait on its
>> -   file descriptor.  Otherwise just wait on a timeout, updating *POLLS.
>> -   Returns -1 on timeout or interrupt, otherwise the value of select.  */
>> +/* An abstraction of a socket, useful when we want to close the socket
>> +   fd automatically when exiting a context.  */
>> +
>> +class gdb_socket
>> +{
>> +public:
>> +  /* Default constructor.  */
>
> A "default constructor" is a constructor that can be called
> with no arguments.  But this one cannot.

Ah, OK.  I'll rewrite the comment.

>> +  gdb_socket (int sock, const struct addrinfo *ainfo)
>> +    : m_socket (sock),
>> +      m_released (false),
>> +      m_ainfo (ainfo)
>> +  {
>> +  }
>> +
>> +  /* Release a socket, i.e., make sure it doesn't get closed when our
>> +     destructor is called.  */
>> +  void release ()
>> +  {
>> +    m_released = true;
>> +  }
>> +
>> +  /* Return the socket associated with this object.  */
>> +  int get_socket () const
>> +  {
>> +    return m_socket;
>> +  }
>> +
>> +  /* Return the addrinfo structure associated with this object.  */
>> +  const struct addrinfo *get_addrinfo () const
>> +  {
>> +    return m_ainfo;
>> +  }
>
> Nit: we don't tend to use a "get_" prefix in class getters.
> Would "socket()" and "addrinfo()" work?

Yes, they work.  I fixed the names.

>> +
>> +  /* Destructor.  Make sure we close the socket if it hasn't been
>> +     released.  */
>> +  ~gdb_socket ()
>> +  {
>> +    if (!m_released)
>> +      close (m_socket);
>> +  }
>> +
>> +private:
>> +  /* The socket.  */
>> +  int m_socket;
>> +
>> +  /* Whether the socket has been released or not.  If it has, then we
>> +     don't close it when our destructor is called.  */
>> +  bool m_released;
>
> Do we need m_released?  Wouldn't e.g., m_socket == -1 instead work?

Yeah, it would.  I think it's clearer to have an explicit flag
signalling that the socket has been "released", but that's a matter of
taste, I guess.

>> +
>> +  /* The addrinfo structure associated with the socket.  */
>> +  const struct addrinfo *m_ainfo;
>
> So the class does not own m_ainfo?  Should be at least mentioned
> in a comment.

That's correct, m_ainfo isn't owned by the class.  I've clarified that
now.

> I think it'd be good to use DISABLE_COPY_AND_ASSIGN explicitly
> to make it clear the class is not meant to be copiable.
>
> I think it should be movable, though.  See comment further below,
> about vector of objects.
>
>
>> +};
>> +
>> +/* Helper function to wait a while.  If SOCKET_POLL is non-null, wait
>> +   on its file descriptors.  Otherwise just wait on a timeout, updating
>> +   *POLLS.  If SOCKET_POLL and SOCKETS_READY are both non-NULL, update
>> +   SOCKETS_READY with the value of the 'write' fd_set upon successful
>> +   completion of the 'select' call.  Return -1 on timeout or
>> +   interrupt, otherwise return the value of the 'select' call.  */
>>  
>>  static int
>> -wait_for_connect (struct serial *scb, unsigned int *polls)
>> +wait_for_connect (const std::vector<std::unique_ptr<gdb_socket>> *socket_poll,
>> +		  unsigned int *polls, fd_set *sockets_ready)
>>  {
>>    struct timeval t;
>>    int n;
>> @@ -120,24 +179,39 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
>>        t.tv_usec = 0;
>>      }
>>  
>> -  if (scb)
>> +  if (socket_poll != NULL)
>>      {
>> -      fd_set rset, wset, eset;
>> +      fd_set wset, eset;
>> +      int maxfd = 0;
>> +
>> +      FD_ZERO (&wset);
>> +      FD_ZERO (&eset);
>> +      for (const std::unique_ptr<gdb_socket> &ptr : *socket_poll)
>> +	{
>> +	  const gdb_socket *sock = ptr.get ();
>> +	  int s = sock->get_socket ();
>
> I don't see why you'd extract the raw pointer out of the
> unique_ptr instead of dereferencing the unique_ptr directly, like:
>
>       for (const std::unique_ptr<gdb_socket> &sock : *socket_poll)
> 	{
> 	  int s = sock->get_socket ();
>
> ?
>
> This appears in several places in the patch.

Pure ignorance.  Sorry about that, I've simplified the code now.

> But, why does the vector need to hold heap-allocated
> gdb_socket instances instead of being a vector of
> gdb_socket object, like:
>
>   std::vector<gdb_socket>
>
> ?
>
> I think you'd just need to add a move ctor (and move assign)
> to gdb_socket.
>
> Then you'd use emplace_back to push a new socket.  I.e., instead
> of:
>
>              socket_poll.push_back
>                (std::unique_ptr<gdb_socket>
>                 (new gdb_socket (sock, cur_ainfo)));
>
> you write:
>
>              socket_poll.emplace_back (sock, cur_ainfo));

My first version of the code was like that, but I was having issues with
sockets being closed in the middle of the function.  That was, of
course, because the vector was being extended and, when moving
gdb_socket's around, it was calling destructors everywhere.  I remember
thinking "I should probably implement some move semantics here", but in
the end I chose the more complicated path.  Thanks for the tips, I've
now simplified the code a bit more.

>> +
>> +	  FD_SET (s, &wset);
>> +	  FD_SET (s, &eset);
>> +	  maxfd = std::max (maxfd, s);
>> +	}
>>  
>> -      FD_ZERO (&rset);
>> -      FD_SET (scb->fd, &rset);
>> -      wset = rset;
>> -      eset = rset;
>> -	  
>>        /* POSIX systems return connection success or failure by signalling
>>  	 wset.  Windows systems return success in wset and failure in
>>  	 eset.
>> -     
>> +
>>  	 We must call select here, rather than gdb_select, because
>>  	 the serial structure has not yet been initialized - the
>>  	 MinGW select wrapper will not know that this FD refers
>>  	 to a socket.  */
>> -      n = select (scb->fd + 1, &rset, &wset, &eset, &t);
>> +      n = select (maxfd + 1, NULL, &wset, &eset, &t);
>> +
>> +      if (n > 0 && sockets_ready != NULL)
>> +	{
>> +	  /* We're just interested in possible successes, so we just
>> +	     copy wset here.  */
>> +	  *sockets_ready = wset;
>> +	}
>>      }
>>    else
>>      /* Use gdb_select here, since we have no file descriptors, and on
>> @@ -153,171 +227,271 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
>>    return n;
>>  }
>>  
>> -/* Open a tcp socket.  */
>> +/* Return TRUE if there is an error associated with socket SOCK, or
>> +   FALSE otherwise.  If there's an error, set ERRNO accordingly.  */
>>  
>> -int
>> -net_open (struct serial *scb, const char *name)
>> +static bool
>> +socket_error_p (int sock)
>>  {
>> -  char hostname[100];
>> -  const char *port_str;
>> -  int n, port, tmp;
>> -  int use_udp;
>> -  struct hostent *hostent;
>> -  struct sockaddr_in sockaddr;
>> -#ifdef USE_WIN32API
>> -  u_long ioarg;
>> -#else
>> -  int ioarg;
>> -#endif
>> -  unsigned int polls = 0;
>> +  int res, err;
>> +  socklen_t len = sizeof (err);
>>  
>> -  use_udp = 0;
>> -  if (startswith (name, "udp:"))
>> -    {
>> -      use_udp = 1;
>> -      name = name + 4;
>> -    }
>> -  else if (startswith (name, "tcp:"))
>> -    name = name + 4;
>> +  /* On Windows, the fourth parameter to getsockopt is a "char *";
>> +     on UNIX systems it is generally "void *".  The cast to "char *"
>> +     is OK everywhere, since in C++ any data pointer type can be
>> +     implicitly converted to "void *".  */
>> +  res = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
>>  
>> -  port_str = strchr (name, ':');
>> +  if (err != 0)
>> +    errno = err;
>>  
>> -  if (!port_str)
>> -    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
>> -						       happen.  */
>> +  return (res < 0 || err != 0) ? true : false;
>
> You can just write:
>
>  return (res < 0 || err != 0);
>
> just like we write:
>
>   if (expression)
>
> instead of :
>
>   if (expression == true)

That I know!  Heh.  This was a brain fart; the code was different before
(I was returning something else instead of true/false), so when I
changed the return value I blindly adjusted this statement.  Anyway,
fixed.

>> +}
>>  
>> -  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
>> -  strncpy (hostname, name, tmp);	/* Don't want colon.  */
>> -  hostname[tmp] = '\000';	/* Tie off host name.  */
>> -  port = atoi (port_str + 1);
>> +/* Helper structure containing a pair of socket and addrinfo.  */
>>  
>> -  /* Default hostname is localhost.  */
>> -  if (!hostname[0])
>> -    strcpy (hostname, "localhost");
>> +struct gdb_connect_info
>> +{
>> +  int socket;
>>  
>> -  hostent = gethostbyname (hostname);
>> -  if (!hostent)
>> -    {
>> -      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
>> -      errno = ENOENT;
>> -      return -1;
>> -    }
>> +  const struct addrinfo *ainfo;
>> +};
>>  
>> -  sockaddr.sin_family = PF_INET;
>> -  sockaddr.sin_port = htons (port);
>> -  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
>> -	  sizeof (struct in_addr));
>> +/* Iterate over the entries of AINFO and try to open a socket and
>> +   perform a 'connect' on each one.  If there's a success, return the
>> +   socket and the associated 'struct addrinfo' that succeeded.
>> +   Otherwise, return a special instance of gdb_connect_info whose
>> +   SOCKET is -1 and AINFO is NULL.
>> +
>> +   Sockets that are opened are marked as non-blocking.  When a socket
>> +   fails to connect (i.e., when 'connect' returns -1 and ERRNO is set
>> +   to EINPROGRESS), we add this socket (along with its associated
>> +   'struct addrinfo') into SOCKET_POLL.  The caller can then use
>> +   SOCKET_POLL to perform a 'select' on the sockets and check if any
>> +   of them succeeded.  */
>> +
>> +static gdb_connect_info
>> +gdb_connect (const struct addrinfo *ainfo,
>> +	     std::vector<std::unique_ptr<gdb_socket>> &socket_poll)
>> +{
>> +  gdb_connect_info ret;
>> +#ifdef USE_WIN32API
>> +  u_long ioarg;
>> +#else
>> +  int ioarg;
>> +#endif
>>  
>> - retry:
>> +  ret.socket = -1;
>> +  ret.ainfo = NULL;
>>  
>
> Note you can write:
>
>   gdb_connect_info ret = {-1, NULL};
>
> Or add in-class initialization defaults:
>
>  /* Helper structure containing a pair of socket and addrinfo.  */
>  
>  struct gdb_connect_info
>  {
>    int socket = -1;
>  
>    const struct addrinfo *ainfo = nullptr;
>  };

Ah, fair enough.  I'll go with in-class initialization.

>> -  if (use_udp)
>> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
>> -  else
>> -    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
>> +  for (const struct addrinfo *cur_ainfo = ainfo;
>> +       cur_ainfo != NULL;
>> +       cur_ainfo = cur_ainfo->ai_next)
>> +    {
>> +      int sock = gdb_socket_cloexec (cur_ainfo->ai_family,
>> +				     cur_ainfo->ai_socktype,
>> +				     cur_ainfo->ai_protocol);
>>  
>> -  if (scb->fd == -1)
>> -    return -1;
>> -  
>> -  /* Set socket nonblocking.  */
>> -  ioarg = 1;
>> -  ioctl (scb->fd, FIONBIO, &ioarg);
>> +      if (sock < 0)
>> +	continue;
>>  
>> -  /* Use Non-blocking connect.  connect() will return 0 if connected
>> -     already.  */
>> -  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
>> +      /* Set socket nonblocking.  */
>> +      ioarg = 1;
>> +      ioctl (sock, FIONBIO, &ioarg);
>>  
>> -  if (n < 0)
>> -    {
>> +      /* Use Non-blocking connect.  connect() will return 0 if
>> +	 connected already.  */
>> +      if (connect (sock, cur_ainfo->ai_addr, cur_ainfo->ai_addrlen) == 0)
>> +	{
>> +	  if (!socket_error_p (sock))
>> +	    {
>> +	      /* Connection succeeded, we can stop trying.  */
>> +	      ret.socket = sock;
>> +	      ret.ainfo = cur_ainfo;
>> +	      break;
>> +	    }
>> +	  else
>> +	    {
>> +	      /* There was an error with the socket.  Just try the
>> +		 next one.  */
>> +	      close (sock);
>> +	      continue;
>> +	    }
>> +	}
>> +      else
>> +	{
>>  #ifdef USE_WIN32API
>> -      int err = WSAGetLastError();
>> +	  int err = WSAGetLastError();
>>  #else
>> -      int err = errno;
>> +	  int err = errno;
>>  #endif
>>  
>> -      /* Maybe we're waiting for the remote target to become ready to
>> -	 accept connections.  */
>> -      if (tcp_auto_retry
>> +	  if (tcp_auto_retry
>>  #ifdef USE_WIN32API
>> -	  && err == WSAECONNREFUSED
>> +	      /* Under Windows, calling "connect" with a
>> +		 non-blocking socket results in WSAEWOULDBLOCK,
>> +		 not WSAEINPROGRESS.  */
>> +	      && err == WSAEWOULDBLOCK
>>  #else
>> -	  && err == ECONNREFUSED
>> +	      && err == EINPROGRESS
>>  #endif
>> -	  && wait_for_connect (NULL, &polls) >= 0)
>
>
> Hard to read in the patch in the mail, but in the original code, we see
> that tcp_auto_retry was _only_ checked if we got a ECONNREFUSED:
>
>       /* Maybe we're waiting for the remote target to become ready to
> 	 accept connections.  */
>       if (tcp_auto_retry
> #ifdef USE_WIN32API
> 	  && err == WSAECONNREFUSED
> #else
> 	  && err == ECONNREFUSED
> #endif
> 	  && wait_for_connect (NULL, &polls) >= 0)
> 	{
> 	  close (scb->fd);
> 	  goto retry;
> 	}
>
>
> while with your patch, we would check for tcp_auto_retry only when
> we get EINPROGRESS:
>
> 	  if (tcp_auto_retry
> #ifdef USE_WIN32API
> 	      /* Under Windows, calling "connect" with a
> 		 non-blocking socket results in WSAEWOULDBLOCK,
> 		 not WSAEINPROGRESS.  */
> 	      && err == WSAEWOULDBLOCK
> #else
> 	      && err == EINPROGRESS
> #endif
> 	      )
> 	    {
> 	      /* If we have an "INPROGRESS" error, add the socket
> 		 to the poll of sockets we have to perform a
> 		 'select' on.  */
> 	      socket_poll.push_back
> 		(std::unique_ptr<gdb_socket>
> 		 (new gdb_socket (sock, cur_ainfo)));
> 	    }
>
>
> So I don't think I understand this.  AFAICS, you removed all references
> to ECONNREFUSED.  Can you clarify?

Yeah, I guess I've made a confusion here.  There's a difference between
"wait_for_connect (SOCKET, &polls);" and "wait_for_connect (NULL,
&polls);".  The first one will perform a 'select' on a socket that got
EINPROGRESS, in which case the 'select' is probably going to return
before the timeout.  In this case, in the original code, GDB doesn't
check for 'tcp_auto_retry'.  This makes sense, because we want to
call 'wait_for_connect' anyway.  In my code, GDB is check the value of
'tcp_auto_retry' in this case.  This is wrong,

As for ECONNREFUSED, in my proposal, if 'connect' doesn't return
EINPROGRESS, then I consider it's worth retrying.  Maybe I should indeed
be more selective and just perform the retry when errno is
ECONNREFUSED.  Also, I noticed I'm not calling 'tcp_auto_retry' when I
should, which is here:

      ...
      if (got_connection)
	{
	  /* Maybe we've been able to establish a connection.  If so,
	     just break.  */
	  break;
	}

      /* Let's wait a bit.  */
----> // HERE
      if (wait_for_connect (NULL, &polls, NULL) < 0)
	break;
      ...

So yeah, there is a problem with the current proposal.

My current fix is to:

1) Remove the check for 'tcp_auto_retry' when checking for errno ==
EINPROGRESS.

2) Add a check for errno == ECONNREFUSED, in which case we'd signal that
GDB needs to call "wait_for_connect (NULL...)" if no connection
succeeds.

>> -	{
>> -	  close (scb->fd);
>> -	  goto retry;
>> +	      )
>> +	    {
>> +	      /* If we have an "INPROGRESS" error, add the socket
>> +		 to the poll of sockets we have to perform a
>> +		 'select' on.  */
>> +	      socket_poll.push_back
>> +		(std::unique_ptr<gdb_socket>
>> +		 (new gdb_socket (sock, cur_ainfo)));
>> +	    }
>>  	}
>> +    }
>> +  return ret;
>> +}
>>  
>> -      if (
>> +/* Open a tcp socket.  */
>> +
>> +int
>> +net_open (struct serial *scb, const char *name)
>> +{
>> +  bool use_udp;
>>  #ifdef USE_WIN32API
>> -	  /* Under Windows, calling "connect" with a non-blocking socket
>> -	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
>> -	  err != WSAEWOULDBLOCK
>> +  u_long ioarg;
>>  #else
>> -	  err != EINPROGRESS
>> +  int ioarg;
>>  #endif
>> -	  )
>> +  struct addrinfo hint;
>> +  struct addrinfo *ainfo;
>> +
>> +  memset (&hint, 0, sizeof (hint));
>> +  /* Assume no prefix will be passed, therefore we should use
>> +     AF_UNSPEC.  */
>> +  hint.ai_family = AF_UNSPEC;
>> +  hint.ai_socktype = SOCK_STREAM;
>> +  hint.ai_protocol = IPPROTO_TCP;
>> +
>> +  parsed_hostname parsed = parse_hostname (name, &hint);
>> +
>> +  if (parsed.port_str.empty ())
>> +    error (_("Missing port on hostname '%s'"), name);
>> +
>> +  int r = getaddrinfo (parsed.host_str.c_str (),
>> +		       parsed.port_str.c_str (), &hint, &ainfo);
>> +
>> +  if (r != 0)
>> +    {
>> +      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
>> +			  name, gai_strerror (r));
>> +      errno = ENOENT;
>> +      return -1;
>> +    }
>> +
>> +  scoped_free_addrinfo free_ainfo (ainfo);
>> +
>> +  const struct addrinfo *cur_ainfo;
>> +  bool got_connection = false;
>> +  unsigned int polls = 0;
>> +
>> +  /* Assume the worst.  */
>> +  scb->fd = -1;
>> +
>> +  while (!got_connection)
>> +    {
>> +      /* A poll of sockets.  This poll will store the sockets that
>> +	 "error'd" with EINPROGRESS, meaning that we will perform a
>> +	 'select' on them.  */
>> +      std::vector<std::unique_ptr<gdb_socket>> socket_poll;
>> +      /* Try to connect.  This function will return a pair of socket
>> +	 and addrinfo.  If the connection succeeded, the socket will
>> +	 have a positive value and the addrinfo will not be NULL.  */
>> +      gdb_connect_info gci = gdb_connect (ainfo, socket_poll);
>> +
>> +      if (gci.socket > -1)
>
> That "> -1" reads as an unusual check, which gives pause.  I'd suggest:
>
>      if (gci.socket != -1)

Done.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v3] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
                   ` (3 preceding siblings ...)
  2018-06-15  0:25 ` [PATCH v2] " Sergio Durigan Junior
@ 2018-07-07 20:47 ` Sergio Durigan Junior
  2018-07-11 12:55   ` Pedro Alves
  2018-07-11 19:16 ` [PATCH v4] " Sergio Durigan Junior
  5 siblings, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-07-07 20:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes, Sergio Durigan Junior

Changes from v2:

- s/parse_hostname/parse_connection_spec/

- Wrote unittests for parse_connection_spec* functions.

- Improved parse_connection_spec_without_prefix, specifically for the
  case when we're dealing with an IPv6 address.  Now the function
  performs better checks for open/close brackets, and correctly
  extracts the port number when the IPv6 address doesn't use brackets.

- Removed inclusion of common/gdb_string_view.h from
  common/netstuff.h.

- Clarified that, when the IPv6 address is passed without the use of
  brackets, it may be ambiguous for the user, but not for GDB (which
  will extract the number after the last colon as the port number
  anyway).

- s/16/GDB_NI_MAX_PORT/ on gdbserver/remote-utils.c:remote_open

- Reimplemented logic on gdb/ser-tcp.c:net_open aiming at simplifying
  the function, based on Pedro's suggestions.

- Implemented tests on gdb.server/ for the various connection specs.



This patch implements IPv6 support for both GDB and gdbserver.  Based
on my research, it is the fourth attempt to do that since 2006.  Since
I used ideas from all of the previous patches, I also added their
authors's names on the ChangeLogs as a way to recognize their
efforts.  For reference sake, you can find the previous attempts at:

  https://sourceware.org/ml/gdb-patches/2006-09/msg00192.html

  https://sourceware.org/ml/gdb-patches/2014-02/msg00248.html

  https://sourceware.org/ml/gdb-patches/2016-02/msg00226.html

The basic idea behind the patch is to start using the new
'getaddrinfo'/'getnameinfo' calls, which are responsible for
translating names and addresses in a protocol-independent way.  This
means that if we ever have a new version of the IP protocol, we won't
need to change the code again (or, at least, won't have to change the
majority of the code).

The function 'getaddrinfo' returns a linked list of possible addresses
to connect to.  Dealing with multiple addresses proved to be a hard
task with the current TCP auto-retry mechanism implemented on
ser-tcp:net_open.  For example, when gdbserver listened only on an
IPv4 socket:

  $ ./gdbserver --once 127.0.0.1:1234 ./a.out

and GDB was instructed to try to connect to both IPv6 and IPv4
sockets:

  $ ./gdb -ex 'target extended-remote localhost:1234' ./a.out

the user would notice a somewhat big delay before GDB was able to
connect to the IPv4 socket.  This happened because GDB was trying to
connect to the IPv6 socket first, and had to wait until the connection
timed out before it tried to connect to the IPv4 socket.

For that reason, I had to rewrite the main loop and implement a new
method for handling multiple connections.  After some discussion,
Pedro and I agreed on the following algorithm:

  1) For each entry returned by 'getaddrinfo', we try to open a socket
  and connect to it.

  2.a) If we have a successful 'connect', we just use that connection.

  2.b) If we don't have a successfull 'connect', but if we've got a
  ECONNREFUSED (meaning the the connection was refused), we keep track
  of this fact by using a flag.

  2.c) If we don't have a successfull 'connect', but if we've got a
  EINPROGRESS (meaning that the connection is in progress), we perform
  a 'select' call on the socket until we have a result (either a
  successful connection, or an error on the socket).

  3) If tcp_auto_retry is true, and we haven't gotten a successful
  connection, and at least one of our attempts failed with
  ECONNREFUSED, then we wait a little bit (i.e., call
  'wait_for_connect'), check to see if there was a
  timeout/interruption (in which case we bail out), and then go back
  to (1).

After multiple tests, I was able to connect without delay on the
scenario described above, and was also able to connect in all other
types of scenarios.

I also implemented some hostname parsing functions (along with their
corresponding unit tests) which are used to help GDB and gdbserver to
parse hostname strings provided by the user.  These new functions are
living inside common/netstuff.[ch].  I've had to do that since IPv6
introduces a new URL scheme, which defines that square brackets can be
used to enclose the host part and differentiate it from the
port (e.g., "[::1]:1234" means "host ::1, port 1234").  I spent some
time thinking about a reasonable way to interpret what the user wants,
and I came up with the following:

  - If the user has provided a prefix that doesn't specify the protocol
    version (i.e., "tcp:" or "udp:"), or if the user has not provided
    any prefix, don't make any assumptions (i.e., assume AF_UNSPEC when
    dealing with 'getaddrinfo') *unless* the host starts with "[" (in
    which case, assume it's an IPv6 host).

  - If the user has provided a prefix that does specify the protocol
    version (i.e., "tcp4:", "tcp6:", "udp4:" or "udp6:"), then respect
    that.

This method doesn't follow strictly what RFC 2732 proposes (that
literal IPv6 addresses should be provided enclosed in "[" and "]")
because IPv6 addresses still can be provided without square brackets
in our case, but since we have prefixes to specify protocol versions I
think this is not an issue.

Another thing worth mentioning is the new 'GDB_TEST_SOCKETHOST'
testcase parameter, which makes it possible to specify the
hostname (without the port) to be used when testing GDB and
gdbserver.  For example, to run IPv6 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'

Or, to run IPv4 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp4:127.0.0.1'

This required a few changes on the gdbserver-base.exp, and also a
minimal adjustment on gdb.server/run-without-local-binary.exp.

Finally, I've implemented a new testcase,
gdb.server/server-connect.exp, which is supposed to run on the native
host and perform various "smoke tests" using different connection
methods.

This patch has been regression-tested on BuildBot and locally, and
also built using a x86_64-w64-mingw32 GCC, and no problems were found.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>
	    Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	'unittests/parse-connection-spec-selftests.c'.
	(COMMON_SFILES): Add 'common/netstuff.c'.
	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
	* NEWS (Changes since GDB 8.2): Mention IPv6 support.
	* common/netstuff.c: New file.
	* common/netstuff.h: New file.
	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
	(wait_for_connect): Update comment.  New parameter
	'gdb::optional<int> sock' instead of 'struct serial *scb'.
	Use 'sock' directly instead of 'scb->fd'.
	(try_connect): New function, with code from 'net_open'.
	(net_open): Rewrite main loop to deal with multiple
	sockets/addresses.  Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	* unittests/parse-connection-spec-selftests.c: New file.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
	(OBS): Add 'common/netstuff.o'.
	(GDBREPLAY_OBS): Likewise.
	* gdbreplay.c: Include 'wspiapi.h' and 'netstuff.h'.
	(remote_open): Implement support for IPv6
	connections.
	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
	and 'wspiapi.h'.
	(handle_accept_event): Accept connections from IPv6 sources.
	(remote_prepare): Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	(remote_open): Implement support for printing connections from
	IPv6 sources.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* README (Testsuite Parameters): Mention new 'GDB_TEST_SOCKETHOST'
	parameter.
	* boards/native-extended-gdbserver.exp: Do not set 'sockethost'
	by default.
	* boards/native-gdbserver.exp: Likewise.
	* gdb.server/run-without-local-binary.exp: Improve regexp used
	for detecting when a remote debugging connection succeeds.
	* gdb.server/server-connect.exp: New file.
	* lib/gdbserver-support.exp (gdbserver_default_get_comm_port):
	Do not prefix the port number with ":".
	(gdbserver_start): New global GDB_TEST_SOCKETHOST.  Implement
	support for detecting and using it.  Add '$debughost_gdbserver'
	to the list of arguments used to start gdbserver.  Handle case
	when gdbserver cannot resolve a network name.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* gdb.texinfo (Remote Connection Commands): Add explanation
	about new IPv6 support.  Add new connection prefixes.
---
 gdb/Makefile.in                                    |   3 +
 gdb/NEWS                                           |   4 +
 gdb/common/netstuff.c                              | 155 +++++++++++
 gdb/common/netstuff.h                              |  76 +++++
 gdb/doc/gdb.texinfo                                |  51 +++-
 gdb/gdbserver/Makefile.in                          |   3 +
 gdb/gdbserver/gdbreplay.c                          | 129 ++++++---
 gdb/gdbserver/remote-utils.c                       | 119 +++++---
 gdb/ser-tcp.c                                      | 308 +++++++++++++--------
 gdb/testsuite/README                               |  14 +
 gdb/testsuite/boards/native-extended-gdbserver.exp |   2 -
 gdb/testsuite/boards/native-gdbserver.exp          |   1 -
 .../gdb.server/run-without-local-binary.exp        |   2 +-
 gdb/testsuite/gdb.server/server-connect.exp        | 111 ++++++++
 gdb/testsuite/lib/gdbserver-support.exp            |  28 +-
 gdb/unittests/parse-connection-spec-selftests.c    | 251 +++++++++++++++++
 16 files changed, 1058 insertions(+), 199 deletions(-)
 create mode 100644 gdb/common/netstuff.c
 create mode 100644 gdb/common/netstuff.h
 create mode 100644 gdb/testsuite/gdb.server/server-connect.exp
 create mode 100644 gdb/unittests/parse-connection-spec-selftests.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 13627e07e0..a3b22c1134 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -430,6 +430,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/offset-type-selftests.c \
 	unittests/observable-selftests.c \
 	unittests/optional-selftests.c \
+	unittests/parse-connection-spec-selftests.c \
 	unittests/ptid-selftests.c \
 	unittests/rsp-low-selftests.c \
 	unittests/scoped_fd-selftests.c \
@@ -967,6 +968,7 @@ COMMON_SFILES = \
 	common/job-control.c \
 	common/gdb_tilde_expand.c \
 	common/gdb_vecs.c \
+	common/netstuff.c \
 	common/new-op.c \
 	common/pathstuff.c \
 	common/print-utils.c \
@@ -1448,6 +1450,7 @@ HFILES_NO_SRCDIR = \
 	common/gdb_vecs.h \
 	common/gdb_wait.h \
 	common/common-inferior.h \
+	common/netstuff.h \
 	common/host-defs.h \
 	common/pathstuff.h \
 	common/print-utils.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index 2d1d161233..acb9c34fb2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 8.2
 
+* GDB and GDBserver now support IPv6 connections.  IPv6 addresses
+  can be passed using the '[ADDRESS]:PORT' notation, or the regular
+  'ADDRESS:PORT' method.
+
 *** Changes in GDB 8.2
 
 * The 'set disassembler-options' command now supports specifying options
diff --git a/gdb/common/netstuff.c b/gdb/common/netstuff.c
new file mode 100644
index 0000000000..c1c401ccb2
--- /dev/null
+++ b/gdb/common/netstuff.c
@@ -0,0 +1,155 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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 <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+/* See common/netstuff.h.  */
+
+scoped_free_addrinfo::~scoped_free_addrinfo ()
+{
+  freeaddrinfo (m_res);
+}
+
+/* See common/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 common/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);
+}
diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
new file mode 100644
index 0000000000..17deb3fa87
--- /dev/null
+++ b/gdb/common/netstuff.h
@@ -0,0 +1,76 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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/>.  */
+
+#ifndef NETSTUFF_H
+#define NETSTUFF_H
+
+#include <string>
+
+/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
+#define GDB_NI_MAX_ADDR 64
+#define GDB_NI_MAX_PORT 16
+
+/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
+
+class scoped_free_addrinfo
+{
+public:
+  /* Default constructor.  */
+  scoped_free_addrinfo (struct addrinfo *ainfo)
+    : m_res (ainfo)
+  {
+  }
+
+  /* Destructor responsible for free'ing M_RES by calling
+     'freeaddrinfo'.  */
+  ~scoped_free_addrinfo ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
+
+private:
+  /* The addrinfo resource.  */
+  struct addrinfo *m_res;
+};
+
+/* The struct we return after parsing the connection spec.  */
+
+struct parsed_connection_spec
+{
+  /* The hostname.  */
+  std::string host_str;
+
+  /* The port, if any.  */
+  std::string port_str;
+};
+
+
+/* Parse SPEC (which is a string in the form of "ADDR:PORT") and
+   return a 'parsed_connection_spec' structure with the proper fields
+   filled in.  Also adjust HINT accordingly.  */
+extern parsed_connection_spec
+  parse_connection_spec_without_prefix (std::string spec,
+					struct addrinfo *hint);
+
+/* Parse SPEC (which is a string in the form of
+   "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_connection_spec'
+   structure with the proper fields filled in.  Also adjust HINT
+   accordingly.  */
+extern parsed_connection_spec parse_connection_spec (const char *spec,
+						     struct addrinfo *hint);
+
+#endif /* ! NETSTUFF_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 91ec219958..cd472ce5b5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20517,16 +20517,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
 @code{target} command.
 
 @item target remote @code{@var{host}:@var{port}}
+@itemx target remote @code{@var{[host]}:@var{port}}
 @itemx target remote @code{tcp:@var{host}:@var{port}}
+@itemx target remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target remote @code{tcp4:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{[host]}:@var{port}}
 @itemx target extended-remote @code{@var{host}:@var{port}}
+@itemx target extended-remote @code{@var{[host]}:@var{port}}
 @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{[host]}:@var{port}}
 @cindex @acronym{TCP} port, @code{target remote}
 Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
-The @var{host} may be either a host name or a numeric @acronym{IP}
-address; @var{port} must be a decimal number.  The @var{host} could be
-the target machine itself, if it is directly connected to the net, or
-it might be a terminal server which in turn has a serial line to the
-target.
+The @var{host} may be either a host name, a numeric @acronym{IPv4}
+address, or a numeric @acronym{IPv6} address (with or without the
+square brackets to separate the address from the port); @var{port}
+must be a decimal number.  The @var{host} could be the target machine
+itself, if it is directly connected to the net, or it might be a
+terminal server which in turn has a serial line to the target.
 
 For example, to connect to port 2828 on a terminal server named
 @code{manyfarms}:
@@ -20535,6 +20546,28 @@ For example, to connect to port 2828 on a terminal server named
 target remote manyfarms:2828
 @end smallexample
 
+To connect to port 2828 on a terminal server whose address is
+@code{2001:0db8:85a3:0000:0000:8a2e:0370:7334}, you can either use the
+square bracket syntax:
+
+@smallexample
+target remote [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2828
+@end smallexample
+
+@noindent
+or explicitly specify the @acronym{IPv6} protocol:
+
+@smallexample
+target remote tcp6:2001:0db8:85a3:0000:0000:8a2e:0370:7334:2828
+@end smallexample
+
+This last example may be confusing to the reader, because there is no
+visible separation between the hostname and the port number.
+Therefore, we recommend the user to provide @acronym{IPv6} addresses
+using square brackets for clarity.  However, it is important to
+mention that for @value{GDBN} there is no ambiguity: the number after
+the last colon is considered to be the port number.
+
 If your remote target is actually running on the same machine as your
 debugger session (e.g.@: a simulator for your target running on the
 same host), you can omit the hostname.  For example, to connect to
@@ -20548,7 +20581,15 @@ target remote :1234
 Note that the colon is still required here.
 
 @item target remote @code{udp:@var{host}:@var{port}}
+@itemx target remote @code{udp:@var{[host]}:@var{port}}
+@itemx target remote @code{udp4:@var{host}:@var{port}}
+@itemx target remote @code{udp6:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp:@var{host}:@var{port}}
 @itemx target extended-remote @code{udp:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{[host]}:@var{port}}
 @cindex @acronym{UDP} port, @code{target remote}
 Debug using @acronym{UDP} packets to @var{port} on @var{host}.  For example, to
 connect to @acronym{UDP} port 2828 on a terminal server named @code{manyfarms}:
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 513f286289..f2f8a084bd 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -211,6 +211,7 @@ SFILES = \
 	$(srcdir)/common/job-control.c \
 	$(srcdir)/common/gdb_tilde_expand.c \
 	$(srcdir)/common/gdb_vecs.c \
+	$(srcdir)/common/netstuff.c \
 	$(srcdir)/common/new-op.c \
 	$(srcdir)/common/pathstuff.c \
 	$(srcdir)/common/print-utils.c \
@@ -254,6 +255,7 @@ OBS = \
 	common/format.o \
 	common/gdb_tilde_expand.o \
 	common/gdb_vecs.o \
+	common/netstuff.o \
 	common/new-op.o \
 	common/pathstuff.o \
 	common/print-utils.o \
@@ -290,6 +292,7 @@ GDBREPLAY_OBS = \
 	common/common-exceptions.o \
 	common/common-utils.o \
 	common/errors.o \
+	common/netstuff.o \
 	common/print-utils.o \
 	gdbreplay.o \
 	utils.o \
diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
index a4bc892462..026bbfccaf 100644
--- a/gdb/gdbserver/gdbreplay.c
+++ b/gdb/gdbserver/gdbreplay.c
@@ -46,8 +46,11 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
+#include "netstuff.h"
+
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
@@ -142,56 +145,108 @@ remote_close (void)
 static void
 remote_open (char *name)
 {
-  if (!strchr (name, ':'))
+  char *last_colon = strrchr (name, ':');
+
+  if (last_colon == NULL)
     {
       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
       fflush (stderr);
       exit (1);
     }
-  else
-    {
+
 #ifdef USE_WIN32API
-      static int winsock_initialized;
+  static int winsock_initialized;
 #endif
-      char *port_str;
-      int port;
-      struct sockaddr_in sockaddr;
-      socklen_t tmp;
-      int tmp_desc;
+  char *port_str;
+  int tmp;
+  int tmp_desc;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
 
-      port_str = strchr (name, ':');
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
 
-      port = atoi (port_str + 1);
+  parsed_connection_spec parsed = parse_connection_spec (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
 
 #ifdef USE_WIN32API
-      if (!winsock_initialized)
-	{
-	  WSADATA wsad;
+  if (!winsock_initialized)
+    {
+      WSADATA wsad;
 
-	  WSAStartup (MAKEWORD (1, 0), &wsad);
-	  winsock_initialized = 1;
-	}
+      WSAStartup (MAKEWORD (1, 0), &wsad);
+      winsock_initialized = 1;
+    }
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
-      if (tmp_desc == -1)
-	perror_with_name ("Can't open socket");
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
 
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-		  sizeof (tmp));
+  if (r != 0)
+    {
+      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+	       parsed.host_str.c_str (), parsed.port_str.c_str (),
+	       gai_strerror (r));
+      fflush (stderr);
+      exit (1);
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  struct addrinfo *p;
+
+  for (p = ainfo; p != NULL; p = p->ai_next)
+    {
+      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
 
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      sockaddr.sin_addr.s_addr = INADDR_ANY;
+      if (tmp_desc >= 0)
+	break;
+    }
+
+  if (p == NULL)
+    perror_with_name ("Cannot open socket");
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-	  || listen (tmp_desc, 1))
-	perror_with_name ("Can't bind address");
+  /* Allow rapid reuse of this port. */
+  tmp = 1;
+  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+	      sizeof (tmp));
+
+  switch (p->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+      exit (1);
+    }
+
+  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+    perror_with_name ("Can't bind address");
+
+  if (p->ai_socktype == SOCK_DGRAM)
+    remote_desc = tmp_desc;
+  else
+    {
+      struct sockaddr_storage sockaddr;
+      socklen_t sockaddrsize = sizeof (sockaddr);
+      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+      if (listen (tmp_desc, 1) != 0)
+	perror_with_name ("Can't listen on socket");
+
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+			    &sockaddrsize);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
       if (remote_desc == -1)
 	perror_with_name ("Accept failed");
 
@@ -206,6 +261,16 @@ remote_open (char *name)
       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
 		  (char *) &tmp, sizeof (tmp));
 
+      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+	{
+	  fprintf (stderr, "Remote debugging from host %s, port %s\n",
+		   orig_host, orig_port);
+	  fflush (stderr);
+	}
+
 #ifndef USE_WIN32API
       close (tmp_desc);		/* No longer need this */
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 45d5c8d451..1734c54e39 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -26,6 +26,8 @@
 #include "dll.h"
 #include "rsp-low.h"
 #include "gdbthread.h"
+#include "netstuff.h"
+#include "filestuff.h"
 #include <ctype.h>
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -63,6 +65,7 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
 #if __QNX__
@@ -151,19 +154,18 @@ enable_async_notification (int fd)
 static int
 handle_accept_event (int err, gdb_client_data client_data)
 {
-  struct sockaddr_in sockaddr;
-  socklen_t tmp;
+  struct sockaddr_storage sockaddr;
+  socklen_t len = sizeof (sockaddr);
 
   if (debug_threads)
     debug_printf ("handling possible accept event\n");
 
-  tmp = sizeof (sockaddr);
-  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
+  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
   if (remote_desc == -1)
     perror_with_name ("Accept failed");
 
   /* Enable TCP keep alive process. */
-  tmp = 1;
+  socklen_t tmp = 1;
   setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
 	      (char *) &tmp, sizeof (tmp));
 
@@ -192,8 +194,19 @@ handle_accept_event (int err, gdb_client_data client_data)
   delete_file_handler (listen_desc);
 
   /* Convert IP address to string.  */
-  fprintf (stderr, "Remote debugging from host %s\n",
-	   inet_ntoa (sockaddr.sin_addr));
+  char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV);
+
+  if (r != 0)
+    fprintf (stderr, _("Could not obtain remote address: %s\n"),
+	     gai_strerror (r));
+  else
+    fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
+	     orig_host, orig_port);
 
   enable_async_notification (remote_desc);
 
@@ -222,10 +235,7 @@ remote_prepare (const char *name)
 #ifdef USE_WIN32API
   static int winsock_initialized;
 #endif
-  int port;
-  struct sockaddr_in sockaddr;
   socklen_t tmp;
-  char *port_end;
 
   remote_is_stdio = 0;
   if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
@@ -238,17 +248,25 @@ remote_prepare (const char *name)
       return;
     }
 
-  port_str = strchr (name, ':');
-  if (port_str == NULL)
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_connection_spec parsed
+    = parse_connection_spec_without_prefix (name, &hint);
+
+  if (parsed.port_str.empty ())
     {
       cs.transport_is_reliable = 0;
       return;
     }
 
-  port = strtoul (port_str + 1, &port_end, 10);
-  if (port_str[1] == '\0' || *port_end != '\0')
-    error ("Bad port argument: %s", name);
-
 #ifdef USE_WIN32API
   if (!winsock_initialized)
     {
@@ -259,8 +277,26 @@ remote_prepare (const char *name)
     }
 #endif
 
-  listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (listen_desc == -1)
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
+
+  if (r != 0)
+    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+
+  scoped_free_addrinfo freeaddrinfo (ainfo);
+
+  struct addrinfo *iter;
+
+  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+    {
+      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+					iter->ai_protocol);
+
+      if (listen_desc >= 0)
+	break;
+    }
+
+  if (iter == NULL)
     perror_with_name ("Can't open socket");
 
   /* Allow rapid reuse of this port. */
@@ -268,14 +304,25 @@ remote_prepare (const char *name)
   setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
 	      sizeof (tmp));
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  sockaddr.sin_addr.s_addr = INADDR_ANY;
+  switch (iter->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
+    }
 
-  if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-      || listen (listen_desc, 1))
+  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
     perror_with_name ("Can't bind address");
 
+  if (listen (listen_desc, 1) != 0)
+    perror_with_name ("Can't listen on socket");
+
   cs.transport_is_reliable = 1;
 }
 
@@ -350,18 +397,24 @@ remote_open (const char *name)
 #endif /* USE_WIN32API */
   else
     {
-      int port;
-      socklen_t len;
-      struct sockaddr_in sockaddr;
-
-      len = sizeof (sockaddr);
-      if (getsockname (listen_desc,
-		       (struct sockaddr *) &sockaddr, &len) < 0
-	  || len < sizeof (sockaddr))
+      char listen_port[GDB_NI_MAX_PORT];
+      struct sockaddr_storage sockaddr;
+      socklen_t len = sizeof (sockaddr);
+
+      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
 	perror_with_name ("Can't determine port");
-      port = ntohs (sockaddr.sin_port);
 
-      fprintf (stderr, "Listening on port %d\n", port);
+      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+			   NULL, 0,
+			   listen_port, sizeof (listen_port),
+			   NI_NUMERICSERV);
+
+      if (r != 0)
+	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
+		 gai_strerror (r));
+      else
+	fprintf (stderr, _("Listening on port %s\n"), listen_port);
+
       fflush (stderr);
 
       /* Register the event loop handler.  */
diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index 23ef3b04b8..fdb3afefbf 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -25,6 +25,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
 #include "filestuff.h"
+#include "netstuff.h"
 
 #include <sys/types.h>
 
@@ -39,6 +40,7 @@
 
 #ifdef USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #ifndef ETIMEDOUT
 #define ETIMEDOUT WSAETIMEDOUT
 #endif
@@ -81,12 +83,13 @@ static unsigned int tcp_retry_limit = 15;
 
 #define POLL_INTERVAL 5
 
-/* Helper function to wait a while.  If SCB is non-null, wait on its
-   file descriptor.  Otherwise just wait on a timeout, updating *POLLS.
-   Returns -1 on timeout or interrupt, otherwise the value of select.  */
+/* Helper function to wait a while.  If SOCK is defined, wait on its
+   file descriptor.  Otherwise just wait on a timeout, updating
+   *POLLS.  Returns -1 on timeout or interrupt, otherwise the value of
+   select.  */
 
 static int
-wait_for_connect (struct serial *scb, unsigned int *polls)
+wait_for_connect (gdb::optional<int> sock, unsigned int *polls)
 {
   struct timeval t;
   int n;
@@ -120,24 +123,24 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
       t.tv_usec = 0;
     }
 
-  if (scb)
+  if (sock)
     {
       fd_set rset, wset, eset;
 
       FD_ZERO (&rset);
-      FD_SET (scb->fd, &rset);
+      FD_SET (*sock, &rset);
       wset = rset;
       eset = rset;
-	  
+
       /* POSIX systems return connection success or failure by signalling
 	 wset.  Windows systems return success in wset and failure in
 	 eset.
-     
+
 	 We must call select here, rather than gdb_select, because
 	 the serial structure has not yet been initialized - the
 	 MinGW select wrapper will not know that this FD refers
 	 to a socket.  */
-      n = select (scb->fd + 1, &rset, &wset, &eset, &t);
+      n = select (*sock + 1, &rset, &wset, &eset, &t);
     }
   else
     /* Use gdb_select here, since we have no file descriptors, and on
@@ -153,80 +156,29 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
   return n;
 }
 
-/* Open a tcp socket.  */
+/* Try to connect to the host represented by AINFO.  If the connection
+   succeeds, return its socket.  If we get a ECONNREFUSED error,
+   return -1.  Otherwise, return nothing.  POLLS is used when
+   'connect' returns EINPROGRESS, and we need to invoke
+   'wait_for_connect' to obtain the status.  */
 
-int
-net_open (struct serial *scb, const char *name)
+static gdb::optional<int>
+try_connect (const struct addrinfo *ainfo, unsigned int *polls)
 {
-  char hostname[100];
-  const char *port_str;
-  int n, port, tmp;
-  int use_udp;
-  struct hostent *hostent;
-  struct sockaddr_in sockaddr;
-#ifdef USE_WIN32API
-  u_long ioarg;
-#else
-  int ioarg;
-#endif
-  unsigned int polls = 0;
-
-  use_udp = 0;
-  if (startswith (name, "udp:"))
-    {
-      use_udp = 1;
-      name = name + 4;
-    }
-  else if (startswith (name, "tcp:"))
-    name = name + 4;
-
-  port_str = strchr (name, ':');
-
-  if (!port_str)
-    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
-						       happen.  */
-
-  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
-  strncpy (hostname, name, tmp);	/* Don't want colon.  */
-  hostname[tmp] = '\000';	/* Tie off host name.  */
-  port = atoi (port_str + 1);
-
-  /* Default hostname is localhost.  */
-  if (!hostname[0])
-    strcpy (hostname, "localhost");
-
-  hostent = gethostbyname (hostname);
-  if (!hostent)
-    {
-      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
-      errno = ENOENT;
-      return -1;
-    }
+  int sock = gdb_socket_cloexec (ainfo->ai_family, ainfo->ai_socktype,
+				 ainfo->ai_protocol);
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
-	  sizeof (struct in_addr));
+  if (sock < 0)
+    return {};
 
- retry:
-
-  if (use_udp)
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
-  else
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
-
-  if (scb->fd == -1)
-    return -1;
-  
   /* Set socket nonblocking.  */
-  ioarg = 1;
-  ioctl (scb->fd, FIONBIO, &ioarg);
+  int ioarg = 1;
+
+  ioctl (sock, FIONBIO, &ioarg);
 
   /* Use Non-blocking connect.  connect() will return 0 if connected
      already.  */
-  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
-
-  if (n < 0)
+  if (connect (sock, ainfo->ai_addr, ainfo->ai_addrlen) < 0)
     {
 #ifdef USE_WIN32API
       int err = WSAGetLastError();
@@ -234,90 +186,202 @@ net_open (struct serial *scb, const char *name)
       int err = errno;
 #endif
 
-      /* Maybe we're waiting for the remote target to become ready to
-	 accept connections.  */
-      if (tcp_auto_retry
+      /* If we've got a "connection refused" error, just return
+	 -1.  The caller will know what to do.  */
+      if (
 #ifdef USE_WIN32API
-	  && err == WSAECONNREFUSED
+	  err == WSAECONNREFUSED
 #else
-	  && err == ECONNREFUSED
+	  err == ECONNREFUSED
 #endif
-	  && wait_for_connect (NULL, &polls) >= 0)
+	  )
 	{
-	  close (scb->fd);
-	  goto retry;
+	  close (sock);
+	  return -1;
 	}
 
       if (
 #ifdef USE_WIN32API
-	  /* Under Windows, calling "connect" with a non-blocking socket
-	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
+	  /* Any other error (except EINPROGRESS) will be "swallowed"
+	     here.  We return without specifying a return value, and
+	     set errno if the caller wants to inspect what
+	     happened.  */
 	  err != WSAEWOULDBLOCK
 #else
 	  err != EINPROGRESS
 #endif
 	  )
 	{
+	  close (sock);
 	  errno = err;
-	  net_close (scb);
-	  return -1;
+	  return {};
 	}
 
       /* Looks like we need to wait for the connect.  */
-      do 
-	{
-	  n = wait_for_connect (scb, &polls);
-	} 
+      int n;
+
+      do
+	n = wait_for_connect (sock, polls);
       while (n == 0);
+
       if (n < 0)
 	{
-	  net_close (scb);
+	  /* A negative value here means that we either timed out or
+	     got interrupted by the user.  Just return.  */
+	  close (sock);
 	  return -1;
 	}
     }
 
   /* Got something.  Is it an error?  */
-  {
-    int res, err;
-    socklen_t len;
-
-    len = sizeof (err);
-    /* On Windows, the fourth parameter to getsockopt is a "char *";
-       on UNIX systems it is generally "void *".  The cast to "char *"
-       is OK everywhere, since in C++ any data pointer type can be
-       implicitly converted to "void *".  */
-    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
-    if (res < 0 || err)
-      {
-	/* Maybe the target still isn't ready to accept the connection.  */
-	if (tcp_auto_retry
+  int err;
+  socklen_t len = sizeof (err);
+
+  /* On Windows, the fourth parameter to getsockopt is a "char *";
+     on UNIX systems it is generally "void *".  The cast to "char *"
+     is OK everywhere, since in C++ any data pointer type can be
+     implicitly converted to "void *".  */
+  int ret = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
+
+  if (ret < 0)
+    {
+      close (sock);
+      errno = ret;
+      return {};
+    }
+  else if (ret == 0 && err != 0)
+    {
+      close (sock);
+      errno = err;
+
+      /* Check if the connection was refused.  */
+      if (
 #ifdef USE_WIN32API
-	    && err == WSAECONNREFUSED
+	  err == WSAECONNREFUSED
 #else
-	    && err == ECONNREFUSED
+	  err == ECONNREFUSED
 #endif
-	    && wait_for_connect (NULL, &polls) >= 0)
-	  {
-	    close (scb->fd);
-	    goto retry;
-	  }
-	if (err)
-	  errno = err;
-	net_close (scb);
+	  )
 	return -1;
-      }
-  } 
+      else
+	{
+	  /* If we have any other kind of error, just return nothing.  */
+	  return {};
+	}
+    }
+
+  /* The connection succeeded.  Return the socket.  */
+  return sock;
+}
+
+/* Open a tcp socket.  */
+
+int
+net_open (struct serial *scb, const char *name)
+{
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_connection_spec parsed = parse_connection_spec (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
+
+  int r = getaddrinfo (parsed.host_str.c_str (),
+		       parsed.port_str.c_str (),
+		       &hint, &ainfo);
+
+  if (r != 0)
+    {
+      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
+			  name, gai_strerror (r));
+      errno = ENOENT;
+      return -1;
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  /* Flag to indicate whether we've got a connection refused.  It will
+     be true if any of the connections tried was refused.  */
+  bool got_connrefused;
+  /* If a connection succeeeds, SUCCESS_AINFO will point to the
+     'struct addrinfo' that succeed.  */
+  struct addrinfo *success_ainfo = NULL;
+  unsigned int polls = 0;
+
+  /* Assume the worst.  */
+  scb->fd = -1;
+
+  do
+    {
+      got_connrefused = false;
+
+      for (struct addrinfo *iter = ainfo; iter != NULL; iter = iter->ai_next)
+	{
+	  /* Iterate over the list of possible addresses to connect
+	     to.  For each, we'll try to connect and see if it
+	     succeeds.  */
+	  gdb::optional<int> sock = try_connect (iter, &polls);
+
+	  if (sock)
+	    {
+	      if (*sock == -1)
+		{
+		  /* The special value "-1" means that there was an
+		     ECONNREFUSED while trying to connect.  */
+		  got_connrefused = true;
+		}
+	      else
+		{
+		  /* We've gotten a successful connection.  Save its
+		     'struct addrinfo', the socket, and break.  */
+		  success_ainfo = iter;
+		  scb->fd = *sock;
+		  break;
+		}
+	    }
+	}
+    }
+  /* Just retry if:
+
+     - tcp_auto_retry is true, and
+     - We haven't gotten a connection yet, and
+     - Any of our connection attempts returned with ECONNREFUSED, and
+     - wait_for_connect signals that we can keep going.  */
+  while (tcp_auto_retry
+	 && success_ainfo == NULL
+	 && got_connrefused
+	 && wait_for_connect ({}, &polls) >= 0);
+
+  if (success_ainfo == NULL)
+    {
+      net_close (scb);
+      return -1;
+    }
 
   /* Turn off nonblocking.  */
-  ioarg = 0;
+#ifdef USE_WIN32API
+  u_long ioarg = 0;
+#else
+  int ioarg = 0;
+#endif
+
   ioctl (scb->fd, FIONBIO, &ioarg);
 
-  if (use_udp == 0)
+  if (success_ainfo->ai_socktype == IPPROTO_TCP)
     {
       /* Disable Nagle algorithm.  Needed in some cases.  */
-      tmp = 1;
+      int tmp = 1;
+
       setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
-		  (char *)&tmp, sizeof (tmp));
+		  (char *) &tmp, sizeof (tmp));
     }
 
 #ifdef SIGPIPE
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 4475ac21a9..55abfb3254 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -259,6 +259,20 @@ This make (not runtest) variable is used to specify whether the
 testsuite preloads the read1.so library into expect.  Any non-empty
 value means true.  See "Race detection" below.
 
+GDB_TEST_SOCKETHOST
+
+This variable can provide the hostname/address that should be used
+when performing GDBserver-related tests.  This is useful in some
+situations, e.g., when you want to test the IPv6 connectivity of GDB
+and GDBserver, or when using a different hostname/address is needed.
+For example, to make GDB and GDBserver use IPv6-only connections, you
+can do:
+
+	make check TESTS="gdb.server/*.exp" RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'
+
+Note that only a hostname/address can be provided, without a port
+number.
+
 Race detection
 **************
 
diff --git a/gdb/testsuite/boards/native-extended-gdbserver.exp b/gdb/testsuite/boards/native-extended-gdbserver.exp
index df949994fd..482e4e3c14 100644
--- a/gdb/testsuite/boards/native-extended-gdbserver.exp
+++ b/gdb/testsuite/boards/native-extended-gdbserver.exp
@@ -24,8 +24,6 @@ load_generic_config "extended-gdbserver"
 load_board_description "gdbserver-base"
 load_board_description "local-board"
 
-set_board_info sockethost "localhost:"
-
 # We will be using the extended GDB remote protocol.
 set_board_info gdb_protocol "extended-remote"
 
diff --git a/gdb/testsuite/boards/native-gdbserver.exp b/gdb/testsuite/boards/native-gdbserver.exp
index ef9316007e..1dee3df4f1 100644
--- a/gdb/testsuite/boards/native-gdbserver.exp
+++ b/gdb/testsuite/boards/native-gdbserver.exp
@@ -30,7 +30,6 @@ set_board_info gdb,do_reload_on_run 1
 # There's no support for argument-passing (yet).
 set_board_info noargs 1
 
-set_board_info sockethost "localhost:"
 set_board_info use_gdb_stub 1
 set_board_info exit_is_reliable 1
 
diff --git a/gdb/testsuite/gdb.server/run-without-local-binary.exp b/gdb/testsuite/gdb.server/run-without-local-binary.exp
index 1665ca9912..6ba3e711d9 100644
--- a/gdb/testsuite/gdb.server/run-without-local-binary.exp
+++ b/gdb/testsuite/gdb.server/run-without-local-binary.exp
@@ -53,7 +53,7 @@ save_vars { GDBFLAGS } {
     set use_gdb_stub 0
 
     gdb_test "target ${gdbserver_protocol} ${gdbserver_gdbport}" \
-	"Remote debugging using $gdbserver_gdbport" \
+	"Remote debugging using [string_to_regexp $gdbserver_gdbport]" \
 	"connect to gdbserver"
 
     gdb_test "run" \
diff --git a/gdb/testsuite/gdb.server/server-connect.exp b/gdb/testsuite/gdb.server/server-connect.exp
new file mode 100644
index 0000000000..e8e96d2d8a
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-connect.exp
@@ -0,0 +1,111 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# 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/>.
+
+# Test multiple types of connection (IPv4, IPv6, TCP, UDP) and make
+# sure both gdbserver and GDB work.
+
+load_lib gdbserver-support.exp
+
+standard_testfile normal.c
+
+if {[skip_gdbserver_tests]} {
+    return 0
+}
+
+# We want to have control over where we start gdbserver.
+if { [is_remote target] } {
+    return 0
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+# Make sure we're disconnected, in case we're testing with an
+# extended-remote board, therefore already connected.
+gdb_test "disconnect" ".*"
+
+set target_exec [gdbserver_download_current_prog]
+
+# An array containing the test instructions for each scenario.  The
+# description of each field is as follows:
+#
+# - The connection specification to be used when starting
+#   gdbserver/GDB.  This string will be used to set the
+#   GDB_TEST_SOCKETHOST when calling gdbserver_start.
+#
+# - A flag indicating whether gdbserver should fail when we attempt to
+#   start it.  Useful when testing erroneous connection specs such as
+#   "tcp8:".
+#
+# - The prefix that should be prepended to the test messages.
+set test_params \
+    { \
+	  { "tcp4:127.0.0.1" 0 "tcp4" } \
+	  { "tcp6:::1"       0 "tcp6" } \
+	  { "tcp6:[::1]"     0 "tcp6-with-brackets" } \
+	  { "tcp:localhost"  0 "tcp" } \
+	  { "udp4:127.0.0.1" 0 "udp4" } \
+	  { "udp6:::1"       0 "udp6" } \
+	  { "udp6:[::1]"     0 "udp6-with-brackets" } \
+	  { "tcp8:123"       1 "tcp8" } \
+	  { "udp123:::"      1 "udp123" } \
+	  { "garbage:1234"   1 "garbage:1234" } \
+    }
+
+# The best way to test different types of connections is to set the
+# GDB_TEST_SOCKETHOST variable accordingly.
+save_vars { GDB_TEST_SOCKETHOST } {
+    foreach line $test_params {
+	set sockhost [lindex $line 0]
+	set gdbserver_should_fail [lindex $line 1]
+	set prefix [lindex $line 2]
+
+	with_test_prefix $prefix {
+	    set GDB_TEST_SOCKETHOST $sockhost
+	    set test "start gdbserver"
+
+	    # Try to start gdbserver.
+	    set catchres [catch {set res [gdbserver_start "" $target_exec]} errmsg]
+
+	    if { $catchres != 0 } {
+		if { $gdbserver_should_fail } {
+		    pass "$test: gdbserver failed as expected"
+		} else {
+		    fail "$test: $errmsg"
+		}
+		continue
+	    } else {
+		if { $gdbserver_should_fail } {
+		    fail "$test: gdbserver should fail but did not"
+		} else {
+		    pass "$test"
+		}
+	    }
+
+	    set gdbserver_protocol [lindex $res 0]
+	    set gdbserver_gdbport [lindex $res 1]
+	    set test "connect to gdbserver using $sockhost"
+
+	    if { [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0 } {
+		pass $test
+	    } else {
+		fail $test
+	    }
+	}
+    }
+}
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 46e4f77922..3a939e8cda 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -211,7 +211,7 @@ proc gdbserver_default_get_remote_address { host port } {
 # Default routine to compute the "comm" argument for gdbserver.
 
 proc gdbserver_default_get_comm_port { port } {
-    return ":$port"
+    return "$port"
 }
 
 # Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
@@ -221,6 +221,7 @@ proc gdbserver_default_get_comm_port { port } {
 
 proc gdbserver_start { options arguments } {
     global portnum
+    global GDB_TEST_SOCKETHOST
 
     # Port id -- either specified in baseboard file, or managed here.
     if [target_info exists gdb,socketport] {
@@ -231,10 +232,22 @@ proc gdbserver_start { options arguments } {
     }
 
     # Extract the local and remote host ids from the target board struct.
-    if [target_info exists sockethost] {
+    if { [info exists GDB_TEST_SOCKETHOST] } {
+	# The user is not supposed to provide a port number, just a
+	# hostname/address, therefore we add the trailing ":" here.
+	set debughost "${GDB_TEST_SOCKETHOST}:"
+	# Espace open and close square brackets.
+	set debughost_tmp [string map { [ \\[ ] \\] } $debughost]
+	# We need a "gdbserver" version of the debughost, which will
+	# have the possible connection prefix stripped.  This is
+	# because gdbserver currently doesn't recognize the prefixes.
+	regsub -all "^\(tcp:|udp:|tcp4:|udp4:|tcp6:|udp6:\)" $debughost_tmp "" debughost_gdbserver
+    } elseif [target_info exists sockethost] {
 	set debughost [target_info sockethost]
+	set debughost_gdbserver $debughost
     } else {
 	set debughost "localhost:"
+	set debughost_gdbserver $debughost
     }
 
     # Some boards use a different value for the port that is passed to
@@ -277,8 +290,14 @@ proc gdbserver_start { options arguments } {
 	if { $options != "" } {
 	    append gdbserver_command " $options"
 	}
+	if { $debughost_gdbserver != "" } {
+	    append gdbserver_command " $debughost_gdbserver"
+	}
 	if { $portnum != "" } {
-	    append gdbserver_command " [$get_comm_port $portnum]"
+	    if { $debughost_gdbserver == "" } {
+		append gdbserver_command " "
+	    }
+	    append gdbserver_command "[$get_comm_port $portnum]"
 	}
 	if { $arguments != "" } {
 	    append gdbserver_command " $arguments"
@@ -307,6 +326,9 @@ proc gdbserver_start { options arguments } {
 		    continue
 		}
 	    }
+	    -re ".*: cannot resolve name: Name or service not known\r\n" {
+		error "gdbserver cannot resolve name."
+	    }
 	    timeout {
 		error "Timeout waiting for gdbserver response."
 	    }
diff --git a/gdb/unittests/parse-connection-spec-selftests.c b/gdb/unittests/parse-connection-spec-selftests.c
new file mode 100644
index 0000000000..10155ae133
--- /dev/null
+++ b/gdb/unittests/parse-connection-spec-selftests.c
@@ -0,0 +1,251 @@
+/* Self tests for parsing connection specs for GDB, the GNU debugger.
+
+   Copyright (C) 2017-2018 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 "defs.h"
+#include "selftest.h"
+#include "common/netstuff.h"
+#include "diagnostics.h"
+#ifdef USE_WIN32API
+#include <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+namespace selftests {
+namespace parse_connection_spec_tests {
+
+/* Auxiliary struct that holds info about a specific test for a
+   connection spec.  */
+
+struct parse_conn_test
+{
+  /* The connection spec.  */
+  const char *connspec;
+
+  /* Expected result from 'parse_connection_spec'.  */
+  parsed_connection_spec expected_result;
+
+  /* True if this test should fail, false otherwise.  If true, only
+     the CONNSPEC field should be considered as valid.  */
+  bool should_fail;
+
+  /* The expected AI_FAMILY to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_family;
+
+  /* The expected AI_SOCKTYPE to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_socktype;
+
+  /* The expected AI_PROTOCOL to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_protocol;
+};
+
+/* Some defines to help us fill a 'struct parse_conn_test'.  */
+
+/* Initialize a full entry.  */
+#define INIT_ENTRY(ADDR, EXP_HOST, EXP_PORT, SHOULD_FAIL, EXP_AI_FAMILY, \
+		   EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL)			\
+  { ADDR, { EXP_HOST, EXP_PORT }, SHOULD_FAIL, EXP_AI_FAMILY, \
+    EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL }
+
+/* Initialized an unprefixed entry.  In this case, we don't expect
+   nothing on the 'struct addrinfo' HINT.  */
+#define INIT_UNPREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, 0, 0, 0)
+
+/* Initialized an unprefixed IPv6 entry.  In this case, we don't
+   expect nothing on the 'struct addrinfo' HINT.  */
+#define INIT_UNPREFIXED_IPV6_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, AF_INET6, 0, 0)
+
+/* Initialize a prefixed entry.  */
+#define INIT_PREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT, EXP_AI_FAMILY, \
+			    EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, EXP_AI_FAMILY, \
+	      EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL)
+
+/* Initialize an entry prefixed with "tcp4:".  */
+#define INIT_PREFIXED_IPV4_TCP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET, SOCK_STREAM, \
+		       IPPROTO_TCP)
+
+/* Initialize an entry prefixed with "tcp6:".  */
+#define INIT_PREFIXED_IPV6_TCP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET6, SOCK_STREAM, \
+		       IPPROTO_TCP)
+
+/* Initialize an entry prefixed with "udp4:".  */
+#define INIT_PREFIXED_IPV4_UDP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET, SOCK_DGRAM, \
+		       IPPROTO_UDP)
+
+/* Initialize an entry prefixed with "udp6:".  */
+#define INIT_PREFIXED_IPV6_UDP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET6, SOCK_DGRAM, \
+		       IPPROTO_UDP)
+
+/* Initialize a bogus entry, i.e., a connection spec that should
+   fail.  */
+#define INIT_BOGUS_ENTRY(ADDR) \
+  INIT_ENTRY (ADDR, "", "", true, 0, 0, 0)
+
+/* The variable which holds all of our tests.  */
+
+static const parse_conn_test conn_test[] =
+  {
+    /* Unprefixed addresses.  */
+
+    /* IPv4, host and port present.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* IPv4, only host.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1", "127.0.0.1", ""),
+    /* IPv4, missing port.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1:", "127.0.0.1", ""),
+
+    /* IPv6, host and port present, no brackets.  */
+    INIT_UNPREFIXED_ENTRY ("::1:1234", "::1", "1234"),
+    /* IPv6, only host, no brackets.  */
+    // INIT_UNPREFIXED_ENTRY ("::1", "::1", ""),
+    /* IPv6, missing port, no brackets.  */
+    INIT_UNPREFIXED_ENTRY ("::1:", "::1", ""),
+    /* IPv6, host and port present, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]:1234", "::1", "1234"),
+    /* IPv6, only host, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]", "::1", ""),
+    /* IPv6, missing port, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]:", "::1", ""),
+
+    /* Unspecified, only port.  */
+    INIT_UNPREFIXED_ENTRY (":1234", "localhost", "1234"),
+
+    /* Prefixed addresses.  */
+
+    /* Prefixed "tcp4:" IPv4, host and port presents.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* Prefixed "tcp4:" IPv4, only port.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4::1234", "localhost", "1234"),
+    /* Prefixed "tcp4:" IPv4, only host.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1", "127.0.0.1", ""),
+    /* Prefixed "tcp4:" IPv4, missing port.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1:", "127.0.0.1", ""),
+
+    /* Prefixed "udp4:" IPv4, host and port presents.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* Prefixed "udp4:" IPv4, only port.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4::1234", "localhost", "1234"),
+    /* Prefixed "udp4:" IPv4, only host.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1", "127.0.0.1", ""),
+    /* Prefixed "udp4:" IPv4, missing port.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1:", "127.0.0.1", ""),
+
+
+    /* Prefixed "tcp6:" IPv6, host and port presents.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:::1:1234", "::1", "1234"),
+    /* Prefixed "tcp6:" IPv6, only port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6::1234", "localhost", "1234"),
+    /* Prefixed "tcp6:" IPv6, only host.  */
+    //INIT_PREFIXED_IPV6_TCP ("tcp6:::1", "::1", ""),
+    /* Prefixed "tcp6:" IPv6, missing port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:::1:", "::1", ""),
+
+    /* Prefixed "udp6:" IPv6, host and port presents.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:::1:1234", "::1", "1234"),
+    /* Prefixed "udp6:" IPv6, only port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6::1234", "localhost", "1234"),
+    /* Prefixed "udp6:" IPv6, only host.  */
+    //INIT_PREFIXED_IPV6_UDP ("udp6:::1", "::1", ""),
+    /* Prefixed "udp6:" IPv6, missing port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:::1:", "::1", ""),
+
+    /* Prefixed "tcp6:" IPv6 with brackets, host and port presents.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]:1234", "::1", "1234"),
+    /* Prefixed "tcp6:" IPv6 with brackets, only host.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]", "::1", ""),
+    /* Prefixed "tcp6:" IPv6 with brackets, missing port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]:", "::1", ""),
+
+    /* Prefixed "udp6:" IPv6 with brackets, host and port presents.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]:1234", "::1", "1234"),
+    /* Prefixed "udp6:" IPv6 with brackets, only host.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]", "::1", ""),
+    /* Prefixed "udp6:" IPv6 with brackets, missing port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]:", "::1", ""),
+
+
+    /* Bogus addresses.  */
+    INIT_BOGUS_ENTRY ("tcp6:[::1]123:44"),
+    INIT_BOGUS_ENTRY ("[::1"),
+    INIT_BOGUS_ENTRY ("tcp6:::1]:"),
+  };
+
+/* Test a connection spec C.  */
+
+static void
+test_conn (const parse_conn_test &c)
+{
+  struct addrinfo hint;
+  parsed_connection_spec ret;
+
+  memset (&hint, 0, sizeof (hint));
+
+  TRY
+    {
+      ret = parse_connection_spec (c.connspec, &hint);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* If we caught an error, we should check if this connection
+	 spec was supposed to fail.  */
+      SELF_CHECK (c.should_fail);
+      return;
+    }
+  END_CATCH
+
+  SELF_CHECK (!c.should_fail);
+  SELF_CHECK (ret.host_str == c.expected_result.host_str);
+  SELF_CHECK (ret.port_str == c.expected_result.port_str);
+  SELF_CHECK (hint.ai_family == c.exp_ai_family);
+  SELF_CHECK (hint.ai_socktype == c.exp_ai_socktype);
+  SELF_CHECK (hint.ai_protocol == c.exp_ai_protocol);
+}
+
+/* Run the tests associated with parsing connection specs.  */
+
+static void
+run_tests ()
+{
+  for (const parse_conn_test &c : conn_test)
+    test_conn (c);
+}
+} /* namespace parse_connection_spec_tests */
+} /* namespace selftests */
+
+void
+_initialize_parse_connection_spec_selftests ()
+{
+  selftests::register_test ("parse_connection_spec",
+			    selftests::parse_connection_spec_tests::run_tests);
+}
-- 
2.14.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] Implement IPv6 support for GDB/gdbserver
  2018-07-07 20:47 ` [PATCH v3] " Sergio Durigan Junior
@ 2018-07-11 12:55   ` Pedro Alves
  2018-07-11 19:13     ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-07-11 12:55 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki,
	Armand Scholtes

Hi,

This version looks mostly good to me.  Only one thing raised
my eyebrow.  I also noticed a few typos here and there.
See below.

On 07/07/2018 09:47 PM, Sergio Durigan Junior wrote:

> -/* Open a tcp socket.  */
> +/* Try to connect to the host represented by AINFO.  If the connection
> +   succeeds, return its socket.  If we get a ECONNREFUSED error,
> +   return -1.  Otherwise, return nothing.  POLLS is used when
> +   'connect' returns EINPROGRESS, and we need to invoke
> +   'wait_for_connect' to obtain the status.  */
>  
> -int
> -net_open (struct serial *scb, const char *name)
> +static gdb::optional<int>
> +try_connect (const struct addrinfo *ainfo, unsigned int *polls)
>  {

I find this use of gdb::optional<int> more confusing than
helpful, since it creates multiple "levels" of places
to check error.  You have:

 #1 - empty optional, check errno for error [1]
 #2 - non-empty optional, value == -1, means ECONNREFUSED.
 #3 - non-empty optional, value is socket.

([1] the errno part is missing in the function's description)

Above, #1 and #2 seem redundant?

You can instead make the return type plain int, return -1 on
all errors, and the caller can check errno for ECONNREFUSED.

(If we were going to avoid errno, I think something like
std::expected would be better than std/gdb::optional.  std::optional
for a type that already has a nullable state (in this case -1)
tends to be confusing, IMO.  See here for example:
https://www.boost.org/doc/libs/1_61_0/libs/optional/doc/html/boost_optional/tutorial/when_to_use_optional.html
)

Notice how your patch left this case incorrectly returning -1:

      if (n < 0)
	{
	  /* A negative value here means that we either timed out or
	     got interrupted by the user.  Just return.  */
	  close (sock);
	  return -1;
	}

> -    if [target_info exists sockethost] {
> +    if { [info exists GDB_TEST_SOCKETHOST] } {
> +	# The user is not supposed to provide a port number, just a
> +	# hostname/address, therefore we add the trailing ":" here.
> +	set debughost "${GDB_TEST_SOCKETHOST}:"
> +	# Espace open and close square brackets.

Typo: "Espace" -> "Escape"

> +/* Initialized an unprefixed entry.  In this case, we don't expect
> +   nothing on the 'struct addrinfo' HINT.  */
> +#define INIT_UNPREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
> +  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, 0, 0, 0)

Typo: "Initialized" -> "Initialize".

Also, double negatives "don't expect nothing" don't work well in English.

Do you mean "don't expect anything", or "expect nothing" ?
(There's a difference: the former doesn't expect it but
tolerates, the latter really requires nothing.)

> +
> +/* Initialized an unprefixed IPv6 entry.  In this case, we don't
> +   expect nothing on the 'struct addrinfo' HINT.  */
> +#define INIT_UNPREFIXED_IPV6_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
> +  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, AF_INET6, 0, 0)

Same comments apply here.

> +
> +    /* IPv6, host and port present, no brackets.  */
> +    INIT_UNPREFIXED_ENTRY ("::1:1234", "::1", "1234"),
> +    /* IPv6, only host, no brackets.  */
> +    // INIT_UNPREFIXED_ENTRY ("::1", "::1", ""),

Leftover, remove?

> +    /* IPv6, missing port, no brackets.  */
> +    INIT_UNPREFIXED_ENTRY ("::1:", "::1", ""),

> +    /* Prefixed "tcp4:" IPv4, host and port presents.  */

Typo: "presents" -> "present".  This appears in several places.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v3] Implement IPv6 support for GDB/gdbserver
  2018-07-11 12:55   ` Pedro Alves
@ 2018-07-11 19:13     ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-07-11 19:13 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes

On Wednesday, July 11 2018, Pedro Alves wrote:

> Hi,
>
> This version looks mostly good to me.  Only one thing raised
> my eyebrow.  I also noticed a few typos here and there.
> See below.
>
> On 07/07/2018 09:47 PM, Sergio Durigan Junior wrote:
>
>> -/* Open a tcp socket.  */
>> +/* Try to connect to the host represented by AINFO.  If the connection
>> +   succeeds, return its socket.  If we get a ECONNREFUSED error,
>> +   return -1.  Otherwise, return nothing.  POLLS is used when
>> +   'connect' returns EINPROGRESS, and we need to invoke
>> +   'wait_for_connect' to obtain the status.  */
>>  
>> -int
>> -net_open (struct serial *scb, const char *name)
>> +static gdb::optional<int>
>> +try_connect (const struct addrinfo *ainfo, unsigned int *polls)
>>  {
>
> I find this use of gdb::optional<int> more confusing than
> helpful, since it creates multiple "levels" of places
> to check error.  You have:
>
>  #1 - empty optional, check errno for error [1]
>  #2 - non-empty optional, value == -1, means ECONNREFUSED.
>  #3 - non-empty optional, value is socket.
>
> ([1] the errno part is missing in the function's description)
>
> Above, #1 and #2 seem redundant?
>
> You can instead make the return type plain int, return -1 on
> all errors, and the caller can check errno for ECONNREFUSED.
>
> (If we were going to avoid errno, I think something like
> std::expected would be better than std/gdb::optional.  std::optional
> for a type that already has a nullable state (in this case -1)
> tends to be confusing, IMO.  See here for example:
> https://www.boost.org/doc/libs/1_61_0/libs/optional/doc/html/boost_optional/tutorial/when_to_use_optional.html
> )
>
> Notice how your patch left this case incorrectly returning -1:
>
>       if (n < 0)
> 	{
> 	  /* A negative value here means that we either timed out or
> 	     got interrupted by the user.  Just return.  */
> 	  close (sock);
> 	  return -1;
> 	}

No problem.  I've changed the code to use plain int now.

>> -    if [target_info exists sockethost] {
>> +    if { [info exists GDB_TEST_SOCKETHOST] } {
>> +	# The user is not supposed to provide a port number, just a
>> +	# hostname/address, therefore we add the trailing ":" here.
>> +	set debughost "${GDB_TEST_SOCKETHOST}:"
>> +	# Espace open and close square brackets.
>
> Typo: "Espace" -> "Escape"

Fixed.

>> +/* Initialized an unprefixed entry.  In this case, we don't expect
>> +   nothing on the 'struct addrinfo' HINT.  */
>> +#define INIT_UNPREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
>> +  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, 0, 0, 0)
>
> Typo: "Initialized" -> "Initialize".

Fixed.

> Also, double negatives "don't expect nothing" don't work well in English.
>
> Do you mean "don't expect anything", or "expect nothing" ?
> (There's a difference: the former doesn't expect it but
> tolerates, the latter really requires nothing.)

We don't really care about HINT, so "don't expect anything" is the
proper one to use here.  Fixed.

>> +
>> +/* Initialized an unprefixed IPv6 entry.  In this case, we don't
>> +   expect nothing on the 'struct addrinfo' HINT.  */
>> +#define INIT_UNPREFIXED_IPV6_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
>> +  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, AF_INET6, 0, 0)
>
> Same comments apply here.

Fixed.

>> +
>> +    /* IPv6, host and port present, no brackets.  */
>> +    INIT_UNPREFIXED_ENTRY ("::1:1234", "::1", "1234"),
>> +    /* IPv6, only host, no brackets.  */
>> +    // INIT_UNPREFIXED_ENTRY ("::1", "::1", ""),
>
> Leftover, remove?

Indeed, removed.

>> +    /* IPv6, missing port, no brackets.  */
>> +    INIT_UNPREFIXED_ENTRY ("::1:", "::1", ""),
>
>> +    /* Prefixed "tcp4:" IPv4, host and port presents.  */
>
> Typo: "presents" -> "present".  This appears in several places.

Fixed all of them.

I'll send a v4 soon.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v4] Implement IPv6 support for GDB/gdbserver
  2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
                   ` (4 preceding siblings ...)
  2018-07-07 20:47 ` [PATCH v3] " Sergio Durigan Junior
@ 2018-07-11 19:16 ` Sergio Durigan Junior
  2018-07-11 21:48   ` Pedro Alves
  5 siblings, 1 reply; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-07-11 19:16 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes, Sergio Durigan Junior

Changes since v3:

- No longer use gdb::optional as a return type for try_connect; use
  int instead.

- Fix a bunch of typos and thinkos.


This patch implements IPv6 support for both GDB and gdbserver.  Based
on my research, it is the fourth attempt to do that since 2006.  Since
I used ideas from all of the previous patches, I also added their
authors's names on the ChangeLogs as a way to recognize their
efforts.  For reference sake, you can find the previous attempts at:

  https://sourceware.org/ml/gdb-patches/2006-09/msg00192.html

  https://sourceware.org/ml/gdb-patches/2014-02/msg00248.html

  https://sourceware.org/ml/gdb-patches/2016-02/msg00226.html

The basic idea behind the patch is to start using the new
'getaddrinfo'/'getnameinfo' calls, which are responsible for
translating names and addresses in a protocol-independent way.  This
means that if we ever have a new version of the IP protocol, we won't
need to change the code again (or, at least, won't have to change the
majority of the code).

The function 'getaddrinfo' returns a linked list of possible addresses
to connect to.  Dealing with multiple addresses proved to be a hard
task with the current TCP auto-retry mechanism implemented on
ser-tcp:net_open.  For example, when gdbserver listened only on an
IPv4 socket:

  $ ./gdbserver --once 127.0.0.1:1234 ./a.out

and GDB was instructed to try to connect to both IPv6 and IPv4
sockets:

  $ ./gdb -ex 'target extended-remote localhost:1234' ./a.out

the user would notice a somewhat big delay before GDB was able to
connect to the IPv4 socket.  This happened because GDB was trying to
connect to the IPv6 socket first, and had to wait until the connection
timed out before it tried to connect to the IPv4 socket.

For that reason, I had to rewrite the main loop and implement a new
method for handling multiple connections.  After some discussion,
Pedro and I agreed on the following algorithm:

  1) For each entry returned by 'getaddrinfo', we try to open a socket
  and connect to it.

  2.a) If we have a successful 'connect', we just use that connection.

  2.b) If we don't have a successfull 'connect', but if we've got a
  ECONNREFUSED (meaning the the connection was refused), we keep track
  of this fact by using a flag.

  2.c) If we don't have a successfull 'connect', but if we've got a
  EINPROGRESS (meaning that the connection is in progress), we perform
  a 'select' call on the socket until we have a result (either a
  successful connection, or an error on the socket).

  3) If tcp_auto_retry is true, and we haven't gotten a successful
  connection, and at least one of our attempts failed with
  ECONNREFUSED, then we wait a little bit (i.e., call
  'wait_for_connect'), check to see if there was a
  timeout/interruption (in which case we bail out), and then go back
  to (1).

After multiple tests, I was able to connect without delay on the
scenario described above, and was also able to connect in all other
types of scenarios.

I also implemented some hostname parsing functions (along with their
corresponding unit tests) which are used to help GDB and gdbserver to
parse hostname strings provided by the user.  These new functions are
living inside common/netstuff.[ch].  I've had to do that since IPv6
introduces a new URL scheme, which defines that square brackets can be
used to enclose the host part and differentiate it from the
port (e.g., "[::1]:1234" means "host ::1, port 1234").  I spent some
time thinking about a reasonable way to interpret what the user wants,
and I came up with the following:

  - If the user has provided a prefix that doesn't specify the protocol
    version (i.e., "tcp:" or "udp:"), or if the user has not provided
    any prefix, don't make any assumptions (i.e., assume AF_UNSPEC when
    dealing with 'getaddrinfo') *unless* the host starts with "[" (in
    which case, assume it's an IPv6 host).

  - If the user has provided a prefix that does specify the protocol
    version (i.e., "tcp4:", "tcp6:", "udp4:" or "udp6:"), then respect
    that.

This method doesn't follow strictly what RFC 2732 proposes (that
literal IPv6 addresses should be provided enclosed in "[" and "]")
because IPv6 addresses still can be provided without square brackets
in our case, but since we have prefixes to specify protocol versions I
think this is not an issue.

Another thing worth mentioning is the new 'GDB_TEST_SOCKETHOST'
testcase parameter, which makes it possible to specify the
hostname (without the port) to be used when testing GDB and
gdbserver.  For example, to run IPv6 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'

Or, to run IPv4 tests:

  $ make check-gdb RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp4:127.0.0.1'

This required a few changes on the gdbserver-base.exp, and also a
minimal adjustment on gdb.server/run-without-local-binary.exp.

Finally, I've implemented a new testcase,
gdb.server/server-connect.exp, which is supposed to run on the native
host and perform various "smoke tests" using different connection
methods.

This patch has been regression-tested on BuildBot and locally, and
also built using a x86_64-w64-mingw32 GCC, and no problems were found.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>
	    Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	'unittests/parse-connection-spec-selftests.c'.
	(COMMON_SFILES): Add 'common/netstuff.c'.
	(HFILES_NO_SRCDIR): Add 'common/netstuff.h'.
	* NEWS (Changes since GDB 8.2): Mention IPv6 support.
	* common/netstuff.c: New file.
	* common/netstuff.h: New file.
	* ser-tcp.c: Include 'netstuff.h' and 'wspiapi.h'.
	(wait_for_connect): Update comment.  New parameter
	'gdb::optional<int> sock' instead of 'struct serial *scb'.
	Use 'sock' directly instead of 'scb->fd'.
	(try_connect): New function, with code from 'net_open'.
	(net_open): Rewrite main loop to deal with multiple
	sockets/addresses.  Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	* unittests/parse-connection-spec-selftests.c: New file.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* Makefile.in (SFILES): Add '$(srcdir)/common/netstuff.c'.
	(OBS): Add 'common/netstuff.o'.
	(GDBREPLAY_OBS): Likewise.
	* gdbreplay.c: Include 'wspiapi.h' and 'netstuff.h'.
	(remote_open): Implement support for IPv6
	connections.
	* remote-utils.c: Include 'netstuff.h', 'filestuff.h'
	and 'wspiapi.h'.
	(handle_accept_event): Accept connections from IPv6 sources.
	(remote_prepare): Handle IPv6-style hostnames; implement
	support for IPv6 connections.
	(remote_open): Implement support for printing connections from
	IPv6 sources.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* README (Testsuite Parameters): Mention new 'GDB_TEST_SOCKETHOST'
	parameter.
	* boards/native-extended-gdbserver.exp: Do not set 'sockethost'
	by default.
	* boards/native-gdbserver.exp: Likewise.
	* gdb.server/run-without-local-binary.exp: Improve regexp used
	for detecting when a remote debugging connection succeeds.
	* gdb.server/server-connect.exp: New file.
	* lib/gdbserver-support.exp (gdbserver_default_get_comm_port):
	Do not prefix the port number with ":".
	(gdbserver_start): New global GDB_TEST_SOCKETHOST.  Implement
	support for detecting and using it.  Add '$debughost_gdbserver'
	to the list of arguments used to start gdbserver.  Handle case
	when gdbserver cannot resolve a network name.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Paul Fertser  <fercerpav@gmail.com>
	    Tsutomu Seki  <sekiriki@gmail.com>

	* gdb.texinfo (Remote Connection Commands): Add explanation
	about new IPv6 support.  Add new connection prefixes.
---
 gdb/Makefile.in                                    |   3 +
 gdb/NEWS                                           |   4 +
 gdb/common/netstuff.c                              | 155 +++++++++++
 gdb/common/netstuff.h                              |  76 ++++++
 gdb/doc/gdb.texinfo                                |  51 +++-
 gdb/gdbserver/Makefile.in                          |   3 +
 gdb/gdbserver/gdbreplay.c                          | 129 ++++++---
 gdb/gdbserver/remote-utils.c                       | 119 +++++---
 gdb/ser-tcp.c                                      | 299 ++++++++++++---------
 gdb/testsuite/README                               |  14 +
 gdb/testsuite/boards/native-extended-gdbserver.exp |   2 -
 gdb/testsuite/boards/native-gdbserver.exp          |   1 -
 .../gdb.server/run-without-local-binary.exp        |   2 +-
 gdb/testsuite/gdb.server/server-connect.exp        | 111 ++++++++
 gdb/testsuite/lib/gdbserver-support.exp            |  28 +-
 gdb/unittests/parse-connection-spec-selftests.c    | 249 +++++++++++++++++
 16 files changed, 1048 insertions(+), 198 deletions(-)
 create mode 100644 gdb/common/netstuff.c
 create mode 100644 gdb/common/netstuff.h
 create mode 100644 gdb/testsuite/gdb.server/server-connect.exp
 create mode 100644 gdb/unittests/parse-connection-spec-selftests.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9c0dbbfda5..3e1c2d1cad 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -430,6 +430,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/offset-type-selftests.c \
 	unittests/observable-selftests.c \
 	unittests/optional-selftests.c \
+	unittests/parse-connection-spec-selftests.c \
 	unittests/ptid-selftests.c \
 	unittests/rsp-low-selftests.c \
 	unittests/scoped_fd-selftests.c \
@@ -959,6 +960,7 @@ COMMON_SFILES = \
 	common/job-control.c \
 	common/gdb_tilde_expand.c \
 	common/gdb_vecs.c \
+	common/netstuff.c \
 	common/new-op.c \
 	common/pathstuff.c \
 	common/print-utils.c \
@@ -1439,6 +1441,7 @@ HFILES_NO_SRCDIR = \
 	common/gdb_vecs.h \
 	common/gdb_wait.h \
 	common/common-inferior.h \
+	common/netstuff.h \
 	common/host-defs.h \
 	common/pathstuff.h \
 	common/print-utils.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index 2d1d161233..acb9c34fb2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 8.2
 
+* GDB and GDBserver now support IPv6 connections.  IPv6 addresses
+  can be passed using the '[ADDRESS]:PORT' notation, or the regular
+  'ADDRESS:PORT' method.
+
 *** Changes in GDB 8.2
 
 * The 'set disassembler-options' command now supports specifying options
diff --git a/gdb/common/netstuff.c b/gdb/common/netstuff.c
new file mode 100644
index 0000000000..c1c401ccb2
--- /dev/null
+++ b/gdb/common/netstuff.c
@@ -0,0 +1,155 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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 <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+/* See common/netstuff.h.  */
+
+scoped_free_addrinfo::~scoped_free_addrinfo ()
+{
+  freeaddrinfo (m_res);
+}
+
+/* See common/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 common/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);
+}
diff --git a/gdb/common/netstuff.h b/gdb/common/netstuff.h
new file mode 100644
index 0000000000..17deb3fa87
--- /dev/null
+++ b/gdb/common/netstuff.h
@@ -0,0 +1,76 @@
+/* Operations on network stuff.
+   Copyright (C) 2018 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/>.  */
+
+#ifndef NETSTUFF_H
+#define NETSTUFF_H
+
+#include <string>
+
+/* Like NI_MAXHOST/NI_MAXSERV, but enough for numeric forms.  */
+#define GDB_NI_MAX_ADDR 64
+#define GDB_NI_MAX_PORT 16
+
+/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
+
+class scoped_free_addrinfo
+{
+public:
+  /* Default constructor.  */
+  scoped_free_addrinfo (struct addrinfo *ainfo)
+    : m_res (ainfo)
+  {
+  }
+
+  /* Destructor responsible for free'ing M_RES by calling
+     'freeaddrinfo'.  */
+  ~scoped_free_addrinfo ();
+
+  DISABLE_COPY_AND_ASSIGN (scoped_free_addrinfo);
+
+private:
+  /* The addrinfo resource.  */
+  struct addrinfo *m_res;
+};
+
+/* The struct we return after parsing the connection spec.  */
+
+struct parsed_connection_spec
+{
+  /* The hostname.  */
+  std::string host_str;
+
+  /* The port, if any.  */
+  std::string port_str;
+};
+
+
+/* Parse SPEC (which is a string in the form of "ADDR:PORT") and
+   return a 'parsed_connection_spec' structure with the proper fields
+   filled in.  Also adjust HINT accordingly.  */
+extern parsed_connection_spec
+  parse_connection_spec_without_prefix (std::string spec,
+					struct addrinfo *hint);
+
+/* Parse SPEC (which is a string in the form of
+   "[tcp[6]:|udp[6]:]ADDR:PORT") and return a 'parsed_connection_spec'
+   structure with the proper fields filled in.  Also adjust HINT
+   accordingly.  */
+extern parsed_connection_spec parse_connection_spec (const char *spec,
+						     struct addrinfo *hint);
+
+#endif /* ! NETSTUFF_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 245d3f1b97..2e9d76227c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20517,16 +20517,27 @@ If you're using a serial line, you may want to give @value{GDBN} the
 @code{target} command.
 
 @item target remote @code{@var{host}:@var{port}}
+@itemx target remote @code{@var{[host]}:@var{port}}
 @itemx target remote @code{tcp:@var{host}:@var{port}}
+@itemx target remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target remote @code{tcp4:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{host}:@var{port}}
+@itemx target remote @code{tcp6:@var{[host]}:@var{port}}
 @itemx target extended-remote @code{@var{host}:@var{port}}
+@itemx target extended-remote @code{@var{[host]}:@var{port}}
 @itemx target extended-remote @code{tcp:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{tcp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{tcp6:@var{[host]}:@var{port}}
 @cindex @acronym{TCP} port, @code{target remote}
 Debug using a @acronym{TCP} connection to @var{port} on @var{host}.
-The @var{host} may be either a host name or a numeric @acronym{IP}
-address; @var{port} must be a decimal number.  The @var{host} could be
-the target machine itself, if it is directly connected to the net, or
-it might be a terminal server which in turn has a serial line to the
-target.
+The @var{host} may be either a host name, a numeric @acronym{IPv4}
+address, or a numeric @acronym{IPv6} address (with or without the
+square brackets to separate the address from the port); @var{port}
+must be a decimal number.  The @var{host} could be the target machine
+itself, if it is directly connected to the net, or it might be a
+terminal server which in turn has a serial line to the target.
 
 For example, to connect to port 2828 on a terminal server named
 @code{manyfarms}:
@@ -20535,6 +20546,28 @@ For example, to connect to port 2828 on a terminal server named
 target remote manyfarms:2828
 @end smallexample
 
+To connect to port 2828 on a terminal server whose address is
+@code{2001:0db8:85a3:0000:0000:8a2e:0370:7334}, you can either use the
+square bracket syntax:
+
+@smallexample
+target remote [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2828
+@end smallexample
+
+@noindent
+or explicitly specify the @acronym{IPv6} protocol:
+
+@smallexample
+target remote tcp6:2001:0db8:85a3:0000:0000:8a2e:0370:7334:2828
+@end smallexample
+
+This last example may be confusing to the reader, because there is no
+visible separation between the hostname and the port number.
+Therefore, we recommend the user to provide @acronym{IPv6} addresses
+using square brackets for clarity.  However, it is important to
+mention that for @value{GDBN} there is no ambiguity: the number after
+the last colon is considered to be the port number.
+
 If your remote target is actually running on the same machine as your
 debugger session (e.g.@: a simulator for your target running on the
 same host), you can omit the hostname.  For example, to connect to
@@ -20548,7 +20581,15 @@ target remote :1234
 Note that the colon is still required here.
 
 @item target remote @code{udp:@var{host}:@var{port}}
+@itemx target remote @code{udp:@var{[host]}:@var{port}}
+@itemx target remote @code{udp4:@var{host}:@var{port}}
+@itemx target remote @code{udp6:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp:@var{host}:@var{port}}
 @itemx target extended-remote @code{udp:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp:@var{[host]}:@var{port}}
+@itemx target extended-remote @code{udp4:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{host}:@var{port}}
+@itemx target extended-remote @code{udp6:@var{[host]}:@var{port}}
 @cindex @acronym{UDP} port, @code{target remote}
 Debug using @acronym{UDP} packets to @var{port} on @var{host}.  For example, to
 connect to @acronym{UDP} port 2828 on a terminal server named @code{manyfarms}:
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 513f286289..f2f8a084bd 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -211,6 +211,7 @@ SFILES = \
 	$(srcdir)/common/job-control.c \
 	$(srcdir)/common/gdb_tilde_expand.c \
 	$(srcdir)/common/gdb_vecs.c \
+	$(srcdir)/common/netstuff.c \
 	$(srcdir)/common/new-op.c \
 	$(srcdir)/common/pathstuff.c \
 	$(srcdir)/common/print-utils.c \
@@ -254,6 +255,7 @@ OBS = \
 	common/format.o \
 	common/gdb_tilde_expand.o \
 	common/gdb_vecs.o \
+	common/netstuff.o \
 	common/new-op.o \
 	common/pathstuff.o \
 	common/print-utils.o \
@@ -290,6 +292,7 @@ GDBREPLAY_OBS = \
 	common/common-exceptions.o \
 	common/common-utils.o \
 	common/errors.o \
+	common/netstuff.o \
 	common/print-utils.o \
 	gdbreplay.o \
 	utils.o \
diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
index a4bc892462..026bbfccaf 100644
--- a/gdb/gdbserver/gdbreplay.c
+++ b/gdb/gdbserver/gdbreplay.c
@@ -46,8 +46,11 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
+#include "netstuff.h"
+
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
@@ -142,56 +145,108 @@ remote_close (void)
 static void
 remote_open (char *name)
 {
-  if (!strchr (name, ':'))
+  char *last_colon = strrchr (name, ':');
+
+  if (last_colon == NULL)
     {
       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
       fflush (stderr);
       exit (1);
     }
-  else
-    {
+
 #ifdef USE_WIN32API
-      static int winsock_initialized;
+  static int winsock_initialized;
 #endif
-      char *port_str;
-      int port;
-      struct sockaddr_in sockaddr;
-      socklen_t tmp;
-      int tmp_desc;
+  char *port_str;
+  int tmp;
+  int tmp_desc;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
 
-      port_str = strchr (name, ':');
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
 
-      port = atoi (port_str + 1);
+  parsed_connection_spec parsed = parse_connection_spec (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
 
 #ifdef USE_WIN32API
-      if (!winsock_initialized)
-	{
-	  WSADATA wsad;
+  if (!winsock_initialized)
+    {
+      WSADATA wsad;
 
-	  WSAStartup (MAKEWORD (1, 0), &wsad);
-	  winsock_initialized = 1;
-	}
+      WSAStartup (MAKEWORD (1, 0), &wsad);
+      winsock_initialized = 1;
+    }
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
-      if (tmp_desc == -1)
-	perror_with_name ("Can't open socket");
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
 
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-		  sizeof (tmp));
+  if (r != 0)
+    {
+      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+	       parsed.host_str.c_str (), parsed.port_str.c_str (),
+	       gai_strerror (r));
+      fflush (stderr);
+      exit (1);
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  struct addrinfo *p;
+
+  for (p = ainfo; p != NULL; p = p->ai_next)
+    {
+      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
 
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      sockaddr.sin_addr.s_addr = INADDR_ANY;
+      if (tmp_desc >= 0)
+	break;
+    }
+
+  if (p == NULL)
+    perror_with_name ("Cannot open socket");
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-	  || listen (tmp_desc, 1))
-	perror_with_name ("Can't bind address");
+  /* Allow rapid reuse of this port. */
+  tmp = 1;
+  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+	      sizeof (tmp));
+
+  switch (p->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+      exit (1);
+    }
+
+  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+    perror_with_name ("Can't bind address");
+
+  if (p->ai_socktype == SOCK_DGRAM)
+    remote_desc = tmp_desc;
+  else
+    {
+      struct sockaddr_storage sockaddr;
+      socklen_t sockaddrsize = sizeof (sockaddr);
+      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+      if (listen (tmp_desc, 1) != 0)
+	perror_with_name ("Can't listen on socket");
+
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+			    &sockaddrsize);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
       if (remote_desc == -1)
 	perror_with_name ("Accept failed");
 
@@ -206,6 +261,16 @@ remote_open (char *name)
       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
 		  (char *) &tmp, sizeof (tmp));
 
+      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+	{
+	  fprintf (stderr, "Remote debugging from host %s, port %s\n",
+		   orig_host, orig_port);
+	  fflush (stderr);
+	}
+
 #ifndef USE_WIN32API
       close (tmp_desc);		/* No longer need this */
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 45d5c8d451..1734c54e39 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -26,6 +26,8 @@
 #include "dll.h"
 #include "rsp-low.h"
 #include "gdbthread.h"
+#include "netstuff.h"
+#include "filestuff.h"
 #include <ctype.h>
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -63,6 +65,7 @@
 
 #if USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #endif
 
 #if __QNX__
@@ -151,19 +154,18 @@ enable_async_notification (int fd)
 static int
 handle_accept_event (int err, gdb_client_data client_data)
 {
-  struct sockaddr_in sockaddr;
-  socklen_t tmp;
+  struct sockaddr_storage sockaddr;
+  socklen_t len = sizeof (sockaddr);
 
   if (debug_threads)
     debug_printf ("handling possible accept event\n");
 
-  tmp = sizeof (sockaddr);
-  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
+  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &len);
   if (remote_desc == -1)
     perror_with_name ("Accept failed");
 
   /* Enable TCP keep alive process. */
-  tmp = 1;
+  socklen_t tmp = 1;
   setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
 	      (char *) &tmp, sizeof (tmp));
 
@@ -192,8 +194,19 @@ handle_accept_event (int err, gdb_client_data client_data)
   delete_file_handler (listen_desc);
 
   /* Convert IP address to string.  */
-  fprintf (stderr, "Remote debugging from host %s\n",
-	   inet_ntoa (sockaddr.sin_addr));
+  char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+		       orig_host, sizeof (orig_host),
+		       orig_port, sizeof (orig_port),
+		       NI_NUMERICHOST | NI_NUMERICSERV);
+
+  if (r != 0)
+    fprintf (stderr, _("Could not obtain remote address: %s\n"),
+	     gai_strerror (r));
+  else
+    fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
+	     orig_host, orig_port);
 
   enable_async_notification (remote_desc);
 
@@ -222,10 +235,7 @@ remote_prepare (const char *name)
 #ifdef USE_WIN32API
   static int winsock_initialized;
 #endif
-  int port;
-  struct sockaddr_in sockaddr;
   socklen_t tmp;
-  char *port_end;
 
   remote_is_stdio = 0;
   if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
@@ -238,17 +248,25 @@ remote_prepare (const char *name)
       return;
     }
 
-  port_str = strchr (name, ':');
-  if (port_str == NULL)
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_connection_spec parsed
+    = parse_connection_spec_without_prefix (name, &hint);
+
+  if (parsed.port_str.empty ())
     {
       cs.transport_is_reliable = 0;
       return;
     }
 
-  port = strtoul (port_str + 1, &port_end, 10);
-  if (port_str[1] == '\0' || *port_end != '\0')
-    error ("Bad port argument: %s", name);
-
 #ifdef USE_WIN32API
   if (!winsock_initialized)
     {
@@ -259,8 +277,26 @@ remote_prepare (const char *name)
     }
 #endif
 
-  listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (listen_desc == -1)
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+		       &hint, &ainfo);
+
+  if (r != 0)
+    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+
+  scoped_free_addrinfo freeaddrinfo (ainfo);
+
+  struct addrinfo *iter;
+
+  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+    {
+      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+					iter->ai_protocol);
+
+      if (listen_desc >= 0)
+	break;
+    }
+
+  if (iter == NULL)
     perror_with_name ("Can't open socket");
 
   /* Allow rapid reuse of this port. */
@@ -268,14 +304,25 @@ remote_prepare (const char *name)
   setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
 	      sizeof (tmp));
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  sockaddr.sin_addr.s_addr = INADDR_ANY;
+  switch (iter->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
+    }
 
-  if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-      || listen (listen_desc, 1))
+  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
     perror_with_name ("Can't bind address");
 
+  if (listen (listen_desc, 1) != 0)
+    perror_with_name ("Can't listen on socket");
+
   cs.transport_is_reliable = 1;
 }
 
@@ -350,18 +397,24 @@ remote_open (const char *name)
 #endif /* USE_WIN32API */
   else
     {
-      int port;
-      socklen_t len;
-      struct sockaddr_in sockaddr;
-
-      len = sizeof (sockaddr);
-      if (getsockname (listen_desc,
-		       (struct sockaddr *) &sockaddr, &len) < 0
-	  || len < sizeof (sockaddr))
+      char listen_port[GDB_NI_MAX_PORT];
+      struct sockaddr_storage sockaddr;
+      socklen_t len = sizeof (sockaddr);
+
+      if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0)
 	perror_with_name ("Can't determine port");
-      port = ntohs (sockaddr.sin_port);
 
-      fprintf (stderr, "Listening on port %d\n", port);
+      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+			   NULL, 0,
+			   listen_port, sizeof (listen_port),
+			   NI_NUMERICSERV);
+
+      if (r != 0)
+	fprintf (stderr, _("Can't obtain port where we are listening: %s"),
+		 gai_strerror (r));
+      else
+	fprintf (stderr, _("Listening on port %s\n"), listen_port);
+
       fflush (stderr);
 
       /* Register the event loop handler.  */
diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index 23ef3b04b8..36f34b2afa 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -25,6 +25,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
 #include "filestuff.h"
+#include "netstuff.h"
 
 #include <sys/types.h>
 
@@ -39,6 +40,7 @@
 
 #ifdef USE_WIN32API
 #include <winsock2.h>
+#include <wspiapi.h>
 #ifndef ETIMEDOUT
 #define ETIMEDOUT WSAETIMEDOUT
 #endif
@@ -81,12 +83,13 @@ static unsigned int tcp_retry_limit = 15;
 
 #define POLL_INTERVAL 5
 
-/* Helper function to wait a while.  If SCB is non-null, wait on its
-   file descriptor.  Otherwise just wait on a timeout, updating *POLLS.
-   Returns -1 on timeout or interrupt, otherwise the value of select.  */
+/* Helper function to wait a while.  If SOCK is not -1, wait on its
+   file descriptor.  Otherwise just wait on a timeout, updating
+   *POLLS.  Returns -1 on timeout or interrupt, otherwise the value of
+   select.  */
 
 static int
-wait_for_connect (struct serial *scb, unsigned int *polls)
+wait_for_connect (int sock, unsigned int *polls)
 {
   struct timeval t;
   int n;
@@ -120,24 +123,24 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
       t.tv_usec = 0;
     }
 
-  if (scb)
+  if (sock >= 0)
     {
       fd_set rset, wset, eset;
 
       FD_ZERO (&rset);
-      FD_SET (scb->fd, &rset);
+      FD_SET (sock, &rset);
       wset = rset;
       eset = rset;
-	  
+
       /* POSIX systems return connection success or failure by signalling
 	 wset.  Windows systems return success in wset and failure in
 	 eset.
-     
+
 	 We must call select here, rather than gdb_select, because
 	 the serial structure has not yet been initialized - the
 	 MinGW select wrapper will not know that this FD refers
 	 to a socket.  */
-      n = select (scb->fd + 1, &rset, &wset, &eset, &t);
+      n = select (sock + 1, &rset, &wset, &eset, &t);
     }
   else
     /* Use gdb_select here, since we have no file descriptors, and on
@@ -153,80 +156,28 @@ wait_for_connect (struct serial *scb, unsigned int *polls)
   return n;
 }
 
-/* Open a tcp socket.  */
+/* Try to connect to the host represented by AINFO.  If the connection
+   succeeds, return its socket.  Otherwise, return -1 and set ERRNO
+   accordingly.  POLLS is used when 'connect' returns EINPROGRESS, and
+   we need to invoke 'wait_for_connect' to obtain the status.  */
 
-int
-net_open (struct serial *scb, const char *name)
+static int
+try_connect (const struct addrinfo *ainfo, unsigned int *polls)
 {
-  char hostname[100];
-  const char *port_str;
-  int n, port, tmp;
-  int use_udp;
-  struct hostent *hostent;
-  struct sockaddr_in sockaddr;
-#ifdef USE_WIN32API
-  u_long ioarg;
-#else
-  int ioarg;
-#endif
-  unsigned int polls = 0;
-
-  use_udp = 0;
-  if (startswith (name, "udp:"))
-    {
-      use_udp = 1;
-      name = name + 4;
-    }
-  else if (startswith (name, "tcp:"))
-    name = name + 4;
-
-  port_str = strchr (name, ':');
-
-  if (!port_str)
-    error (_("net_open: No colon in host name!"));  /* Shouldn't ever
-						       happen.  */
-
-  tmp = std::min (port_str - name, (ptrdiff_t) sizeof hostname - 1);
-  strncpy (hostname, name, tmp);	/* Don't want colon.  */
-  hostname[tmp] = '\000';	/* Tie off host name.  */
-  port = atoi (port_str + 1);
-
-  /* Default hostname is localhost.  */
-  if (!hostname[0])
-    strcpy (hostname, "localhost");
-
-  hostent = gethostbyname (hostname);
-  if (!hostent)
-    {
-      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
-      errno = ENOENT;
-      return -1;
-    }
+  int sock = gdb_socket_cloexec (ainfo->ai_family, ainfo->ai_socktype,
+				 ainfo->ai_protocol);
 
-  sockaddr.sin_family = PF_INET;
-  sockaddr.sin_port = htons (port);
-  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
-	  sizeof (struct in_addr));
-
- retry:
-
-  if (use_udp)
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_DGRAM, 0);
-  else
-    scb->fd = gdb_socket_cloexec (PF_INET, SOCK_STREAM, 0);
-
-  if (scb->fd == -1)
+  if (sock < 0)
     return -1;
-  
+
   /* Set socket nonblocking.  */
-  ioarg = 1;
-  ioctl (scb->fd, FIONBIO, &ioarg);
+  int ioarg = 1;
+
+  ioctl (sock, FIONBIO, &ioarg);
 
   /* Use Non-blocking connect.  connect() will return 0 if connected
      already.  */
-  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
-
-  if (n < 0)
+  if (connect (sock, ainfo->ai_addr, ainfo->ai_addrlen) < 0)
     {
 #ifdef USE_WIN32API
       int err = WSAGetLastError();
@@ -234,90 +185,196 @@ net_open (struct serial *scb, const char *name)
       int err = errno;
 #endif
 
-      /* Maybe we're waiting for the remote target to become ready to
-	 accept connections.  */
-      if (tcp_auto_retry
+      /* If we've got a "connection refused" error, just return
+	 -1.  The caller will know what to do.  */
+      if (
 #ifdef USE_WIN32API
-	  && err == WSAECONNREFUSED
+	  err == WSAECONNREFUSED
 #else
-	  && err == ECONNREFUSED
+	  err == ECONNREFUSED
 #endif
-	  && wait_for_connect (NULL, &polls) >= 0)
+	  )
 	{
-	  close (scb->fd);
-	  goto retry;
+	  close (sock);
+	  errno = err;
+	  return -1;
 	}
 
       if (
 #ifdef USE_WIN32API
-	  /* Under Windows, calling "connect" with a non-blocking socket
-	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
+	  /* Any other error (except EINPROGRESS) will be "swallowed"
+	     here.  We return without specifying a return value, and
+	     set errno if the caller wants to inspect what
+	     happened.  */
 	  err != WSAEWOULDBLOCK
 #else
 	  err != EINPROGRESS
 #endif
 	  )
 	{
+	  close (sock);
 	  errno = err;
-	  net_close (scb);
 	  return -1;
 	}
 
       /* Looks like we need to wait for the connect.  */
-      do 
-	{
-	  n = wait_for_connect (scb, &polls);
-	} 
+      int n;
+
+      do
+	n = wait_for_connect (sock, polls);
       while (n == 0);
+
       if (n < 0)
 	{
-	  net_close (scb);
+	  /* A negative value here means that we either timed out or
+	     got interrupted by the user.  Just return.  */
+	  close (sock);
 	  return -1;
 	}
     }
 
   /* Got something.  Is it an error?  */
-  {
-    int res, err;
-    socklen_t len;
-
-    len = sizeof (err);
-    /* On Windows, the fourth parameter to getsockopt is a "char *";
-       on UNIX systems it is generally "void *".  The cast to "char *"
-       is OK everywhere, since in C++ any data pointer type can be
-       implicitly converted to "void *".  */
-    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
-    if (res < 0 || err)
-      {
-	/* Maybe the target still isn't ready to accept the connection.  */
-	if (tcp_auto_retry
+  int err;
+  socklen_t len = sizeof (err);
+
+  /* On Windows, the fourth parameter to getsockopt is a "char *";
+     on UNIX systems it is generally "void *".  The cast to "char *"
+     is OK everywhere, since in C++ any data pointer type can be
+     implicitly converted to "void *".  */
+  int ret = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
+
+  if (ret < 0)
+    {
+      close (sock);
+      errno = ret;
+      return -1;
+    }
+  else if (ret == 0 && err != 0)
+    {
+      close (sock);
+      errno = err;
+
+      /* Check if the connection was refused.  */
+      if (
 #ifdef USE_WIN32API
-	    && err == WSAECONNREFUSED
+	  err == WSAECONNREFUSED
 #else
-	    && err == ECONNREFUSED
+	  err == ECONNREFUSED
 #endif
-	    && wait_for_connect (NULL, &polls) >= 0)
-	  {
-	    close (scb->fd);
-	    goto retry;
-	  }
-	if (err)
-	  errno = err;
-	net_close (scb);
+	  )
 	return -1;
-      }
-  } 
+      else
+	{
+	  /* If we have any other kind of error, just return nothing.  */
+	  return -1;
+	}
+    }
+
+  /* The connection succeeded.  Return the socket.  */
+  return sock;
+}
+
+/* Open a tcp socket.  */
+
+int
+net_open (struct serial *scb, const char *name)
+{
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  parsed_connection_spec parsed = parse_connection_spec (name, &hint);
+
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
+
+  int r = getaddrinfo (parsed.host_str.c_str (),
+		       parsed.port_str.c_str (),
+		       &hint, &ainfo);
+
+  if (r != 0)
+    {
+      fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"),
+			  name, gai_strerror (r));
+      errno = ENOENT;
+      return -1;
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
+
+  /* Flag to indicate whether we've got a connection refused.  It will
+     be true if any of the connections tried was refused.  */
+  bool got_connrefused;
+  /* If a connection succeeeds, SUCCESS_AINFO will point to the
+     'struct addrinfo' that succeed.  */
+  struct addrinfo *success_ainfo = NULL;
+  unsigned int polls = 0;
+
+  /* Assume the worst.  */
+  scb->fd = -1;
+
+  do
+    {
+      got_connrefused = false;
+
+      for (struct addrinfo *iter = ainfo; iter != NULL; iter = iter->ai_next)
+	{
+	  /* Iterate over the list of possible addresses to connect
+	     to.  For each, we'll try to connect and see if it
+	     succeeds.  */
+	  int sock = try_connect (iter, &polls);
+
+	  if (sock >= 0)
+	    {
+	      /* We've gotten a successful connection.  Save its
+		 'struct addrinfo', the socket, and break.  */
+	      success_ainfo = iter;
+	      scb->fd = sock;
+	      break;
+	    }
+	  else if (errno == ECONNREFUSED)
+	    got_connrefused = true;
+	}
+    }
+  /* Just retry if:
+
+     - tcp_auto_retry is true, and
+     - We haven't gotten a connection yet, and
+     - Any of our connection attempts returned with ECONNREFUSED, and
+     - wait_for_connect signals that we can keep going.  */
+  while (tcp_auto_retry
+	 && success_ainfo == NULL
+	 && got_connrefused
+	 && wait_for_connect (-1, &polls) >= 0);
+
+  if (success_ainfo == NULL)
+    {
+      net_close (scb);
+      return -1;
+    }
 
   /* Turn off nonblocking.  */
-  ioarg = 0;
+#ifdef USE_WIN32API
+  u_long ioarg = 0;
+#else
+  int ioarg = 0;
+#endif
+
   ioctl (scb->fd, FIONBIO, &ioarg);
 
-  if (use_udp == 0)
+  if (success_ainfo->ai_socktype == IPPROTO_TCP)
     {
       /* Disable Nagle algorithm.  Needed in some cases.  */
-      tmp = 1;
+      int tmp = 1;
+
       setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
-		  (char *)&tmp, sizeof (tmp));
+		  (char *) &tmp, sizeof (tmp));
     }
 
 #ifdef SIGPIPE
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 4475ac21a9..55abfb3254 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -259,6 +259,20 @@ This make (not runtest) variable is used to specify whether the
 testsuite preloads the read1.so library into expect.  Any non-empty
 value means true.  See "Race detection" below.
 
+GDB_TEST_SOCKETHOST
+
+This variable can provide the hostname/address that should be used
+when performing GDBserver-related tests.  This is useful in some
+situations, e.g., when you want to test the IPv6 connectivity of GDB
+and GDBserver, or when using a different hostname/address is needed.
+For example, to make GDB and GDBserver use IPv6-only connections, you
+can do:
+
+	make check TESTS="gdb.server/*.exp" RUNTESTFLAGS='GDB_TEST_SOCKETHOST=tcp6:[::1]'
+
+Note that only a hostname/address can be provided, without a port
+number.
+
 Race detection
 **************
 
diff --git a/gdb/testsuite/boards/native-extended-gdbserver.exp b/gdb/testsuite/boards/native-extended-gdbserver.exp
index df949994fd..482e4e3c14 100644
--- a/gdb/testsuite/boards/native-extended-gdbserver.exp
+++ b/gdb/testsuite/boards/native-extended-gdbserver.exp
@@ -24,8 +24,6 @@ load_generic_config "extended-gdbserver"
 load_board_description "gdbserver-base"
 load_board_description "local-board"
 
-set_board_info sockethost "localhost:"
-
 # We will be using the extended GDB remote protocol.
 set_board_info gdb_protocol "extended-remote"
 
diff --git a/gdb/testsuite/boards/native-gdbserver.exp b/gdb/testsuite/boards/native-gdbserver.exp
index ef9316007e..1dee3df4f1 100644
--- a/gdb/testsuite/boards/native-gdbserver.exp
+++ b/gdb/testsuite/boards/native-gdbserver.exp
@@ -30,7 +30,6 @@ set_board_info gdb,do_reload_on_run 1
 # There's no support for argument-passing (yet).
 set_board_info noargs 1
 
-set_board_info sockethost "localhost:"
 set_board_info use_gdb_stub 1
 set_board_info exit_is_reliable 1
 
diff --git a/gdb/testsuite/gdb.server/run-without-local-binary.exp b/gdb/testsuite/gdb.server/run-without-local-binary.exp
index 1665ca9912..6ba3e711d9 100644
--- a/gdb/testsuite/gdb.server/run-without-local-binary.exp
+++ b/gdb/testsuite/gdb.server/run-without-local-binary.exp
@@ -53,7 +53,7 @@ save_vars { GDBFLAGS } {
     set use_gdb_stub 0
 
     gdb_test "target ${gdbserver_protocol} ${gdbserver_gdbport}" \
-	"Remote debugging using $gdbserver_gdbport" \
+	"Remote debugging using [string_to_regexp $gdbserver_gdbport]" \
 	"connect to gdbserver"
 
     gdb_test "run" \
diff --git a/gdb/testsuite/gdb.server/server-connect.exp b/gdb/testsuite/gdb.server/server-connect.exp
new file mode 100644
index 0000000000..e8e96d2d8a
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-connect.exp
@@ -0,0 +1,111 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# 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/>.
+
+# Test multiple types of connection (IPv4, IPv6, TCP, UDP) and make
+# sure both gdbserver and GDB work.
+
+load_lib gdbserver-support.exp
+
+standard_testfile normal.c
+
+if {[skip_gdbserver_tests]} {
+    return 0
+}
+
+# We want to have control over where we start gdbserver.
+if { [is_remote target] } {
+    return 0
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+# Make sure we're disconnected, in case we're testing with an
+# extended-remote board, therefore already connected.
+gdb_test "disconnect" ".*"
+
+set target_exec [gdbserver_download_current_prog]
+
+# An array containing the test instructions for each scenario.  The
+# description of each field is as follows:
+#
+# - The connection specification to be used when starting
+#   gdbserver/GDB.  This string will be used to set the
+#   GDB_TEST_SOCKETHOST when calling gdbserver_start.
+#
+# - A flag indicating whether gdbserver should fail when we attempt to
+#   start it.  Useful when testing erroneous connection specs such as
+#   "tcp8:".
+#
+# - The prefix that should be prepended to the test messages.
+set test_params \
+    { \
+	  { "tcp4:127.0.0.1" 0 "tcp4" } \
+	  { "tcp6:::1"       0 "tcp6" } \
+	  { "tcp6:[::1]"     0 "tcp6-with-brackets" } \
+	  { "tcp:localhost"  0 "tcp" } \
+	  { "udp4:127.0.0.1" 0 "udp4" } \
+	  { "udp6:::1"       0 "udp6" } \
+	  { "udp6:[::1]"     0 "udp6-with-brackets" } \
+	  { "tcp8:123"       1 "tcp8" } \
+	  { "udp123:::"      1 "udp123" } \
+	  { "garbage:1234"   1 "garbage:1234" } \
+    }
+
+# The best way to test different types of connections is to set the
+# GDB_TEST_SOCKETHOST variable accordingly.
+save_vars { GDB_TEST_SOCKETHOST } {
+    foreach line $test_params {
+	set sockhost [lindex $line 0]
+	set gdbserver_should_fail [lindex $line 1]
+	set prefix [lindex $line 2]
+
+	with_test_prefix $prefix {
+	    set GDB_TEST_SOCKETHOST $sockhost
+	    set test "start gdbserver"
+
+	    # Try to start gdbserver.
+	    set catchres [catch {set res [gdbserver_start "" $target_exec]} errmsg]
+
+	    if { $catchres != 0 } {
+		if { $gdbserver_should_fail } {
+		    pass "$test: gdbserver failed as expected"
+		} else {
+		    fail "$test: $errmsg"
+		}
+		continue
+	    } else {
+		if { $gdbserver_should_fail } {
+		    fail "$test: gdbserver should fail but did not"
+		} else {
+		    pass "$test"
+		}
+	    }
+
+	    set gdbserver_protocol [lindex $res 0]
+	    set gdbserver_gdbport [lindex $res 1]
+	    set test "connect to gdbserver using $sockhost"
+
+	    if { [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0 } {
+		pass $test
+	    } else {
+		fail $test
+	    }
+	}
+    }
+}
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 46e4f77922..ed9b31b3ab 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -211,7 +211,7 @@ proc gdbserver_default_get_remote_address { host port } {
 # Default routine to compute the "comm" argument for gdbserver.
 
 proc gdbserver_default_get_comm_port { port } {
-    return ":$port"
+    return "$port"
 }
 
 # Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
@@ -221,6 +221,7 @@ proc gdbserver_default_get_comm_port { port } {
 
 proc gdbserver_start { options arguments } {
     global portnum
+    global GDB_TEST_SOCKETHOST
 
     # Port id -- either specified in baseboard file, or managed here.
     if [target_info exists gdb,socketport] {
@@ -231,10 +232,22 @@ proc gdbserver_start { options arguments } {
     }
 
     # Extract the local and remote host ids from the target board struct.
-    if [target_info exists sockethost] {
+    if { [info exists GDB_TEST_SOCKETHOST] } {
+	# The user is not supposed to provide a port number, just a
+	# hostname/address, therefore we add the trailing ":" here.
+	set debughost "${GDB_TEST_SOCKETHOST}:"
+	# Escape open and close square brackets.
+	set debughost_tmp [string map { [ \\[ ] \\] } $debughost]
+	# We need a "gdbserver" version of the debughost, which will
+	# have the possible connection prefix stripped.  This is
+	# because gdbserver currently doesn't recognize the prefixes.
+	regsub -all "^\(tcp:|udp:|tcp4:|udp4:|tcp6:|udp6:\)" $debughost_tmp "" debughost_gdbserver
+    } elseif [target_info exists sockethost] {
 	set debughost [target_info sockethost]
+	set debughost_gdbserver $debughost
     } else {
 	set debughost "localhost:"
+	set debughost_gdbserver $debughost
     }
 
     # Some boards use a different value for the port that is passed to
@@ -277,8 +290,14 @@ proc gdbserver_start { options arguments } {
 	if { $options != "" } {
 	    append gdbserver_command " $options"
 	}
+	if { $debughost_gdbserver != "" } {
+	    append gdbserver_command " $debughost_gdbserver"
+	}
 	if { $portnum != "" } {
-	    append gdbserver_command " [$get_comm_port $portnum]"
+	    if { $debughost_gdbserver == "" } {
+		append gdbserver_command " "
+	    }
+	    append gdbserver_command "[$get_comm_port $portnum]"
 	}
 	if { $arguments != "" } {
 	    append gdbserver_command " $arguments"
@@ -307,6 +326,9 @@ proc gdbserver_start { options arguments } {
 		    continue
 		}
 	    }
+	    -re ".*: cannot resolve name: Name or service not known\r\n" {
+		error "gdbserver cannot resolve name."
+	    }
 	    timeout {
 		error "Timeout waiting for gdbserver response."
 	    }
diff --git a/gdb/unittests/parse-connection-spec-selftests.c b/gdb/unittests/parse-connection-spec-selftests.c
new file mode 100644
index 0000000000..72ad48e80b
--- /dev/null
+++ b/gdb/unittests/parse-connection-spec-selftests.c
@@ -0,0 +1,249 @@
+/* Self tests for parsing connection specs for GDB, the GNU debugger.
+
+   Copyright (C) 2017-2018 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 "defs.h"
+#include "selftest.h"
+#include "common/netstuff.h"
+#include "diagnostics.h"
+#ifdef USE_WIN32API
+#include <winsock2.h>
+#include <wspiapi.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#endif
+
+namespace selftests {
+namespace parse_connection_spec_tests {
+
+/* Auxiliary struct that holds info about a specific test for a
+   connection spec.  */
+
+struct parse_conn_test
+{
+  /* The connection spec.  */
+  const char *connspec;
+
+  /* Expected result from 'parse_connection_spec'.  */
+  parsed_connection_spec expected_result;
+
+  /* True if this test should fail, false otherwise.  If true, only
+     the CONNSPEC field should be considered as valid.  */
+  bool should_fail;
+
+  /* The expected AI_FAMILY to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_family;
+
+  /* The expected AI_SOCKTYPE to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_socktype;
+
+  /* The expected AI_PROTOCOL to be found on the 'struct addrinfo'
+     HINT.  */
+  int exp_ai_protocol;
+};
+
+/* Some defines to help us fill a 'struct parse_conn_test'.  */
+
+/* Initialize a full entry.  */
+#define INIT_ENTRY(ADDR, EXP_HOST, EXP_PORT, SHOULD_FAIL, EXP_AI_FAMILY, \
+		   EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL)			\
+  { ADDR, { EXP_HOST, EXP_PORT }, SHOULD_FAIL, EXP_AI_FAMILY, \
+    EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL }
+
+/* Initialize an unprefixed entry.  In this case, we don't expect
+   anything on the 'struct addrinfo' HINT.  */
+#define INIT_UNPREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, 0, 0, 0)
+
+/* Initialized an unprefixed IPv6 entry.  In this case, we don't
+   expect anything on the 'struct addrinfo' HINT.  */
+#define INIT_UNPREFIXED_IPV6_ENTRY(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, AF_INET6, 0, 0)
+
+/* Initialize a prefixed entry.  */
+#define INIT_PREFIXED_ENTRY(ADDR, EXP_HOST, EXP_PORT, EXP_AI_FAMILY, \
+			    EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL) \
+  INIT_ENTRY (ADDR, EXP_HOST, EXP_PORT, false, EXP_AI_FAMILY, \
+	      EXP_AI_SOCKTYPE, EXP_AI_PROTOCOL)
+
+/* Initialize an entry prefixed with "tcp4:".  */
+#define INIT_PREFIXED_IPV4_TCP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET, SOCK_STREAM, \
+		       IPPROTO_TCP)
+
+/* Initialize an entry prefixed with "tcp6:".  */
+#define INIT_PREFIXED_IPV6_TCP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET6, SOCK_STREAM, \
+		       IPPROTO_TCP)
+
+/* Initialize an entry prefixed with "udp4:".  */
+#define INIT_PREFIXED_IPV4_UDP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET, SOCK_DGRAM, \
+		       IPPROTO_UDP)
+
+/* Initialize an entry prefixed with "udp6:".  */
+#define INIT_PREFIXED_IPV6_UDP(ADDR, EXP_HOST, EXP_PORT) \
+  INIT_PREFIXED_ENTRY (ADDR, EXP_HOST, EXP_PORT, AF_INET6, SOCK_DGRAM, \
+		       IPPROTO_UDP)
+
+/* Initialize a bogus entry, i.e., a connection spec that should
+   fail.  */
+#define INIT_BOGUS_ENTRY(ADDR) \
+  INIT_ENTRY (ADDR, "", "", true, 0, 0, 0)
+
+/* The variable which holds all of our tests.  */
+
+static const parse_conn_test conn_test[] =
+  {
+    /* Unprefixed addresses.  */
+
+    /* IPv4, host and port present.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* IPv4, only host.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1", "127.0.0.1", ""),
+    /* IPv4, missing port.  */
+    INIT_UNPREFIXED_ENTRY ("127.0.0.1:", "127.0.0.1", ""),
+
+    /* IPv6, host and port present, no brackets.  */
+    INIT_UNPREFIXED_ENTRY ("::1:1234", "::1", "1234"),
+    /* IPv6, missing port, no brackets.  */
+    INIT_UNPREFIXED_ENTRY ("::1:", "::1", ""),
+    /* IPv6, host and port present, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]:1234", "::1", "1234"),
+    /* IPv6, only host, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]", "::1", ""),
+    /* IPv6, missing port, with brackets.  */
+    INIT_UNPREFIXED_IPV6_ENTRY ("[::1]:", "::1", ""),
+
+    /* Unspecified, only port.  */
+    INIT_UNPREFIXED_ENTRY (":1234", "localhost", "1234"),
+
+    /* Prefixed addresses.  */
+
+    /* Prefixed "tcp4:" IPv4, host and port presents.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* Prefixed "tcp4:" IPv4, only port.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4::1234", "localhost", "1234"),
+    /* Prefixed "tcp4:" IPv4, only host.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1", "127.0.0.1", ""),
+    /* Prefixed "tcp4:" IPv4, missing port.  */
+    INIT_PREFIXED_IPV4_TCP ("tcp4:127.0.0.1:", "127.0.0.1", ""),
+
+    /* Prefixed "udp4:" IPv4, host and port present.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1:1234", "127.0.0.1", "1234"),
+    /* Prefixed "udp4:" IPv4, only port.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4::1234", "localhost", "1234"),
+    /* Prefixed "udp4:" IPv4, only host.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1", "127.0.0.1", ""),
+    /* Prefixed "udp4:" IPv4, missing port.  */
+    INIT_PREFIXED_IPV4_UDP ("udp4:127.0.0.1:", "127.0.0.1", ""),
+
+
+    /* Prefixed "tcp6:" IPv6, host and port present.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:::1:1234", "::1", "1234"),
+    /* Prefixed "tcp6:" IPv6, only port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6::1234", "localhost", "1234"),
+    /* Prefixed "tcp6:" IPv6, only host.  */
+    //INIT_PREFIXED_IPV6_TCP ("tcp6:::1", "::1", ""),
+    /* Prefixed "tcp6:" IPv6, missing port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:::1:", "::1", ""),
+
+    /* Prefixed "udp6:" IPv6, host and port present.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:::1:1234", "::1", "1234"),
+    /* Prefixed "udp6:" IPv6, only port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6::1234", "localhost", "1234"),
+    /* Prefixed "udp6:" IPv6, only host.  */
+    //INIT_PREFIXED_IPV6_UDP ("udp6:::1", "::1", ""),
+    /* Prefixed "udp6:" IPv6, missing port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:::1:", "::1", ""),
+
+    /* Prefixed "tcp6:" IPv6 with brackets, host and port present.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]:1234", "::1", "1234"),
+    /* Prefixed "tcp6:" IPv6 with brackets, only host.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]", "::1", ""),
+    /* Prefixed "tcp6:" IPv6 with brackets, missing port.  */
+    INIT_PREFIXED_IPV6_TCP ("tcp6:[::1]:", "::1", ""),
+
+    /* Prefixed "udp6:" IPv6 with brackets, host and port present.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]:1234", "::1", "1234"),
+    /* Prefixed "udp6:" IPv6 with brackets, only host.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]", "::1", ""),
+    /* Prefixed "udp6:" IPv6 with brackets, missing port.  */
+    INIT_PREFIXED_IPV6_UDP ("udp6:[::1]:", "::1", ""),
+
+
+    /* Bogus addresses.  */
+    INIT_BOGUS_ENTRY ("tcp6:[::1]123:44"),
+    INIT_BOGUS_ENTRY ("[::1"),
+    INIT_BOGUS_ENTRY ("tcp6:::1]:"),
+  };
+
+/* Test a connection spec C.  */
+
+static void
+test_conn (const parse_conn_test &c)
+{
+  struct addrinfo hint;
+  parsed_connection_spec ret;
+
+  memset (&hint, 0, sizeof (hint));
+
+  TRY
+    {
+      ret = parse_connection_spec (c.connspec, &hint);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* If we caught an error, we should check if this connection
+	 spec was supposed to fail.  */
+      SELF_CHECK (c.should_fail);
+      return;
+    }
+  END_CATCH
+
+  SELF_CHECK (!c.should_fail);
+  SELF_CHECK (ret.host_str == c.expected_result.host_str);
+  SELF_CHECK (ret.port_str == c.expected_result.port_str);
+  SELF_CHECK (hint.ai_family == c.exp_ai_family);
+  SELF_CHECK (hint.ai_socktype == c.exp_ai_socktype);
+  SELF_CHECK (hint.ai_protocol == c.exp_ai_protocol);
+}
+
+/* Run the tests associated with parsing connection specs.  */
+
+static void
+run_tests ()
+{
+  for (const parse_conn_test &c : conn_test)
+    test_conn (c);
+}
+} /* namespace parse_connection_spec_tests */
+} /* namespace selftests */
+
+void
+_initialize_parse_connection_spec_selftests ()
+{
+  selftests::register_test ("parse_connection_spec",
+			    selftests::parse_connection_spec_tests::run_tests);
+}
-- 
2.14.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4] Implement IPv6 support for GDB/gdbserver
  2018-07-11 19:16 ` [PATCH v4] " Sergio Durigan Junior
@ 2018-07-11 21:48   ` Pedro Alves
  2018-07-11 23:43     ` Sergio Durigan Junior
  0 siblings, 1 reply; 27+ messages in thread
From: Pedro Alves @ 2018-07-11 21:48 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Eli Zaretskii, Jan Kratochvil, Paul Fertser, Tsutomu Seki,
	Armand Scholtes

On 07/11/2018 08:16 PM, Sergio Durigan Junior wrote:
> Changes since v3:
> 
> - No longer use gdb::optional as a return type for try_connect; use
>   int instead.
> 
> - Fix a bunch of typos and thinkos.
> 

Thanks.  

> +/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
> +
> +class scoped_free_addrinfo
> +{
> +public:
> +  /* Default constructor.  */
> +  scoped_free_addrinfo (struct addrinfo *ainfo)
> +    : m_res (ainfo)

explicit.

>  
>        if (
>  #ifdef USE_WIN32API
> -	  /* Under Windows, calling "connect" with a non-blocking socket
> -	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
> +	  /* Any other error (except EINPROGRESS) will be "swallowed"
> +	     here.  We return without specifying a return value, and
> +	     set errno if the caller wants to inspect what
> +	     happened.  */

This comment should be outside "#ifdef USE_WIN32API", and the
existing comment "Under Windows, ... WSAEWOULDBLOCK" should be
preserved.

>  	  err != WSAEWOULDBLOCK
>  #else
>  	  err != EINPROGRESS
>  #endif
>  	  )
>  	{
> +	  close (sock);
>  	  errno = err;
> -	  net_close (scb);
>  	  return -1;
>  	}
>  

> +  /* On Windows, the fourth parameter to getsockopt is a "char *";
> +     on UNIX systems it is generally "void *".  The cast to "char *"
> +     is OK everywhere, since in C++ any data pointer type can be
> +     implicitly converted to "void *".  */
> +  int ret = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
> +
> +  if (ret < 0)
> +    {
> +      close (sock);
> +      errno = ret;

I don't think this "errno = ret" here is right.  getsockopt returns
-1 on error with errno already set.  Pedantically, close can 
fail and set errno, so the correct thing to do is:

 int save_errno = errno;
 close (sock);
 errno = save_errno;
 return -1;

> +      return -1;
> +    }
> +  else if (ret == 0 && err != 0)
> +    {
> +      close (sock);
> +      errno = err;
> +
> +      /* Check if the connection was refused.  */
> +      if (
>  #ifdef USE_WIN32API
> -	    && err == WSAECONNREFUSED
> +	  err == WSAECONNREFUSED
>  #else
> -	    && err == ECONNREFUSED
> +	  err == ECONNREFUSED
>  #endif
> -	    && wait_for_connect (NULL, &polls) >= 0)
> -	  {
> -	    close (scb->fd);
> -	    goto retry;
> -	  }
> -	if (err)
> -	  errno = err;
> -	net_close (scb);
> +	  )
>  	return -1;> -      }
> -  } 
> +      else
> +	{
> +	  /* If we have any other kind of error, just return nothing.  */
> +	  return -1;
> +	}

Aren't both the then/else branches exactly the same, i.e.,
just "return -1;" ?

Seems like you can all the "if then/else", and just return -1;

> +    }
> +
> +  /* The connection succeeded.  Return the socket.  */
> +  return sock;
> +}
> +
OK with the issues above fixed.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v4] Implement IPv6 support for GDB/gdbserver
  2018-07-11 21:48   ` Pedro Alves
@ 2018-07-11 23:43     ` Sergio Durigan Junior
  0 siblings, 0 replies; 27+ messages in thread
From: Sergio Durigan Junior @ 2018-07-11 23:43 UTC (permalink / raw)
  To: Pedro Alves
  Cc: GDB Patches, Eli Zaretskii, Jan Kratochvil, Paul Fertser,
	Tsutomu Seki, Armand Scholtes

On Wednesday, July 11 2018, Pedro Alves wrote:

> On 07/11/2018 08:16 PM, Sergio Durigan Junior wrote:
>> Changes since v3:
>> 
>> - No longer use gdb::optional as a return type for try_connect; use
>>   int instead.
>> 
>> - Fix a bunch of typos and thinkos.
>> 
>
> Thanks.  
>
>> +/* Helper class to guarantee that we always call 'freeaddrinfo'.  */
>> +
>> +class scoped_free_addrinfo
>> +{
>> +public:
>> +  /* Default constructor.  */
>> +  scoped_free_addrinfo (struct addrinfo *ainfo)
>> +    : m_res (ainfo)
>
> explicit.

Done.

>>  
>>        if (
>>  #ifdef USE_WIN32API
>> -	  /* Under Windows, calling "connect" with a non-blocking socket
>> -	     results in WSAEWOULDBLOCK, not WSAEINPROGRESS.  */
>> +	  /* Any other error (except EINPROGRESS) will be "swallowed"
>> +	     here.  We return without specifying a return value, and
>> +	     set errno if the caller wants to inspect what
>> +	     happened.  */
>
> This comment should be outside "#ifdef USE_WIN32API", and the
> existing comment "Under Windows, ... WSAEWOULDBLOCK" should be
> preserved.

I don't know why I removed the existing comment.  Sorry about that.  I
readded it and rearranged them accordingly.

>>  	  err != WSAEWOULDBLOCK
>>  #else
>>  	  err != EINPROGRESS
>>  #endif
>>  	  )
>>  	{
>> +	  close (sock);
>>  	  errno = err;
>> -	  net_close (scb);
>>  	  return -1;
>>  	}
>>  
>
>> +  /* On Windows, the fourth parameter to getsockopt is a "char *";
>> +     on UNIX systems it is generally "void *".  The cast to "char *"
>> +     is OK everywhere, since in C++ any data pointer type can be
>> +     implicitly converted to "void *".  */
>> +  int ret = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
>> +
>> +  if (ret < 0)
>> +    {
>> +      close (sock);
>> +      errno = ret;
>
> I don't think this "errno = ret" here is right.  getsockopt returns
> -1 on error with errno already set.

That's true, mistake on my part.

>  Pedantically, close can 
> fail and set errno, so the correct thing to do is:
>
>  int save_errno = errno;
>  close (sock);
>  errno = save_errno;
>  return -1;

Right, will do that.

>> +      return -1;
>> +    }
>> +  else if (ret == 0 && err != 0)
>> +    {
>> +      close (sock);
>> +      errno = err;
>> +
>> +      /* Check if the connection was refused.  */
>> +      if (
>>  #ifdef USE_WIN32API
>> -	    && err == WSAECONNREFUSED
>> +	  err == WSAECONNREFUSED
>>  #else
>> -	    && err == ECONNREFUSED
>> +	  err == ECONNREFUSED
>>  #endif
>> -	    && wait_for_connect (NULL, &polls) >= 0)
>> -	  {
>> -	    close (scb->fd);
>> -	    goto retry;
>> -	  }
>> -	if (err)
>> -	  errno = err;
>> -	net_close (scb);
>> +	  )
>>  	return -1;> -      }
>> -  } 
>> +      else
>> +	{
>> +	  /* If we have any other kind of error, just return nothing.  */
>> +	  return -1;
>> +	}
>
> Aren't both the then/else branches exactly the same, i.e.,
> just "return -1;" ?
>
> Seems like you can all the "if then/else", and just return -1;

That's true.  And also, in this case, I have to guard the errno checking
that happens on net_open using #ifdef USE_WIN32API...#else...#endif.
Done that as well.

>> +    }
>> +
>> +  /* The connection succeeded.  Return the socket.  */
>> +  return sock;
>> +}
>> +
> OK with the issues above fixed.

Thanks for the reviews.  Pushed.

c7ab0aef11d91b637bf091aa9176b8dc4aadee46

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2018-07-11 23:43 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-23 21:48 [PATCH] Implement IPv6 support for GDB/gdbserver Sergio Durigan Junior
2018-05-23 23:40 ` Eli Zaretskii
2018-05-24  0:41   ` Sergio Durigan Junior
2018-05-24 16:54     ` Eli Zaretskii
2018-05-25  1:57       ` Sergio Durigan Junior
2018-05-31 20:10 ` Sergio Durigan Junior
2018-06-06 12:26 ` Pedro Alves
2018-06-08  1:13   ` Sergio Durigan Junior
2018-06-08 13:53     ` Pedro Alves
2018-06-08 17:47       ` Sergio Durigan Junior
2018-06-08 18:44         ` Pedro Alves
2018-06-08 19:28           ` Pedro Alves
2018-06-08 19:51             ` Pedro Alves
2018-06-08 20:43               ` Sergio Durigan Junior
2018-06-08 21:21           ` Sergio Durigan Junior
2018-06-08 21:51             ` Pedro Alves
2018-06-08 22:01               ` Sergio Durigan Junior
2018-06-15  0:25 ` [PATCH v2] " Sergio Durigan Junior
2018-06-15  7:12   ` Eli Zaretskii
2018-06-20 15:24   ` Pedro Alves
2018-06-21  4:54     ` Sergio Durigan Junior
2018-07-07 20:47 ` [PATCH v3] " Sergio Durigan Junior
2018-07-11 12:55   ` Pedro Alves
2018-07-11 19:13     ` Sergio Durigan Junior
2018-07-11 19:16 ` [PATCH v4] " Sergio Durigan Junior
2018-07-11 21:48   ` Pedro Alves
2018-07-11 23:43     ` Sergio Durigan Junior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).