1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 ** Description: Support for various functions unique to IPv6
13 #if !defined(_PR_INET6) || defined(_PR_INET6_PROBE)
15 static PRIOMethods ipv6_to_v4_tcpMethods
;
16 static PRIOMethods ipv6_to_v4_udpMethods
;
17 static PRDescIdentity _pr_ipv6_to_ipv4_id
;
18 extern PRBool
IsValidNetAddr(const PRNetAddr
* addr
);
19 extern const PRIPv6Addr _pr_in6addr_any
;
20 extern const PRIPv6Addr _pr_in6addr_loopback
;
23 * convert an IPv4-mapped IPv6 addr to an IPv4 addr
25 static void _PR_ConvertToIpv4NetAddr(const PRNetAddr
* src_v6addr
,
26 PRNetAddr
* dst_v4addr
) {
29 PR_ASSERT(PR_AF_INET6
== src_v6addr
->ipv6
.family
);
31 if (PR_IsNetAddrType(src_v6addr
, PR_IpAddrV4Mapped
)) {
32 srcp
= src_v6addr
->ipv6
.ip
.pr_s6_addr
;
33 memcpy((char*)&dst_v4addr
->inet
.ip
, srcp
+ 12, 4);
34 } else if (PR_IsNetAddrType(src_v6addr
, PR_IpAddrAny
)) {
35 dst_v4addr
->inet
.ip
= htonl(INADDR_ANY
);
36 } else if (PR_IsNetAddrType(src_v6addr
, PR_IpAddrLoopback
)) {
37 dst_v4addr
->inet
.ip
= htonl(INADDR_LOOPBACK
);
39 dst_v4addr
->inet
.family
= PR_AF_INET
;
40 dst_v4addr
->inet
.port
= src_v6addr
->ipv6
.port
;
44 * convert an IPv4 addr to an IPv4-mapped IPv6 addr
46 static void _PR_ConvertToIpv6NetAddr(const PRNetAddr
* src_v4addr
,
47 PRNetAddr
* dst_v6addr
) {
50 PR_ASSERT(PR_AF_INET
== src_v4addr
->inet
.family
);
51 dst_v6addr
->ipv6
.family
= PR_AF_INET6
;
52 dst_v6addr
->ipv6
.port
= src_v4addr
->inet
.port
;
54 if (htonl(INADDR_ANY
) == src_v4addr
->inet
.ip
) {
55 dst_v6addr
->ipv6
.ip
= _pr_in6addr_any
;
57 dstp
= dst_v6addr
->ipv6
.ip
.pr_s6_addr
;
59 memset(dstp
+ 10, 0xff, 2);
60 memcpy(dstp
+ 12, (char*)&src_v4addr
->inet
.ip
, 4);
64 static PRStatus PR_CALLBACK
Ipv6ToIpv4SocketBind(PRFileDesc
* fd
,
65 const PRNetAddr
* addr
) {
66 PRNetAddr tmp_ipv4addr
;
67 const PRNetAddr
* tmp_addrp
;
68 PRFileDesc
* lo
= fd
->lower
;
70 if (PR_AF_INET6
!= addr
->raw
.family
) {
71 PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR
, 0);
74 if (PR_IsNetAddrType(addr
, PR_IpAddrV4Mapped
) ||
75 PR_IsNetAddrType(addr
, PR_IpAddrAny
)) {
76 _PR_ConvertToIpv4NetAddr(addr
, &tmp_ipv4addr
);
77 tmp_addrp
= &tmp_ipv4addr
;
79 PR_SetError(PR_NETWORK_UNREACHABLE_ERROR
, 0);
82 return ((lo
->methods
->bind
)(lo
, tmp_addrp
));
85 static PRStatus PR_CALLBACK
Ipv6ToIpv4SocketConnect(PRFileDesc
* fd
,
86 const PRNetAddr
* addr
,
87 PRIntervalTime timeout
) {
88 PRNetAddr tmp_ipv4addr
;
89 const PRNetAddr
* tmp_addrp
;
91 if (PR_AF_INET6
!= addr
->raw
.family
) {
92 PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR
, 0);
95 if (PR_IsNetAddrType(addr
, PR_IpAddrV4Mapped
) ||
96 PR_IsNetAddrType(addr
, PR_IpAddrLoopback
)) {
97 _PR_ConvertToIpv4NetAddr(addr
, &tmp_ipv4addr
);
98 tmp_addrp
= &tmp_ipv4addr
;
100 PR_SetError(PR_NETWORK_UNREACHABLE_ERROR
, 0);
103 return (fd
->lower
->methods
->connect
)(fd
->lower
, tmp_addrp
, timeout
);
106 static PRInt32 PR_CALLBACK
Ipv6ToIpv4SocketSendTo(PRFileDesc
* fd
,
108 PRInt32 amount
, PRIntn flags
,
109 const PRNetAddr
* addr
,
110 PRIntervalTime timeout
) {
111 PRNetAddr tmp_ipv4addr
;
112 const PRNetAddr
* tmp_addrp
;
114 if (PR_AF_INET6
!= addr
->raw
.family
) {
115 PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR
, 0);
118 if (PR_IsNetAddrType(addr
, PR_IpAddrV4Mapped
) ||
119 PR_IsNetAddrType(addr
, PR_IpAddrLoopback
)) {
120 _PR_ConvertToIpv4NetAddr(addr
, &tmp_ipv4addr
);
121 tmp_addrp
= &tmp_ipv4addr
;
123 PR_SetError(PR_NETWORK_UNREACHABLE_ERROR
, 0);
126 return (fd
->lower
->methods
->sendto
)(fd
->lower
, buf
, amount
, flags
, tmp_addrp
,
130 static PRFileDesc
* PR_CALLBACK
Ipv6ToIpv4SocketAccept(PRFileDesc
* fd
,
132 PRIntervalTime timeout
) {
135 PRFileDesc
* newstack
;
136 PRNetAddr tmp_ipv4addr
;
137 PRNetAddr
* addrlower
= NULL
;
139 PR_ASSERT(fd
!= NULL
);
140 PR_ASSERT(fd
->lower
!= NULL
);
142 newstack
= PR_NEW(PRFileDesc
);
143 if (NULL
== newstack
) {
144 PR_SetError(PR_OUT_OF_MEMORY_ERROR
, 0);
147 *newstack
= *fd
; /* make a copy of the accepting layer */
150 addrlower
= &tmp_ipv4addr
;
152 newfd
= (fd
->lower
->methods
->accept
)(fd
->lower
, addrlower
, timeout
);
158 _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr
, addr
);
161 rv
= PR_PushIOLayer(newfd
, PR_TOP_IO_LAYER
, newstack
);
162 PR_ASSERT(PR_SUCCESS
== rv
);
163 return newfd
; /* that's it */
166 static PRInt32 PR_CALLBACK
Ipv6ToIpv4SocketAcceptRead(PRFileDesc
* sd
,
168 PRNetAddr
** ipv6_raddr
,
169 void* buf
, PRInt32 amount
,
170 PRIntervalTime timeout
) {
173 PRNetAddr tmp_ipv4addr
;
174 PRFileDesc
* newstack
;
176 PR_ASSERT(sd
!= NULL
);
177 PR_ASSERT(sd
->lower
!= NULL
);
179 newstack
= PR_NEW(PRFileDesc
);
180 if (NULL
== newstack
) {
181 PR_SetError(PR_OUT_OF_MEMORY_ERROR
, 0);
184 *newstack
= *sd
; /* make a copy of the accepting layer */
186 nbytes
= sd
->lower
->methods
->acceptread(sd
->lower
, nd
, ipv6_raddr
, buf
,
192 tmp_ipv4addr
= **ipv6_raddr
; /* copy */
193 _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr
, *ipv6_raddr
);
195 /* this PR_PushIOLayer call cannot fail */
196 rv
= PR_PushIOLayer(*nd
, PR_TOP_IO_LAYER
, newstack
);
197 PR_ASSERT(PR_SUCCESS
== rv
);
201 static PRStatus PR_CALLBACK
Ipv6ToIpv4SocketGetName(PRFileDesc
* fd
,
202 PRNetAddr
* ipv6addr
) {
204 PRNetAddr tmp_ipv4addr
;
206 result
= (fd
->lower
->methods
->getsockname
)(fd
->lower
, &tmp_ipv4addr
);
207 if (PR_SUCCESS
== result
) {
208 _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr
, ipv6addr
);
209 PR_ASSERT(IsValidNetAddr(ipv6addr
) == PR_TRUE
);
214 static PRStatus PR_CALLBACK
Ipv6ToIpv4SocketGetPeerName(PRFileDesc
* fd
,
215 PRNetAddr
* ipv6addr
) {
217 PRNetAddr tmp_ipv4addr
;
219 result
= (fd
->lower
->methods
->getpeername
)(fd
->lower
, &tmp_ipv4addr
);
220 if (PR_SUCCESS
== result
) {
221 _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr
, ipv6addr
);
222 PR_ASSERT(IsValidNetAddr(ipv6addr
) == PR_TRUE
);
227 static PRInt32 PR_CALLBACK
Ipv6ToIpv4SocketRecvFrom(PRFileDesc
* fd
, void* buf
,
231 PRIntervalTime timeout
) {
232 PRNetAddr tmp_ipv4addr
;
235 result
= (fd
->lower
->methods
->recvfrom
)(fd
->lower
, buf
, amount
, flags
,
236 &tmp_ipv4addr
, timeout
);
238 _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr
, ipv6addr
);
239 PR_ASSERT(IsValidNetAddr(ipv6addr
) == PR_TRUE
);
244 # if defined(_PR_INET6_PROBE)
245 static PRBool ipv6_is_present
;
246 PR_EXTERN(PRBool
) _pr_test_ipv6_socket(void);
248 # if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
249 extern PRStatus
_pr_find_getipnodebyname(void);
252 # if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
253 extern PRStatus
_pr_find_getaddrinfo(void);
256 static PRBool
_pr_probe_ipv6_presence(void) {
257 # if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
258 if (_pr_find_getipnodebyname() != PR_SUCCESS
) {
263 # if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
264 if (_pr_find_getaddrinfo() != PR_SUCCESS
) {
269 return _pr_test_ipv6_socket();
271 # endif /* _PR_INET6_PROBE */
273 static PRCallOnceType _pr_init_ipv6_once
;
275 static PRStatus PR_CALLBACK
_pr_init_ipv6(void) {
276 const PRIOMethods
* stubMethods
;
278 # if defined(_PR_INET6_PROBE)
279 ipv6_is_present
= _pr_probe_ipv6_presence();
280 if (ipv6_is_present
) {
285 _pr_ipv6_to_ipv4_id
= PR_GetUniqueIdentity("Ipv6_to_Ipv4 layer");
286 PR_ASSERT(PR_INVALID_IO_LAYER
!= _pr_ipv6_to_ipv4_id
);
288 stubMethods
= PR_GetDefaultIOMethods();
290 ipv6_to_v4_tcpMethods
= *stubMethods
; /* first get the entire batch */
291 /* then override the ones we care about */
292 ipv6_to_v4_tcpMethods
.connect
= Ipv6ToIpv4SocketConnect
;
293 ipv6_to_v4_tcpMethods
.bind
= Ipv6ToIpv4SocketBind
;
294 ipv6_to_v4_tcpMethods
.accept
= Ipv6ToIpv4SocketAccept
;
295 ipv6_to_v4_tcpMethods
.acceptread
= Ipv6ToIpv4SocketAcceptRead
;
296 ipv6_to_v4_tcpMethods
.getsockname
= Ipv6ToIpv4SocketGetName
;
297 ipv6_to_v4_tcpMethods
.getpeername
= Ipv6ToIpv4SocketGetPeerName
;
299 ipv6_to_v4_tcpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
300 ipv6_to_v4_tcpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
302 ipv6_to_v4_udpMethods
= *stubMethods
; /* first get the entire batch */
303 /* then override the ones we care about */
304 ipv6_to_v4_udpMethods
.connect
= Ipv6ToIpv4SocketConnect
;
305 ipv6_to_v4_udpMethods
.bind
= Ipv6ToIpv4SocketBind
;
306 ipv6_to_v4_udpMethods
.sendto
= Ipv6ToIpv4SocketSendTo
;
307 ipv6_to_v4_udpMethods
.recvfrom
= Ipv6ToIpv4SocketRecvFrom
;
308 ipv6_to_v4_udpMethods
.getsockname
= Ipv6ToIpv4SocketGetName
;
309 ipv6_to_v4_udpMethods
.getpeername
= Ipv6ToIpv4SocketGetPeerName
;
311 ipv6_to_v4_udpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
312 ipv6_to_v4_udpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
317 # if defined(_PR_INET6_PROBE)
318 PRBool
_pr_ipv6_is_present(void) {
319 if (PR_CallOnce(&_pr_init_ipv6_once
, _pr_init_ipv6
) != PR_SUCCESS
) {
322 return ipv6_is_present
;
326 PR_IMPLEMENT(PRStatus
) _pr_push_ipv6toipv4_layer(PRFileDesc
* fd
) {
327 PRFileDesc
* ipv6_fd
= NULL
;
329 if (PR_CallOnce(&_pr_init_ipv6_once
, _pr_init_ipv6
) != PR_SUCCESS
) {
334 * For platforms with no support for IPv6
335 * create layered socket for IPv4-mapped IPv6 addresses
337 if (fd
->methods
->file_type
== PR_DESC_SOCKET_TCP
)
338 ipv6_fd
= PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id
, &ipv6_to_v4_tcpMethods
);
340 ipv6_fd
= PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id
, &ipv6_to_v4_udpMethods
);
341 if (NULL
== ipv6_fd
) {
344 ipv6_fd
->secret
= NULL
;
346 if (PR_PushIOLayer(fd
, PR_TOP_IO_LAYER
, ipv6_fd
) == PR_FAILURE
) {
354 ipv6_fd
->dtor(ipv6_fd
);
359 #endif /* !defined(_PR_INET6) || defined(_PR_INET6_PROBE) */