[m-rev.] [PATCH] [net] Add support for IPv6

Paul Bone paul at bone.id.au
Mon Apr 13 11:23:56 AEST 2015


extras/net/types.m:
    Add IPv6 support

extras/net/test_lookups.m:
    Conform to above change.
---
 extras/net/test_lookups.m |  15 ++-
 extras/net/types.m        | 282 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 267 insertions(+), 30 deletions(-)

diff --git a/extras/net/test_lookups.m b/extras/net/test_lookups.m
index 537ed53..d40c1c2 100644
--- a/extras/net/test_lookups.m
+++ b/extras/net/test_lookups.m
@@ -22,8 +22,17 @@ main(!IO) :-
     getprotobyname(TCPS, TCP, !IO),
     io.format("get_proto_by_name(""%s"", %s, !IO).\n",
         [s(TCPS), s(string(TCP))], !IO),
+
+    io.format("in_addr_any: %s\n", [s(to_string(in_addr_any))], !IO),
+    io.format("in_addr_loopback: %s\n", [s(to_string(in_addr_loopback))], !IO),
+    io.format("in_addr_broadcast: %s\n", [s(to_string(in_addr_broadcast))],
+        !IO),
+    io.format("in6_addr_any: %s\n", [s(to_string(in6_addr_any))], !IO),
+    io.format("in6_addr_loopback: %s\n", [s(to_string(in6_addr_loopback))],
+        !IO),
+
     lookup_host_and_service("www.google.com", string_service("http"),
-        yes(fam_inet), no, GAIResultHostService),
+        no, no, GAIResultHostService),
     (
         GAIResultHostService = ok(HostServiceResults),
         io.write_string("www.google.com:\n", !IO),
@@ -53,8 +62,8 @@ write_lookup_result(lookup_result(Family, Socktype, ProtoNum, SockAddr),
             s(SockStr)],
         !IO),
     ProtoName = string(ProtoNum),
