Permalink
Browse files

Support IPv6 addresses with scope IDs

The scope ID specifies the interface that the system should use when
sending packets to a link-local address. An example of an IPv6 address
with scope ID is "fe80::a00:27ff:fe84:be2%eth0".

Supporting this requires two major code changes:
* The scope ID is part of the sockaddr_in6 structure, not the in6_addr
structure. We therefore need to store socket addresses instead of host
addresses in nc_host_t. Note that since we store port numbers separately
(and we can have a port range instead of a single port), the port part
of the socket addresses stored in nc_host_t is undefined (0).
* In netcat_resolvehost we need to use getaddrinfo (resp. getnameinfo)
instead of inet_pton and gethostbyname (resp. inet_ntop and
gethostbyaddr), because only those functions support scope IDs.
  • Loading branch information...
veithen committed Dec 30, 2013
1 parent f96eef3 commit f26fbe528100ae04c50d80575fb1e38f3dd3f24d
Showing with 138 additions and 135 deletions.
  1. +1 −6 src/netcat.c
  2. +33 −32 src/netcat.h
  3. +10 −8 src/netcore.c
  4. +94 −89 src/network.c
View
@@ -574,12 +574,7 @@ int main(int argc, char *argv[])
netcat_mode = NETCAT_CONNECT;
/* first check that a host parameter was given */
- // TODO: find a more elegant way to check the IPv6 address
- if (!remote_host.host.iaddrs[0].s_addr &&
- !remote_host.host6.iaddrs[0].__in6_u.__u6_addr32[0] &&
- !remote_host.host6.iaddrs[0].__in6_u.__u6_addr32[1] &&
- !remote_host.host6.iaddrs[0].__in6_u.__u6_addr32[2] &&
- !remote_host.host6.iaddrs[0].__in6_u.__u6_addr32[3]) {
+ if (!remote_host.addrs[0].saddr_len) {
/* FIXME: The Networking specifications state that host address "0" is a
valid host to connect to but this broken check will assume as not
specified. */
View
@@ -65,11 +65,13 @@
/* This must be defined to the longest possible internet address length in
string notation.
- Bugfix: Looks like Solaris 7 doesn't define this standard. It's ok to use
- the following workaround since this is going to change to introduce IPv6
- support. */
-#ifdef INET_ADDRSTRLEN
-# define NETCAT_ADDRSTRLEN INET_ADDRSTRLEN
+ Note: in.h normally defines INET_ADDRSTRLEN and INET6_ADDRSTRLEN, but they
+ are not available on all platforms and don't take into account scope IDs. */
+#ifdef USE_IPV6
+// INET6_ADDRSTRLEN is defined as 46 to accomodate for IPv6 addresses such as
+// 0000:0000:0000:0000:0000:0000:192.168.100.201. In addition, we need some space
+// for the optional scope ID.
+# define NETCAT_ADDRSTRLEN 64
#else
# define NETCAT_ADDRSTRLEN 16
#endif
@@ -208,44 +210,43 @@ typedef struct {
} nc_buffer_t;
/**
- * Standard Netcat hosts record.
+ * Standard Netcat address record.
*
- * This is the standard netcat hosts record. It contains an "authoritative"
- * `name' field, which may be empty, and a list of IP addresses in the network
- * notation and in the dotted string notation.
+ * It represents an IPv4 or IPv6 address. The address is stored as a socket address
+ * because in IPv6 we need to store the scope ID as well (the scope ID is part of
+ * the sockaddr_in6 structure, not the in6_addr structure). By definition, the port
+ * in the socket address is undefined (and typically 0). It is stored separately
+ * from the host address.
+ *
+ * A string representation of the address (including the scope ID for IPv6) is also
+ * included.
*/
typedef struct {
- char name[MAXHOSTNAMELEN]; /**< Dns name. */
- char addrs[MAXINETADDRS][NETCAT_ADDRSTRLEN]; /**< Ascii-format IP
- * addresses. */
- struct in_addr iaddrs[MAXINETADDRS]; /**< Real addresses. */
-} nc_host4_t;
-
+ /** Ascii-format IP address. */
+ char addrstr[NETCAT_ADDRSTRLEN];
+ /** Socket address (IPv4 or IPv6). */
+ union {
+ struct sockaddr saddr;
+ struct sockaddr_in saddr_in;
#ifdef USE_IPV6
+ struct sockaddr_in6 saddr_in6;
+#endif
+ };
+ /** Socket address length. */
+ socklen_t saddr_len;
+} nc_addr_t;
/**
- * Standard Netcat hosts record for IPv6 domain.
+ * Standard Netcat hosts record.
*
- * This is the host record for IPv6 hosts, which have a slightly different
- * structure. For example they are 128 bits long while IPv4 addresses are
- * just 32 bits long.
+ * This is the standard netcat hosts record. It contains an "authoritative"
+ * `name' field, which may be empty, and a list of addresses.
*/
typedef struct {
- char name[MAXHOSTNAMELEN]; /**< Dns name. */
- char addrs[MAXINETADDRS][NETCAT_ADDRSTRLEN]; /**< Ascii-format IP
- * addresses. */
- struct in6_addr iaddrs[MAXINETADDRS]; /**< Real addresses. */
-} nc_host6_t;
-
-#endif
-
-typedef struct { /* FIXME: shouldn't become an union??? */
- nc_host4_t host;
-#ifdef USE_IPV6
- nc_host6_t host6;
-#endif
+ char name[MAXHOSTNAMELEN]; /**< Dns name. */
+ nc_addr_t addrs[MAXINETADDRS]; /**< IP addresses. */
} nc_host_t;
/**
View
@@ -47,7 +47,7 @@ static int core_udp_connect(nc_sock_t *ncsock)
return -1;
/* only call bind if it is really needed */
- if (ncsock->local_port.netnum || ncsock->local.host.iaddrs[0].s_addr) {
+ if (ncsock->local_port.netnum || ncsock->local.addrs[0].saddr_len) {
ret = netcat_bind(sock, ncsock->domain, &ncsock->local, &ncsock->local_port);
if (ret < 0)
goto err;
@@ -256,9 +256,10 @@ static int core_udp_listen(nc_sock_t *ncsock)
memset(&dup_socket, 0, sizeof(dup_socket));
dup_socket.domain = ncsock->domain;
dup_socket.proto = ncsock->proto;
- memcpy(&dup_socket.local.host.iaddrs[0], &local_addr.sin_addr,
+ // FIXME: only correct for IPv4
+ memcpy(&dup_socket.local.addrs[0].saddr_in.sin_addr, &local_addr.sin_addr,
sizeof(local_addr));
- memcpy(&dup_socket.remote.host.iaddrs[0], &rem_addr.sin_addr,
+ memcpy(&dup_socket.remote.addrs[0].saddr_in.sin_addr, &rem_addr.sin_addr,
sizeof(local_addr));
dup_socket.local_port.netnum = local_addr.sin_port;
dup_socket.local_port.num = ntohs(local_addr.sin_port);
@@ -315,7 +316,7 @@ static int core_tcp_connect(nc_sock_t *ncsock)
avoid one bind(2) call. */
sock = netcat_socket_new_connect(ncsock->domain, ncsock->proto,
&ncsock->remote, &ncsock->port,
- (ncsock->local.host.iaddrs[0].s_addr ? &ncsock->local : NULL),
+ (ncsock->local.addrs[0].saddr_len ? &ncsock->local : NULL),
&ncsock->local_port,
&ncsock->opts);
@@ -453,9 +454,10 @@ static int core_tcp_listen(nc_sock_t *ncsock)
they are assumed to be the only IP and port(s) allowed to connect to
this socket. See documentation for more information. */
- if ((ncsock->remote.host.iaddrs[0].s_addr &&
- memcmp(&ncsock->remote.host.iaddrs[0], &myaddr.sin_addr,
- sizeof(ncsock->remote.host.iaddrs[0]))) ||
+ // FIXME: IPv4 only
+ if ((ncsock->remote.addrs[0].saddr.sa_family == AF_INET &&
+ memcmp(&ncsock->remote.addrs[0].saddr_in.sin_addr, &myaddr.sin_addr,
+ sizeof(ncsock->remote.addrs[0].saddr_in.sin_addr))) ||
(ncsock->remote_ports != NULL && !netcat_ports_isset(ncsock->remote_ports, ntohs(myaddr.sin_port)))) {
ncprint(NCPRINT_VERB2, _("Unwanted connection from %s:%hu (refused)"),
netcat_inet_ntop(AF_INET, &myaddr.sin_addr), ntohs(myaddr.sin_port));
@@ -469,7 +471,7 @@ static int core_tcp_listen(nc_sock_t *ncsock)
//netcat_resolvehost(&ncsock->remote, NULL, &myaddr.sin_addr);
ncprint(NCPRINT_VERB1, _("Connection from %s:%hu"),
- ncsock->remote.host.name, ncsock->port.num);
+ ncsock->remote.name, ncsock->port.num);
/* with zero I/O mode we don't really accept any connection */
if (opt_zero)
Oops, something went wrong.

0 comments on commit f26fbe5

Please sign in to comment.