From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5651 invoked by alias); 31 May 2018 18:35:59 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 5633 invoked by uid 89); 31 May 2018 18:35:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.7 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_LAZY_DOMAIN_SECURITY,KAM_SHORT,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=efforts, listen, Connection, simulator X-HELO: mx1.redhat.com Received: from mx3-rdu2.redhat.com (HELO mx1.redhat.com) (66.187.233.73) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 31 May 2018 18:35:52 +0000 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 00D1D8011059; Thu, 31 May 2018 18:35:51 +0000 (UTC) Received: from localhost (unused-10-15-17-196.yyz.redhat.com [10.15.17.196]) by smtp.corp.redhat.com (Postfix) with ESMTP id B2FF784427; Thu, 31 May 2018 18:35:48 +0000 (UTC) From: Sergio Durigan Junior To: GDB Patches Cc: Pedro Alves , Eli Zaretskii , Jan Kratochvil , Paul Fertser , Tsutomu Seki Subject: Re: [PATCH] Implement IPv6 support for GDB/gdbserver References: <20180523185719.22832-1-sergiodj@redhat.com> Date: Thu, 31 May 2018 20:10:00 -0000 In-Reply-To: <20180523185719.22832-1-sergiodj@redhat.com> (Sergio Durigan Junior's message of "Wed, 23 May 2018 14:57:19 -0400") Message-ID: <87a7sfvfjv.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.3 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-IsSubscribed: yes X-SW-Source: 2018-05/txt/msg00869.txt.bz2 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 > Jan Kratochvil > Paul Fertser > Tsutomu Seki > > * 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 > Jan Kratochvil > Paul Fertser > Tsutomu Seki > > * 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 > Jan Kratochvil > Paul Fertser > Tsutomu Seki > > * 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 > Jan Kratochvil > Paul Fertser > Tsutomu Seki > > * 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 . */ > + > +#include "common-defs.h" > +#include "netstuff.h" > + > +#ifdef USE_WIN32API > +#include > +#include > +#else > +#include > +#include > +#include > +#include > +#include > +#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 . */ > + > +#ifndef NETSTUFF_H > +#define NETSTUFF_H > + > +#include > + > +/* 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 > +#include > #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 > #if HAVE_SYS_IOCTL_H > #include > @@ -63,6 +65,7 @@ > > #if USE_WIN32API > #include > +#include > #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 > > @@ -39,6 +40,7 @@ > > #ifdef USE_WIN32API > #include > +#include > #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/