Coverage Report

Created: 2025-07-27 06:09

/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) */