-    ( ipv4_sockaddr(InAddr, Port, SockAddr) ->
-        SockStr = format("%s:%d", [s(to_string(InAddr)), i(Port)])
+    ( sockaddr_get_addr_port(SockAddr, Addr, Port) ->
+        SockStr = format("%s:%d", [s(to_string(Addr)), i(Port)])
     ;
         SockStr = "unknown"
     ).
diff --git a/extras/net/types.m b/extras/net/types.m
index aa6814a..507ff53 100644
--- a/extras/net/types.m
+++ b/extras/net/types.m
@@ -6,7 +6,7 @@
 % Public License - see the file COPYING.LIB
 %-----------------------------------------------------------------------------%
 %
-% Module: types 
+% Module: types
 % Main Author:  Paul Bone <paul at bone.id.au>
 % Stability:    low
 %
@@ -70,10 +70,39 @@
 
 %-----------------------------------------------------------------------------%
 
-	% An IPv4 Address.
-	%
+    % An address type can be converted to and from strings.
+    %
+    % The to and from string predicates are not guaranteed to be reciprocal.
+    % Therefore they should not be reverse modes of one-another.
+    %
+:- typeclass addr(A) where [
+    pred from_string(string::in, A::uo) is semidet,
+
+    pred to_string(A::in, string::uo) is det
+].
+
+:- func to_string(A) = string <= addr(A).
+
+    % Existentially typed from_string predicate.
+    %
+    % This will try to to recognise the address. It tries IPv4 then IPv6.
+    %
+:- some [A] pred exist_from_string(string::in, A::uo) is semidet => addr(A).
+
+%-----------------------------------------------------------------------------%
+
+    % An IPv4 Address.
+    %
 :- type in_addr.
 
+    % The conversion code in this typeclass instance will convert an in_addr
+    % into a dotted-decimal format.
+    %
+    % The dotted-decimal format is the typical format with four decimal
+    % numbers separated by dots.
+    %
+:- instance addr(in_addr).
+
     % Constant (special) IP addresses:
     %  + the wildcard address:  0.0.0.0
     %  + the loopback address:  127.0.0.1
@@ -89,20 +118,29 @@
     % structure.
     %
     % The numbers-and-dots format is general and allows up to four numbers
-    % separated by dots, the numbers may be decimal, octal or hexadecimal, 
+    % separated by dots, the numbers may be decimal, octal or hexadecimal,
     % See inet_aton(3).
-	%
-:- pred from_string(string::in, in_addr::uo) is semidet.
+    %
+:- pred in_addr_from_string(string::in, in_addr::uo) is semidet.
+
+%-----------------------------------------------------------------------------%
 
-    % Convert an in_addr into a dotted-decimal format.  This predicate and
-    % inet_aton are not reciprocal, therefore this should not be a reverse
-    % mode of the above.
+    % An IPv6 address.
     %
-    % The dotted-decimal format is the typical format with four decimal
-    % numbers separated by dots.
-	%
-:- pred to_string(in_addr::in, string::uo) is det.
-:- func to_string(in_addr) = string.
+:- type in6_addr.
+
+:- instance addr(in6_addr).
+
+    % Constant IPv6 addresses
+    %  + the wildcard address:  ::0
+    %  + the loopback address:  ::1
+    %
+    % See also ipv6(7).
+    %
+:- func in6_addr_any = in6_addr.
+:- func in6_addr_loopback = in6_addr.
+
+:- pred in6_addr_from_string(string::in, in6_addr::uo) is semidet.
 
 %-----------------------------------------------------------------------------%
 
@@ -122,6 +160,32 @@
 
 :- func ipv4_sockaddr(in_addr, port) = sockaddr.
 
+    % Construct and deconstruct ipv6 sockaddrs.  Deconstruction fails if
+    % this is not an ipv6 socket address.
+    %
+:- pred ipv6_sockaddr(in6_addr, port, sockaddr).
+:- mode ipv6_sockaddr(in, in, uo) is det.
+:- mode ipv6_sockaddr(out, out, in) is semidet.
+
+:- func ipv6_sockaddr(in6_addr, port) = sockaddr.
+
+:- some [A] pred sockaddr_get_addr_port(sockaddr::in, A::out, port::out)
+        is semidet
+    => addr(A).
+
+    % Get the node address from a socket address.
+    %
+    % If the node address type is unknown or unsupported this call will
+    % fail.
+    %
+:- some [A] pred sockaddr_get_addr(sockaddr::in, A::out) is semidet
+    => addr(A).
+
+    % Retrive the port number from the socket address.  Not all socket
+    % addresses have port numbers so this call may fail.
+    %
+:- pred sockaddr_get_port(sockaddr::in, port::out) is semidet.
+
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 :- implementation.
@@ -236,11 +300,37 @@
 
 %-----------------------------------------------------------------------------%
 
+to_string(Addr) = String :-
+    to_string(Addr, String).
+
+    % This type allows code in this mdule to return the existentially
+    % quantified address.
+    %
+:- type univ_address
+    --->    some [A] (univ_address(A) => addr(A)).
+
+exist_from_string(String, Addr) :-
+    ( in_addr_from_string(String, AddrPrime) ->
+        UAddr = 'new univ_address'(AddrPrime)
+    ; in6_addr_from_string(String, AddrPrime) ->
+        UAddr = 'new univ_address'(AddrPrime)
+    ;
+        false
+    ),
+    univ_address(Addr) = UAddr.
+
+%-----------------------------------------------------------------------------%
+
 :- pragma foreign_type("C",
     in_addr,
     "struct in_addr*",
     [can_pass_as_mercury_type]).
 
+:- instance addr(in_addr) where [
+    pred(from_string/2) is in_addr_from_string,
+    pred(to_string/2) is in_addr_to_string
+].
+
 %-----------------------------------------------------------------------------%
 
 :- pragma foreign_proc("C",
@@ -248,7 +338,7 @@
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     Addr = MR_GC_NEW(struct in_addr);
-    Addr->s_addr = INADDR_ANY;
+    Addr->s_addr = htonl(INADDR_ANY);
 ").
 
 :- pragma foreign_proc("C",
@@ -256,7 +346,7 @@
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     Addr = MR_GC_NEW(struct in_addr);
-    Addr->s_addr = INADDR_LOOPBACK;
+    Addr->s_addr = htonl(INADDR_LOOPBACK);
 ").
 
 :- pragma foreign_proc("C",
@@ -264,14 +354,14 @@
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     Addr = MR_GC_NEW(struct in_addr);
-    Addr->s_addr = INADDR_BROADCAST;
+    Addr->s_addr = htonl(INADDR_BROADCAST);
 ").
 
 %-----------------------------------------------------------------------------%
 
 :- pragma foreign_proc("C",
-	from_string(String::in, Addr::uo),
-	[will_not_call_mercury, promise_pure, thread_safe],
+    in_addr_from_string(String::in, Addr::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
 "
     Addr = MR_GC_NEW(struct in_addr);
 
@@ -280,8 +370,11 @@
 
 %-----------------------------------------------------------------------------%
 
-to_string(Addr, String) :-
-    to_string(Addr, String, Success, Errno),
+:- pred in_addr_to_string(in_addr::in, string::uo) is det.
+:- func in_addr_to_string(in_addr) = string.
+
+in_addr_to_string(Addr, String) :-
+    in_addr_to_string(Addr, String, Success, Errno),
     (
         Success = yes
     ;
@@ -289,13 +382,14 @@ to_string(Addr, String) :-
         unexpected($file, $pred,
             "Cannot convert address to string" ++ strerror(Errno))
     ).
-to_string(Addr) = String :-
-    to_string(Addr, String).
+in_addr_to_string(Addr) = String :-
+    in_addr_to_string(Addr, String).
 
-:- pred to_string(in_addr::in, string::uo, bool::out, errno::out) is det.
+:- pred in_addr_to_string(in_addr::in, string::uo, bool::out, errno::out)
+    is det.
 
 :- pragma foreign_proc("C",
-    to_string(Addr::in, String::uo, Success::out, Errno::out),
+    in_addr_to_string(Addr::in, String::uo, Success::out, Errno::out),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     char *temp = MR_GC_malloc_atomic(INET_ADDRSTRLEN);
@@ -312,11 +406,86 @@ to_string(Addr) = String :-
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 
+:- pragma foreign_type("C",
+    in6_addr,
+    "struct in6_addr*",
+    [can_pass_as_mercury_type]).
+
+:- instance addr(in6_addr) where [
+    pred(from_string/2) is in6_addr_from_string,
+    pred(to_string/2) is in6_addr_to_string
+].
+
+%-----------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    in6_addr_any = (Addr::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    Addr = MR_GC_NEW(struct in6_addr);
+    memcpy(Addr, &in6addr_any, sizeof(in6addr_any));
+").
+
+:- pragma foreign_proc("C",
+    in6_addr_loopback = (Addr::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    Addr = MR_GC_NEW(struct in6_addr);
+    memcpy(Addr, &in6addr_loopback, sizeof(in6addr_loopback));
+").
+
+%-----------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    in6_addr_from_string(String::in, Addr::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    Addr = MR_GC_NEW(struct in6_addr);
+
+    SUCCESS_INDICATOR = inet_pton(AF_INET6, String, Addr);
+").
+
+%-----------------------------------------------------------------------------%
+
+:- pred in6_addr_to_string(in6_addr::in, string::uo) is det.
+
+in6_addr_to_string(Addr, String) :-
+    in6_addr_to_string(Addr, String, Success, Errno),
+    (
+        Success = yes
+    ;
+        Success = no,
+        unexpected($file, $pred,
+            "Cannot convert address to string" ++ strerror(Errno))
+    ).
+
+:- pred in6_addr_to_string(in6_addr::in, string::uo, bool::out, errno::out)
+    is det.
+
+:- pragma foreign_proc("C",
+    in6_addr_to_string(Addr::in, String::uo, Success::out, Errno::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    char *temp = MR_GC_malloc_atomic(INET6_ADDRSTRLEN);
+
+    String = (char*)inet_ntop(AF_INET6, Addr, temp, INET6_ADDRSTRLEN);
+    if (String != NULL) {
+        Success = MR_YES;
+    } else {
+        Success = MR_NO;
+        Errno = error();
+    }
+").
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
 :- pragma foreign_decl("C",
 "
     union my_sockaddr {
-        struct sockaddr     raw;
-        struct sockaddr_in  in;
+        struct sockaddr         raw;
+        struct sockaddr_in      in;
+        struct sockaddr_in6     in6;
     };
 
     size_t sock_addr_size(union my_sockaddr *addr);
@@ -328,6 +497,8 @@ to_string(Addr) = String :-
         switch (addr->raw.sa_family) {
             case AF_INET:
                 return sizeof(struct sockaddr_in);
+            case AF_INET6:
+                return sizeof(struct sockaddr_in6);
             default:
                 fprintf(stderr, ""Unhandled family\\n"");
                 abort();
@@ -376,5 +547,62 @@ ipv4_sockaddr(InAddr, Port) = Sockaddr :-
     }
 ").
 
+%-----------------------------------------------------------------------%
+
+ipv6_sockaddr(InAddr, Port) = Sockaddr :-
+    ipv6_sockaddr(InAddr, Port, Sockaddr).
+
+:- pragma foreign_proc("C",
+    ipv6_sockaddr(In6Addr::in, Port::in, Sockaddr::uo),
+    [will_not_call_mercury, thread_safe, promise_pure],
+"
+    Sockaddr = (union my_sockaddr*)MR_GC_NEW(struct sockaddr_in6);
+    Sockaddr->in6.sin6_family = AF_INET6;
+    Sockaddr->in6.sin6_port = htons(Port);
+    Sockaddr->in6.sin6_addr = *In6Addr;
+").
+
+:- pragma foreign_proc("C",
+    ipv6_sockaddr(In6Addr::out, Port::out, Sockaddr::in),
+    [will_not_call_mercury, thread_safe, promise_pure],
+"
+    if (Sockaddr->in6.sin6_family == AF_INET6) {
+        Port = ntohs(Sockaddr->in6.sin6_port);
+        In6Addr = &(Sockaddr->in6.sin6_addr);
+        SUCCESS_INDICATOR = MR_YES;
+    } else {
+        SUCCESS_INDICATOR = MR_NO;
+    }
+").
+
+%-----------------------------------------------------------------------------%
+
+sockaddr_get_addr_port(SockAddr, Addr, Port) :-
+    sockaddr_get_addr(SockAddr, Addr),
+    sockaddr_get_port(SockAddr, Port).
+
+%-----------------------------------------------------------------------------%
+
+sockaddr_get_addr(SockAddr, Addr) :-
+    ( ipv4_sockaddr(AddrPrime, _, SockAddr) ->
+        UAddr = 'new univ_address'(AddrPrime)
+    ; ipv6_sockaddr(AddrPrime, _, SockAddr) ->
+        UAddr = 'new univ_address'(AddrPrime)
+    ;
+        false
+    ),
+    univ_address(Addr) = UAddr.
+
+%-----------------------------------------------------------------------------%
+
+sockaddr_get_port(Sockaddr, Port) :-
+    ( ipv4_sockaddr(_, PortPrime, Sockaddr) ->
+        Port = PortPrime
+    ; ipv6_sockaddr(_, PortPrime, Sockaddr) ->
+        Port = PortPrime
+    ;
+        false
+    ).
+
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
-- 
2.1.4




More information about the reviews mailing list