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