1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : ** File: pripv6.c
40 : ** Description: Support for various functions unique to IPv6
41 : */
42 : #include "primpl.h"
43 : #include <string.h>
44 :
45 : #if !defined(_PR_INET6) || defined(_PR_INET6_PROBE)
46 :
47 : static PRIOMethods ipv6_to_v4_tcpMethods;
48 : static PRIOMethods ipv6_to_v4_udpMethods;
49 : static PRDescIdentity _pr_ipv6_to_ipv4_id;
50 : extern PRBool IsValidNetAddr(const PRNetAddr *addr);
51 : extern PRIPv6Addr _pr_in6addr_any;
52 : extern PRIPv6Addr _pr_in6addr_loopback;
53 :
54 : /*
55 : * convert an IPv4-mapped IPv6 addr to an IPv4 addr
56 : */
57 0 : static void _PR_ConvertToIpv4NetAddr(const PRNetAddr *src_v6addr,
58 : PRNetAddr *dst_v4addr)
59 : {
60 : const PRUint8 *srcp;
61 :
62 0 : PR_ASSERT(PR_AF_INET6 == src_v6addr->ipv6.family);
63 :
64 0 : if (PR_IsNetAddrType(src_v6addr, PR_IpAddrV4Mapped)) {
65 0 : srcp = src_v6addr->ipv6.ip.pr_s6_addr;
66 0 : memcpy((char *) &dst_v4addr->inet.ip, srcp + 12, 4);
67 0 : } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrAny)) {
68 0 : dst_v4addr->inet.ip = htonl(INADDR_ANY);
69 0 : } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrLoopback)) {
70 0 : dst_v4addr->inet.ip = htonl(INADDR_LOOPBACK);
71 : }
72 0 : dst_v4addr->inet.family = PR_AF_INET;
73 0 : dst_v4addr->inet.port = src_v6addr->ipv6.port;
74 0 : }
75 :
76 : /*
77 : * convert an IPv4 addr to an IPv4-mapped IPv6 addr
78 : */
79 0 : static void _PR_ConvertToIpv6NetAddr(const PRNetAddr *src_v4addr,
80 : PRNetAddr *dst_v6addr)
81 : {
82 : PRUint8 *dstp;
83 :
84 0 : PR_ASSERT(PR_AF_INET == src_v4addr->inet.family);
85 0 : dst_v6addr->ipv6.family = PR_AF_INET6;
86 0 : dst_v6addr->ipv6.port = src_v4addr->inet.port;
87 :
88 0 : if (htonl(INADDR_ANY) == src_v4addr->inet.ip) {
89 0 : dst_v6addr->ipv6.ip = _pr_in6addr_any;
90 : } else {
91 0 : dstp = dst_v6addr->ipv6.ip.pr_s6_addr;
92 0 : memset(dstp, 0, 10);
93 0 : memset(dstp + 10, 0xff, 2);
94 0 : memcpy(dstp + 12,(char *) &src_v4addr->inet.ip, 4);
95 : }
96 0 : }
97 :
98 0 : static PRStatus PR_CALLBACK Ipv6ToIpv4SocketBind(PRFileDesc *fd,
99 : const PRNetAddr *addr)
100 : {
101 : PRNetAddr tmp_ipv4addr;
102 : const PRNetAddr *tmp_addrp;
103 0 : PRFileDesc *lo = fd->lower;
104 :
105 0 : if (PR_AF_INET6 != addr->raw.family) {
106 0 : PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
107 0 : return PR_FAILURE;
108 : }
109 0 : if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
110 0 : PR_IsNetAddrType(addr, PR_IpAddrAny)) {
111 0 : _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
112 0 : tmp_addrp = &tmp_ipv4addr;
113 : } else {
114 0 : PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
115 0 : return PR_FAILURE;
116 : }
117 0 : return((lo->methods->bind)(lo,tmp_addrp));
118 : }
119 :
120 0 : static PRStatus PR_CALLBACK Ipv6ToIpv4SocketConnect(
121 : PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
122 : {
123 : PRNetAddr tmp_ipv4addr;
124 : const PRNetAddr *tmp_addrp;
125 :
126 0 : if (PR_AF_INET6 != addr->raw.family) {
127 0 : PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
128 0 : return PR_FAILURE;
129 : }
130 0 : if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
131 0 : PR_IsNetAddrType(addr, PR_IpAddrLoopback)) {
132 0 : _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
133 0 : tmp_addrp = &tmp_ipv4addr;
134 : } else {
135 0 : PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
136 0 : return PR_FAILURE;
137 : }
138 0 : return (fd->lower->methods->connect)(fd->lower, tmp_addrp, timeout);
139 : }
140 :
141 0 : static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketSendTo(
142 : PRFileDesc *fd, const void *buf, PRInt32 amount,
143 : PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
144 : {
145 : PRNetAddr tmp_ipv4addr;
146 : const PRNetAddr *tmp_addrp;
147 :
148 0 : if (PR_AF_INET6 != addr->raw.family) {
149 0 : PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
150 0 : return PR_FAILURE;
151 : }
152 0 : if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
153 0 : PR_IsNetAddrType(addr, PR_IpAddrLoopback)) {
154 0 : _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
155 0 : tmp_addrp = &tmp_ipv4addr;
156 : } else {
157 0 : PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
158 0 : return PR_FAILURE;
159 : }
160 0 : return (fd->lower->methods->sendto)(
161 : fd->lower, buf, amount, flags, tmp_addrp, timeout);
162 : }
163 :
164 0 : static PRFileDesc* PR_CALLBACK Ipv6ToIpv4SocketAccept (
165 : PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
166 : {
167 : PRStatus rv;
168 : PRFileDesc *newfd;
169 : PRFileDesc *newstack;
170 : PRNetAddr tmp_ipv4addr;
171 0 : PRNetAddr *addrlower = NULL;
172 :
173 0 : PR_ASSERT(fd != NULL);
174 0 : PR_ASSERT(fd->lower != NULL);
175 :
176 0 : newstack = PR_NEW(PRFileDesc);
177 0 : if (NULL == newstack)
178 : {
179 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
180 0 : return NULL;
181 : }
182 0 : *newstack = *fd; /* make a copy of the accepting layer */
183 :
184 0 : if (addr)
185 0 : addrlower = &tmp_ipv4addr;
186 0 : newfd = (fd->lower->methods->accept)(fd->lower, addrlower, timeout);
187 0 : if (NULL == newfd)
188 : {
189 0 : PR_DELETE(newstack);
190 0 : return NULL;
191 : }
192 0 : if (addr)
193 0 : _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, addr);
194 :
195 0 : rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
196 0 : PR_ASSERT(PR_SUCCESS == rv);
197 0 : return newfd; /* that's it */
198 : }
199 :
200 0 : static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketAcceptRead(PRFileDesc *sd,
201 : PRFileDesc **nd, PRNetAddr **ipv6_raddr, void *buf, PRInt32 amount,
202 : PRIntervalTime timeout)
203 : {
204 : PRInt32 nbytes;
205 : PRStatus rv;
206 : PRNetAddr tmp_ipv4addr;
207 : PRFileDesc *newstack;
208 :
209 0 : PR_ASSERT(sd != NULL);
210 0 : PR_ASSERT(sd->lower != NULL);
211 :
212 0 : newstack = PR_NEW(PRFileDesc);
213 0 : if (NULL == newstack)
214 : {
215 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
216 0 : return -1;
217 : }
218 0 : *newstack = *sd; /* make a copy of the accepting layer */
219 :
220 0 : nbytes = sd->lower->methods->acceptread(
221 : sd->lower, nd, ipv6_raddr, buf, amount, timeout);
222 0 : if (-1 == nbytes)
223 : {
224 0 : PR_DELETE(newstack);
225 0 : return nbytes;
226 : }
227 0 : tmp_ipv4addr = **ipv6_raddr; /* copy */
228 0 : _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, *ipv6_raddr);
229 :
230 : /* this PR_PushIOLayer call cannot fail */
231 0 : rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
232 0 : PR_ASSERT(PR_SUCCESS == rv);
233 0 : return nbytes;
234 : }
235 :
236 0 : static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetName(PRFileDesc *fd,
237 : PRNetAddr *ipv6addr)
238 : {
239 : PRStatus result;
240 : PRNetAddr tmp_ipv4addr;
241 :
242 0 : result = (fd->lower->methods->getsockname)(fd->lower, &tmp_ipv4addr);
243 0 : if (PR_SUCCESS == result) {
244 0 : _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
245 0 : PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
246 : }
247 0 : return result;
248 : }
249 :
250 0 : static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetPeerName(PRFileDesc *fd,
251 : PRNetAddr *ipv6addr)
252 : {
253 : PRStatus result;
254 : PRNetAddr tmp_ipv4addr;
255 :
256 0 : result = (fd->lower->methods->getpeername)(fd->lower, &tmp_ipv4addr);
257 0 : if (PR_SUCCESS == result) {
258 0 : _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
259 0 : PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
260 : }
261 0 : return result;
262 : }
263 :
264 0 : static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketRecvFrom(PRFileDesc *fd, void *buf,
265 : PRInt32 amount, PRIntn flags, PRNetAddr *ipv6addr,
266 : PRIntervalTime timeout)
267 : {
268 : PRNetAddr tmp_ipv4addr;
269 : PRInt32 result;
270 :
271 0 : result = (fd->lower->methods->recvfrom)(
272 : fd->lower, buf, amount, flags, &tmp_ipv4addr, timeout);
273 0 : if (-1 != result) {
274 0 : _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
275 0 : PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
276 : }
277 0 : return result;
278 : }
279 :
280 : #if defined(_PR_INET6_PROBE)
281 : static PRBool ipv6_is_present;
282 : extern PRBool _pr_test_ipv6_socket(void);
283 :
284 : #if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
285 : extern PRStatus _pr_find_getipnodebyname(void);
286 : #endif
287 :
288 : #if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
289 : extern PRStatus _pr_find_getaddrinfo(void);
290 : #endif
291 :
292 : static PRBool
293 316 : _pr_probe_ipv6_presence(void)
294 : {
295 : #if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
296 : if (_pr_find_getipnodebyname() != PR_SUCCESS)
297 : return PR_FALSE;
298 : #endif
299 :
300 : #if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
301 : if (_pr_find_getaddrinfo() != PR_SUCCESS)
302 : return PR_FALSE;
303 : #endif
304 :
305 316 : return _pr_test_ipv6_socket();
306 : }
307 : #endif /* _PR_INET6_PROBE */
308 :
309 : static PRCallOnceType _pr_init_ipv6_once;
310 :
311 316 : static PRStatus PR_CALLBACK _pr_init_ipv6(void)
312 : {
313 : const PRIOMethods *stubMethods;
314 :
315 : #if defined(_PR_INET6_PROBE)
316 316 : ipv6_is_present = _pr_probe_ipv6_presence();
317 316 : if (ipv6_is_present)
318 316 : return PR_SUCCESS;
319 : #endif
320 :
321 0 : _pr_ipv6_to_ipv4_id = PR_GetUniqueIdentity("Ipv6_to_Ipv4 layer");
322 0 : PR_ASSERT(PR_INVALID_IO_LAYER != _pr_ipv6_to_ipv4_id);
323 :
324 0 : stubMethods = PR_GetDefaultIOMethods();
325 :
326 0 : ipv6_to_v4_tcpMethods = *stubMethods; /* first get the entire batch */
327 : /* then override the ones we care about */
328 0 : ipv6_to_v4_tcpMethods.connect = Ipv6ToIpv4SocketConnect;
329 0 : ipv6_to_v4_tcpMethods.bind = Ipv6ToIpv4SocketBind;
330 0 : ipv6_to_v4_tcpMethods.accept = Ipv6ToIpv4SocketAccept;
331 0 : ipv6_to_v4_tcpMethods.acceptread = Ipv6ToIpv4SocketAcceptRead;
332 0 : ipv6_to_v4_tcpMethods.getsockname = Ipv6ToIpv4SocketGetName;
333 0 : ipv6_to_v4_tcpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName;
334 : /*
335 : ipv6_to_v4_tcpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
336 : ipv6_to_v4_tcpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
337 : */
338 0 : ipv6_to_v4_udpMethods = *stubMethods; /* first get the entire batch */
339 : /* then override the ones we care about */
340 0 : ipv6_to_v4_udpMethods.connect = Ipv6ToIpv4SocketConnect;
341 0 : ipv6_to_v4_udpMethods.bind = Ipv6ToIpv4SocketBind;
342 0 : ipv6_to_v4_udpMethods.sendto = Ipv6ToIpv4SocketSendTo;
343 0 : ipv6_to_v4_udpMethods.recvfrom = Ipv6ToIpv4SocketRecvFrom;
344 0 : ipv6_to_v4_udpMethods.getsockname = Ipv6ToIpv4SocketGetName;
345 0 : ipv6_to_v4_udpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName;
346 : /*
347 : ipv6_to_v4_udpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
348 : ipv6_to_v4_udpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
349 : */
350 0 : return PR_SUCCESS;
351 : }
352 :
353 : #if defined(_PR_INET6_PROBE)
354 44574 : PRBool _pr_ipv6_is_present(void)
355 : {
356 44574 : if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS)
357 0 : return PR_FALSE;
358 44573 : return ipv6_is_present;
359 : }
360 : #endif
361 :
362 0 : PR_IMPLEMENT(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd)
363 : {
364 0 : PRFileDesc *ipv6_fd = NULL;
365 :
366 0 : if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS)
367 0 : return PR_FAILURE;
368 :
369 : /*
370 : * For platforms with no support for IPv6
371 : * create layered socket for IPv4-mapped IPv6 addresses
372 : */
373 0 : if (fd->methods->file_type == PR_DESC_SOCKET_TCP)
374 0 : ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id,
375 : &ipv6_to_v4_tcpMethods);
376 : else
377 0 : ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id,
378 : &ipv6_to_v4_udpMethods);
379 0 : if (NULL == ipv6_fd) {
380 0 : goto errorExit;
381 : }
382 0 : ipv6_fd->secret = NULL;
383 :
384 0 : if (PR_PushIOLayer(fd, PR_TOP_IO_LAYER, ipv6_fd) == PR_FAILURE) {
385 0 : goto errorExit;
386 : }
387 :
388 0 : return PR_SUCCESS;
389 : errorExit:
390 :
391 0 : if (ipv6_fd)
392 0 : ipv6_fd->dtor(ipv6_fd);
393 0 : return PR_FAILURE;
394 : }
395 :
396 : #endif /* !defined(_PR_INET6) || defined(_PR_INET6_PROBE) */
|