nsprpub/pr/src/io/pripv6.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial