/src/nspr/pr/src/io/pripv6.c
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | /* |
7 | | ** File: pripv6.c |
8 | | ** Description: Support for various functions unique to IPv6 |
9 | | */ |
10 | | #include "primpl.h" |
11 | | #include <string.h> |
12 | | |
13 | | #if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) |
14 | | |
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; |
21 | | |
22 | | /* |
23 | | * convert an IPv4-mapped IPv6 addr to an IPv4 addr |
24 | | */ |
25 | | static void _PR_ConvertToIpv4NetAddr(const PRNetAddr* src_v6addr, |
26 | 0 | PRNetAddr* dst_v4addr) { |
27 | 0 | const PRUint8* srcp; |
28 | |
|
29 | 0 | PR_ASSERT(PR_AF_INET6 == src_v6addr->ipv6.family); |
30 | |
|
31 | 0 | if (PR_IsNetAddrType(src_v6addr, PR_IpAddrV4Mapped)) { |
32 | 0 | srcp = src_v6addr->ipv6.ip.pr_s6_addr; |
33 | 0 | memcpy((char*)&dst_v4addr->inet.ip, srcp + 12, 4); |
34 | 0 | } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrAny)) { |
35 | 0 | dst_v4addr->inet.ip = htonl(INADDR_ANY); |
36 | 0 | } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrLoopback)) { |
37 | 0 | dst_v4addr->inet.ip = htonl(INADDR_LOOPBACK); |
38 | 0 | } |
39 | 0 | dst_v4addr->inet.family = PR_AF_INET; |
40 | 0 | dst_v4addr->inet.port = src_v6addr->ipv6.port; |
41 | 0 | } |
42 | | |
43 | | /* |
44 | | * convert an IPv4 addr to an IPv4-mapped IPv6 addr |
45 | | */ |
46 | | static void _PR_ConvertToIpv6NetAddr(const PRNetAddr* src_v4addr, |
47 | 0 | PRNetAddr* dst_v6addr) { |
48 | 0 | PRUint8* dstp; |
49 | |
|
50 | 0 | PR_ASSERT(PR_AF_INET == src_v4addr->inet.family); |
51 | 0 | dst_v6addr->ipv6.family = PR_AF_INET6; |
52 | 0 | dst_v6addr->ipv6.port = src_v4addr->inet.port; |
53 | |
|
54 | 0 | if (htonl(INADDR_ANY) == src_v4addr->inet.ip) { |
55 | 0 | dst_v6addr->ipv6.ip = _pr_in6addr_any; |
56 | 0 | } else { |
57 | 0 | dstp = dst_v6addr->ipv6.ip.pr_s6_addr; |
58 | 0 | memset(dstp, 0, 10); |
59 | 0 | memset(dstp + 10, 0xff, 2); |
60 | 0 | memcpy(dstp + 12, (char*)&src_v4addr->inet.ip, 4); |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | static PRStatus PR_CALLBACK Ipv6ToIpv4SocketBind(PRFileDesc* fd, |
65 | 0 | const PRNetAddr* addr) { |
66 | 0 | PRNetAddr tmp_ipv4addr; |
67 | 0 | const PRNetAddr* tmp_addrp; |
68 | 0 | PRFileDesc* lo = fd->lower; |
69 | |
|
70 | 0 | if (PR_AF_INET6 != addr->raw.family) { |
71 | 0 | PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); |
72 | 0 | return PR_FAILURE; |
73 | 0 | } |
74 | 0 | if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || |
75 | 0 | PR_IsNetAddrType(addr, PR_IpAddrAny)) { |
76 | 0 | _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); |
77 | 0 | tmp_addrp = &tmp_ipv4addr; |
78 | 0 | } else { |
79 | 0 | PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); |
80 | 0 | return PR_FAILURE; |
81 | 0 | } |
82 | 0 | return ((lo->methods->bind)(lo, tmp_addrp)); |
83 | 0 | } |
84 | | |
85 | | static PRStatus PR_CALLBACK Ipv6ToIpv4SocketConnect(PRFileDesc* fd, |
86 | | const PRNetAddr* addr, |
87 | 0 | PRIntervalTime timeout) { |
88 | 0 | PRNetAddr tmp_ipv4addr; |
89 | 0 | const PRNetAddr* tmp_addrp; |
90 | |
|
91 | 0 | if (PR_AF_INET6 != addr->raw.family) { |
92 | 0 | PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); |
93 | 0 | return PR_FAILURE; |
94 | 0 | } |
95 | 0 | if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || |
96 | 0 | PR_IsNetAddrType(addr, PR_IpAddrLoopback)) { |
97 | 0 | _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); |
98 | 0 | tmp_addrp = &tmp_ipv4addr; |
99 | 0 | } else { |
100 | 0 | PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); |
101 | 0 | return PR_FAILURE; |
102 | 0 | } |
103 | 0 | return (fd->lower->methods->connect)(fd->lower, tmp_addrp, timeout); |
104 | 0 | } |
105 | | |
106 | | static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketSendTo(PRFileDesc* fd, |
107 | | const void* buf, |
108 | | PRInt32 amount, PRIntn flags, |
109 | | const PRNetAddr* addr, |
110 | 0 | PRIntervalTime timeout) { |
111 | 0 | PRNetAddr tmp_ipv4addr; |
112 | 0 | const PRNetAddr* tmp_addrp; |
113 | |
|
114 | 0 | if (PR_AF_INET6 != addr->raw.family) { |
115 | 0 | PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); |
116 | 0 | return PR_FAILURE; |
117 | 0 | } |
118 | 0 | if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || |
119 | 0 | PR_IsNetAddrType(addr, PR_IpAddrLoopback)) { |
120 | 0 | _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); |
121 | 0 | tmp_addrp = &tmp_ipv4addr; |
122 | 0 | } else { |
123 | 0 | PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); |
124 | 0 | return PR_FAILURE; |
125 | 0 | } |
126 | 0 | return (fd->lower->methods->sendto)(fd->lower, buf, amount, flags, tmp_addrp, |
127 | 0 | timeout); |
128 | 0 | } |
129 | | |
130 | | static PRFileDesc* PR_CALLBACK Ipv6ToIpv4SocketAccept(PRFileDesc* fd, |
131 | | PRNetAddr* addr, |
132 | 0 | PRIntervalTime timeout) { |
133 | 0 | PRStatus rv; |
134 | 0 | PRFileDesc* newfd; |
135 | 0 | PRFileDesc* newstack; |
136 | 0 | PRNetAddr tmp_ipv4addr; |
137 | 0 | PRNetAddr* addrlower = NULL; |
138 | |
|
139 | 0 | PR_ASSERT(fd != NULL); |
140 | 0 | PR_ASSERT(fd->lower != NULL); |
141 | |
|
142 | 0 | newstack = PR_NEW(PRFileDesc); |
143 | 0 | if (NULL == newstack) { |
144 | 0 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
145 | 0 | return NULL; |
146 | 0 | } |
147 | 0 | *newstack = *fd; /* make a copy of the accepting layer */ |
148 | |
|
149 | 0 | if (addr) { |
150 | 0 | addrlower = &tmp_ipv4addr; |
151 | 0 | } |
152 | 0 | newfd = (fd->lower->methods->accept)(fd->lower, addrlower, timeout); |
153 | 0 | if (NULL == newfd) { |
154 | 0 | PR_DELETE(newstack); |
155 | 0 | return NULL; |
156 | 0 | } |
157 | 0 | if (addr) { |
158 | 0 | _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, addr); |
159 | 0 | } |
160 | |
|
161 | 0 | rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); |
162 | 0 | PR_ASSERT(PR_SUCCESS == rv); |
163 | 0 | return newfd; /* that's it */ |
164 | 0 | } |
165 | | |
166 | | static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketAcceptRead(PRFileDesc* sd, |
167 | | PRFileDesc** nd, |
168 | | PRNetAddr** ipv6_raddr, |
169 | | void* buf, PRInt32 amount, |
170 | 0 | PRIntervalTime timeout) { |
171 | 0 | PRInt32 nbytes; |
172 | 0 | PRStatus rv; |
173 | 0 | PRNetAddr tmp_ipv4addr; |
174 | 0 | PRFileDesc* newstack; |
175 | |
|
176 | 0 | PR_ASSERT(sd != NULL); |
177 | 0 | PR_ASSERT(sd->lower != NULL); |
178 | |
|
179 | 0 | newstack = PR_NEW(PRFileDesc); |
180 | 0 | if (NULL == newstack) { |
181 | 0 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
182 | 0 | return -1; |
183 | 0 | } |
184 | 0 | *newstack = *sd; /* make a copy of the accepting layer */ |
185 | |
|
186 | 0 | nbytes = sd->lower->methods->acceptread(sd->lower, nd, ipv6_raddr, buf, |
187 | 0 | amount, timeout); |
188 | 0 | if (-1 == nbytes) { |
189 | 0 | PR_DELETE(newstack); |
190 | 0 | return nbytes; |
191 | 0 | } |
192 | 0 | tmp_ipv4addr = **ipv6_raddr; /* copy */ |
193 | 0 | _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, *ipv6_raddr); |
194 | | |
195 | | /* this PR_PushIOLayer call cannot fail */ |
196 | 0 | rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack); |
197 | 0 | PR_ASSERT(PR_SUCCESS == rv); |
198 | 0 | return nbytes; |
199 | 0 | } |
200 | | |
201 | | static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetName(PRFileDesc* fd, |
202 | 0 | PRNetAddr* ipv6addr) { |
203 | 0 | PRStatus result; |
204 | 0 | PRNetAddr tmp_ipv4addr; |
205 | |
|
206 | 0 | result = (fd->lower->methods->getsockname)(fd->lower, &tmp_ipv4addr); |
207 | 0 | if (PR_SUCCESS == result) { |
208 | 0 | _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); |
209 | 0 | PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); |
210 | 0 | } |
211 | 0 | return result; |
212 | 0 | } |
213 | | |
214 | | static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetPeerName(PRFileDesc* fd, |
215 | 0 | PRNetAddr* ipv6addr) { |
216 | 0 | PRStatus result; |
217 | 0 | PRNetAddr tmp_ipv4addr; |
218 | |
|
219 | 0 | result = (fd->lower->methods->getpeername)(fd->lower, &tmp_ipv4addr); |
220 | 0 | if (PR_SUCCESS == result) { |
221 | 0 | _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); |
222 | 0 | PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); |
223 | 0 | } |
224 | 0 | return result; |
225 | 0 | } |
226 | | |
227 | | static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketRecvFrom(PRFileDesc* fd, void* buf, |
228 | | PRInt32 amount, |
229 | | PRIntn flags, |
230 | | PRNetAddr* ipv6addr, |
231 | 0 | PRIntervalTime timeout) { |
232 | 0 | PRNetAddr tmp_ipv4addr; |
233 | 0 | PRInt32 result; |
234 | |
|
235 | 0 | result = (fd->lower->methods->recvfrom)(fd->lower, buf, amount, flags, |
236 | 0 | &tmp_ipv4addr, timeout); |
237 | 0 | if (-1 != result) { |
238 | 0 | _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); |
239 | 0 | PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); |
240 | 0 | } |
241 | 0 | return result; |
242 | 0 | } |
243 | | |
244 | | # if defined(_PR_INET6_PROBE) |
245 | | static PRBool ipv6_is_present; |
246 | | PR_EXTERN(PRBool) _pr_test_ipv6_socket(void); |
247 | | |
248 | | # if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) |
249 | | extern PRStatus _pr_find_getipnodebyname(void); |
250 | | # endif |
251 | | |
252 | | # if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO) |
253 | | extern PRStatus _pr_find_getaddrinfo(void); |
254 | | # endif |
255 | | |
256 | 0 | static PRBool _pr_probe_ipv6_presence(void) { |
257 | | # if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) |
258 | | if (_pr_find_getipnodebyname() != PR_SUCCESS) { |
259 | | return PR_FALSE; |
260 | | } |
261 | | # endif |
262 | |
|
263 | | # if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO) |
264 | | if (_pr_find_getaddrinfo() != PR_SUCCESS) { |
265 | | return PR_FALSE; |
266 | | } |
267 | | # endif |
268 | |
|
269 | 0 | return _pr_test_ipv6_socket(); |
270 | 0 | } |
271 | | # endif /* _PR_INET6_PROBE */ |
272 | | |
273 | | static PRCallOnceType _pr_init_ipv6_once; |
274 | | |
275 | 0 | static PRStatus PR_CALLBACK _pr_init_ipv6(void) { |
276 | 0 | const PRIOMethods* stubMethods; |
277 | |
|
278 | 0 | # if defined(_PR_INET6_PROBE) |
279 | 0 | ipv6_is_present = _pr_probe_ipv6_presence(); |
280 | 0 | if (ipv6_is_present) { |
281 | 0 | return PR_SUCCESS; |
282 | 0 | } |
283 | 0 | # endif |
284 | | |
285 | 0 | _pr_ipv6_to_ipv4_id = PR_GetUniqueIdentity("Ipv6_to_Ipv4 layer"); |
286 | 0 | PR_ASSERT(PR_INVALID_IO_LAYER != _pr_ipv6_to_ipv4_id); |
287 | |
|
288 | 0 | stubMethods = PR_GetDefaultIOMethods(); |
289 | |
|
290 | 0 | ipv6_to_v4_tcpMethods = *stubMethods; /* first get the entire batch */ |
291 | | /* then override the ones we care about */ |
292 | 0 | ipv6_to_v4_tcpMethods.connect = Ipv6ToIpv4SocketConnect; |
293 | 0 | ipv6_to_v4_tcpMethods.bind = Ipv6ToIpv4SocketBind; |
294 | 0 | ipv6_to_v4_tcpMethods.accept = Ipv6ToIpv4SocketAccept; |
295 | 0 | ipv6_to_v4_tcpMethods.acceptread = Ipv6ToIpv4SocketAcceptRead; |
296 | 0 | ipv6_to_v4_tcpMethods.getsockname = Ipv6ToIpv4SocketGetName; |
297 | 0 | ipv6_to_v4_tcpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName; |
298 | | /* |
299 | | ipv6_to_v4_tcpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption; |
300 | | ipv6_to_v4_tcpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption; |
301 | | */ |
302 | 0 | ipv6_to_v4_udpMethods = *stubMethods; /* first get the entire batch */ |
303 | | /* then override the ones we care about */ |
304 | 0 | ipv6_to_v4_udpMethods.connect = Ipv6ToIpv4SocketConnect; |
305 | 0 | ipv6_to_v4_udpMethods.bind = Ipv6ToIpv4SocketBind; |
306 | 0 | ipv6_to_v4_udpMethods.sendto = Ipv6ToIpv4SocketSendTo; |
307 | 0 | ipv6_to_v4_udpMethods.recvfrom = Ipv6ToIpv4SocketRecvFrom; |
308 | 0 | ipv6_to_v4_udpMethods.getsockname = Ipv6ToIpv4SocketGetName; |
309 | 0 | ipv6_to_v4_udpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName; |
310 | | /* |
311 | | ipv6_to_v4_udpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption; |
312 | | ipv6_to_v4_udpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption; |
313 | | */ |
314 | 0 | return PR_SUCCESS; |
315 | 0 | } |
316 | | |
317 | | # if defined(_PR_INET6_PROBE) |
318 | 0 | PRBool _pr_ipv6_is_present(void) { |
319 | 0 | if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) { |
320 | 0 | return PR_FALSE; |
321 | 0 | } |
322 | 0 | return ipv6_is_present; |
323 | 0 | } |
324 | | # endif |
325 | | |
326 | 0 | PR_IMPLEMENT(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc* fd) { |
327 | 0 | PRFileDesc* ipv6_fd = NULL; |
328 | |
|
329 | 0 | if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) { |
330 | 0 | return PR_FAILURE; |
331 | 0 | } |
332 | | |
333 | | /* |
334 | | * For platforms with no support for IPv6 |
335 | | * create layered socket for IPv4-mapped IPv6 addresses |
336 | | */ |
337 | 0 | if (fd->methods->file_type == PR_DESC_SOCKET_TCP) |
338 | 0 | ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id, &ipv6_to_v4_tcpMethods); |
339 | 0 | else |
340 | 0 | ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id, &ipv6_to_v4_udpMethods); |
341 | 0 | if (NULL == ipv6_fd) { |
342 | 0 | goto errorExit; |
343 | 0 | } |
344 | 0 | ipv6_fd->secret = NULL; |
345 | |
|
346 | 0 | if (PR_PushIOLayer(fd, PR_TOP_IO_LAYER, ipv6_fd) == PR_FAILURE) { |
347 | 0 | goto errorExit; |
348 | 0 | } |
349 | | |
350 | 0 | return PR_SUCCESS; |
351 | 0 | errorExit: |
352 | |
|
353 | 0 | if (ipv6_fd) { |
354 | 0 | ipv6_fd->dtor(ipv6_fd); |
355 | 0 | } |
356 | 0 | return PR_FAILURE; |
357 | 0 | } |
358 | | |
359 | | #endif /* !defined(_PR_INET6) || defined(_PR_INET6_PROBE) */ |