[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