Coverage Report

Created: 2026-05-19 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nspr/pr/src/io/prmapopt.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
/*
6
 * This file defines _PR_MapOptionName().  The purpose of putting
7
 * _PR_MapOptionName() in a separate file is to work around a Winsock
8
 * header file problem on Windows NT.
9
 *
10
 * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order
11
 * to use Service Pack 3 extensions), windows.h includes winsock2.h
12
 * (instead of winsock.h), which doesn't define many socket options
13
 * defined in winsock.h.
14
 *
15
 * We need the socket options defined in winsock.h.  So this file
16
 * includes winsock.h, with _WIN32_WINNT undefined.
17
 */
18
19
#if defined(WINNT) || defined(__MINGW32__)
20
#  include <winsock.h>
21
#endif
22
23
/* MinGW doesn't define these in its winsock.h. */
24
#ifdef __MINGW32__
25
#  ifndef IP_TTL
26
#    define IP_TTL 7
27
#  endif
28
#  ifndef IP_TOS
29
#    define IP_TOS 8
30
#  endif
31
#endif
32
33
#include "primpl.h"
34
35
#if defined(LINUX) || defined(ANDROID)
36
#  include <netinet/in.h>
37
#endif
38
39
#ifdef DARWIN
40
#  include <netinet/in.h>
41
#  include <netinet/ip.h>
42
#endif
43
44
#ifdef HAVE_NETINET_TCP_H
45
#  include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
46
#endif
47
48
#ifndef _PR_PTHREADS
49
50
PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc* fd,
51
                                               PRSocketOptionData* data) {
52
  PRStatus rv;
53
  PRInt32 length;
54
  PRInt32 level, name;
55
56
  /*
57
   * PR_SockOpt_Nonblocking is a special case that does not
58
   * translate to a getsockopt() call
59
   */
60
  if (PR_SockOpt_Nonblocking == data->option) {
61
    data->value.non_blocking = fd->secret->nonblocking;
62
    return PR_SUCCESS;
63
  }
64
65
  rv = _PR_MapOptionName(data->option, &level, &name);
66
  if (PR_SUCCESS == rv) {
67
    switch (data->option) {
68
      case PR_SockOpt_Linger: {
69
        struct linger linger;
70
        length = sizeof(linger);
71
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&linger, &length);
72
        if (PR_SUCCESS == rv) {
73
          PR_ASSERT(sizeof(linger) == length);
74
          data->value.linger.polarity = (linger.l_onoff) ? PR_TRUE : PR_FALSE;
75
          data->value.linger.linger = PR_SecondsToInterval(linger.l_linger);
76
        }
77
        break;
78
      }
79
      case PR_SockOpt_Reuseaddr:
80
      case PR_SockOpt_Keepalive:
81
      case PR_SockOpt_NoDelay:
82
      case PR_SockOpt_Broadcast:
83
      case PR_SockOpt_Reuseport: {
84
#  ifdef WIN32 /* Winsock */
85
        BOOL value;
86
#  else
87
        PRIntn value;
88
#  endif
89
        length = sizeof(value);
90
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
91
        if (PR_SUCCESS == rv) {
92
          data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
93
        }
94
        break;
95
      }
96
      case PR_SockOpt_McastLoopback: {
97
#  ifdef WIN32 /* Winsock */
98
        BOOL bool;
99
#  else
100
        PRUint8 bool;
101
#  endif
102
        length = sizeof(bool);
103
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&bool, &length);
104
        if (PR_SUCCESS == rv) {
105
          data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE;
106
        }
107
        break;
108
      }
109
      case PR_SockOpt_RecvBufferSize:
110
      case PR_SockOpt_SendBufferSize:
111
      case PR_SockOpt_MaxSegment: {
112
        PRIntn value;
113
        length = sizeof(value);
114
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
115
        if (PR_SUCCESS == rv) {
116
          data->value.recv_buffer_size = value;
117
        }
118
        break;
119
      }
120
      case PR_SockOpt_IpTimeToLive:
121
      case PR_SockOpt_IpTypeOfService: {
122
        /* These options should really be an int (or PRIntn). */
123
        length = sizeof(PRUintn);
124
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&data->value.ip_ttl,
125
                               &length);
126
        break;
127
      }
128
      case PR_SockOpt_McastTimeToLive: {
129
#  ifdef WIN32 /* Winsock */
130
        int ttl;
131
#  else
132
        PRUint8 ttl;
133
#  endif
134
        length = sizeof(ttl);
135
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&ttl, &length);
136
        if (PR_SUCCESS == rv) {
137
          data->value.mcast_ttl = ttl;
138
        }
139
        break;
140
      }
141
#  ifdef IP_ADD_MEMBERSHIP
142
      case PR_SockOpt_AddMember:
143
      case PR_SockOpt_DropMember: {
144
        struct ip_mreq mreq;
145
        length = sizeof(mreq);
146
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&mreq, &length);
147
        if (PR_SUCCESS == rv) {
148
          data->value.add_member.mcaddr.inet.ip = mreq.imr_multiaddr.s_addr;
149
          data->value.add_member.ifaddr.inet.ip = mreq.imr_interface.s_addr;
150
        }
151
        break;
152
      }
153
#  endif /* IP_ADD_MEMBERSHIP */
154
      case PR_SockOpt_McastInterface: {
155
        /* This option is a struct in_addr. */
156
        length = sizeof(data->value.mcast_if.inet.ip);
157
        rv = _PR_MD_GETSOCKOPT(fd, level, name,
158
                               (char*)&data->value.mcast_if.inet.ip, &length);
159
        break;
160
      }
161
      case PR_SockOpt_DontFrag: {
162
#  if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
163
      !defined(ANDROID)
164
        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
165
        rv = PR_FAILURE;
166
#  else
167
#    ifdef WIN32 /* Winsock */
168
        DWORD value;
169
#    else
170
        PRIntn value;
171
#    endif
172
        length = sizeof(value);
173
        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
174
#    if defined(WIN32) || defined(DARWIN)
175
        data->value.dont_fragment = value;
176
#    else
177
        data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0;
178
#    endif
179
#  endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
180
            !defined(ANDROID)) */
181
        break;
182
      }
183
      default:
184
        PR_NOT_REACHED("Unknown socket option");
185
        break;
186
    }
187
  }
188
  return rv;
189
} /* _PR_SocketGetSocketOption */
190
191
PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc* fd,
192
                                               const PRSocketOptionData* data) {
193
  PRStatus rv;
194
  PRInt32 level, name;
195
196
  /*
197
   * PR_SockOpt_Nonblocking is a special case that does not
198
   * translate to a setsockopt call.
199
   */
200
  if (PR_SockOpt_Nonblocking == data->option) {
201
#  ifdef WINNT
202
    PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) ||
203
              (fd->secret->nonblocking == data->value.non_blocking));
204
    if (fd->secret->md.io_model_committed &&
205
        (fd->secret->nonblocking != data->value.non_blocking)) {
206
      /*
207
       * On NT, once we have associated a socket with the io
208
       * completion port, we can't disassociate it.  So we
209
       * can't change the nonblocking option of the socket
210
       * afterwards.
211
       */
212
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
213
      return PR_FAILURE;
214
    }
215
#  endif
216
    fd->secret->nonblocking = data->value.non_blocking;
217
    return PR_SUCCESS;
218
  }
219
220
  rv = _PR_MapOptionName(data->option, &level, &name);
221
  if (PR_SUCCESS == rv) {
222
    switch (data->option) {
223
      case PR_SockOpt_Linger: {
224
        struct linger linger;
225
        linger.l_onoff = data->value.linger.polarity;
226
        linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
227
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&linger, sizeof(linger));
228
        break;
229
      }
230
      case PR_SockOpt_Reuseaddr:
231
      case PR_SockOpt_Keepalive:
232
      case PR_SockOpt_NoDelay:
233
      case PR_SockOpt_Broadcast:
234
      case PR_SockOpt_Reuseport: {
235
#  ifdef WIN32 /* Winsock */
236
        BOOL value;
237
#  else
238
        PRIntn value;
239
#  endif
240
        value = (data->value.reuse_addr) ? 1 : 0;
241
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
242
        break;
243
      }
244
      case PR_SockOpt_McastLoopback: {
245
#  ifdef WIN32 /* Winsock */
246
        BOOL bool;
247
#  else
248
        PRUint8 bool;
249
#  endif
250
        bool = data->value.mcast_loopback ? 1 : 0;
251
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&bool, sizeof(bool));
252
        break;
253
      }
254
      case PR_SockOpt_RecvBufferSize:
255
      case PR_SockOpt_SendBufferSize:
256
      case PR_SockOpt_MaxSegment: {
257
        PRIntn value = data->value.recv_buffer_size;
258
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
259
        break;
260
      }
261
      case PR_SockOpt_IpTimeToLive:
262
      case PR_SockOpt_IpTypeOfService: {
263
        /* These options should really be an int (or PRIntn). */
264
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&data->value.ip_ttl,
265
                               sizeof(PRUintn));
266
        break;
267
      }
268
      case PR_SockOpt_McastTimeToLive: {
269
#  ifdef WIN32 /* Winsock */
270
        int ttl;
271
#  else
272
        PRUint8 ttl;
273
#  endif
274
        ttl = data->value.mcast_ttl;
275
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&ttl, sizeof(ttl));
276
        break;
277
      }
278
#  ifdef IP_ADD_MEMBERSHIP
279
      case PR_SockOpt_AddMember:
280
      case PR_SockOpt_DropMember: {
281
        struct ip_mreq mreq;
282
        mreq.imr_multiaddr.s_addr = data->value.add_member.mcaddr.inet.ip;
283
        mreq.imr_interface.s_addr = data->value.add_member.ifaddr.inet.ip;
284
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&mreq, sizeof(mreq));
285
        break;
286
      }
287
#  endif /* IP_ADD_MEMBERSHIP */
288
      case PR_SockOpt_McastInterface: {
289
        /* This option is a struct in_addr. */
290
        rv = _PR_MD_SETSOCKOPT(fd, level, name,
291
                               (char*)&data->value.mcast_if.inet.ip,
292
                               sizeof(data->value.mcast_if.inet.ip));
293
        break;
294
      }
295
      case PR_SockOpt_DontFrag: {
296
#  if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
297
      !defined(ANDROID)
298
        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
299
        rv = PR_FAILURE;
300
#  else
301
#    if defined(WIN32) /* Winsock */
302
        DWORD value;
303
        value = (data->value.dont_fragment) ? 1 : 0;
304
#    elif defined(LINUX) || defined(ANDROID)
305
        PRIntn value;
306
        value = (data->value.dont_fragment) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
307
#    elif defined(DARWIN)
308
        PRIntn value;
309
        value = data->value.dont_fragment;
310
#    endif
311
        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
312
#  endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
313
            !defined(ANDROID)) */
314
        break;
315
      }
316
      default:
317
        PR_NOT_REACHED("Unknown socket option");
318
        break;
319
    }
320
  }
321
  return rv;
322
} /* _PR_SocketSetSocketOption */
323
324
#endif /* ! _PR_PTHREADS */
325
326
/*
327
 *********************************************************************
328
 *********************************************************************
329
 **
330
 ** Make sure that the following is at the end of this file,
331
 ** because we will be playing with macro redefines.
332
 **
333
 *********************************************************************
334
 *********************************************************************
335
 */
336
337
/*
338
 * Not every platform has all the socket options we want to
339
 * support.  Some older operating systems such as SunOS 4.1.3
340
 * don't have the IP multicast socket options.  Win32 doesn't
341
 * have TCP_MAXSEG.
342
 *
343
 * To deal with this problem, we define the missing socket
344
 * options as _PR_NO_SUCH_SOCKOPT.  _PR_MapOptionName() fails with
345
 * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not
346
 * available on the platform is requested.
347
 */
348
349
/*
350
 * Sanity check.  SO_LINGER and TCP_NODELAY should be available
351
 * on all platforms.  Just to make sure we have included the
352
 * appropriate header files.  Then any undefined socket options
353
 * are really missing.
354
 */
355
356
#if !defined(SO_LINGER)
357
#  error "SO_LINGER is not defined"
358
#endif
359
360
#if !defined(TCP_NODELAY)
361
#  error "TCP_NODELAY is not defined"
362
#endif
363
364
/*
365
 * Make sure the value of _PR_NO_SUCH_SOCKOPT is not
366
 * a valid socket option.
367
 */
368
0
#define _PR_NO_SUCH_SOCKOPT -1
369
370
#ifndef SO_KEEPALIVE
371
#  define SO_KEEPALIVE _PR_NO_SUCH_SOCKOPT
372
#endif
373
374
#ifndef SO_SNDBUF
375
#  define SO_SNDBUF _PR_NO_SUCH_SOCKOPT
376
#endif
377
378
#ifndef SO_RCVBUF
379
#  define SO_RCVBUF _PR_NO_SUCH_SOCKOPT
380
#endif
381
382
#ifndef IP_MULTICAST_IF /* set/get IP multicast interface   */
383
#  define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT
384
#endif
385
386
#ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive  */
387
#  define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT
388
#endif
389
390
#ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback    */
391
#  define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT
392
#endif
393
394
#ifndef IP_ADD_MEMBERSHIP /* add  an IP group membership      */
395
#  define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
396
#endif
397
398
#ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership      */
399
#  define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
400
#endif
401
402
#ifndef IP_TTL /* set/get IP Time To Live          */
403
#  define IP_TTL _PR_NO_SUCH_SOCKOPT
404
#endif
405
406
#ifndef IP_TOS /* set/get IP Type Of Service       */
407
#  define IP_TOS _PR_NO_SUCH_SOCKOPT
408
#endif
409
410
/* set/get IP do not fragment */
411
#if defined(WIN32)
412
#  ifndef IP_DONTFRAGMENT
413
#    define IP_DONTFRAGMENT _PR_NO_SUCH_SOCKOPT
414
#  endif
415
416
#elif defined(LINUX) || defined(ANDROID)
417
#  ifndef IP_MTU_DISCOVER
418
#    define IP_MTU_DISCOVER _PR_NO_SUCH_SOCKOPT
419
#  endif
420
421
#elif defined(DARWIN)
422
#  ifndef IP_DONTFRAG
423
#    define IP_DONTFRAG _PR_NO_SUCH_SOCKOPT
424
#  endif
425
#endif
426
427
#ifndef TCP_NODELAY /* don't delay to coalesce data     */
428
#  define TCP_NODELAY _PR_NO_SUCH_SOCKOPT
429
#endif
430
431
#ifndef TCP_MAXSEG /* maxumum segment size for tcp     */
432
#  define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT
433
#endif
434
435
#ifndef SO_BROADCAST /* enable broadcast on UDP sockets  */
436
#  define SO_BROADCAST _PR_NO_SUCH_SOCKOPT
437
#endif
438
439
#ifndef SO_REUSEPORT /* allow local address & port reuse */
440
#  define SO_REUSEPORT _PR_NO_SUCH_SOCKOPT
441
#endif
442
443
PRStatus _PR_MapOptionName(PRSockOption optname, PRInt32* level,
444
0
                           PRInt32* name) {
445
0
  static PRInt32 socketOptions[PR_SockOpt_Last] = {
446
0
      0,
447
0
      SO_LINGER,
448
0
      SO_REUSEADDR,
449
0
      SO_KEEPALIVE,
450
0
      SO_RCVBUF,
451
0
      SO_SNDBUF,
452
0
      IP_TTL,
453
0
      IP_TOS,
454
0
      IP_ADD_MEMBERSHIP,
455
0
      IP_DROP_MEMBERSHIP,
456
0
      IP_MULTICAST_IF,
457
0
      IP_MULTICAST_TTL,
458
0
      IP_MULTICAST_LOOP,
459
0
      TCP_NODELAY,
460
0
      TCP_MAXSEG,
461
0
      SO_BROADCAST,
462
0
      SO_REUSEPORT,
463
#if defined(WIN32)
464
      IP_DONTFRAGMENT,
465
#elif defined(LINUX) || defined(ANDROID)
466
0
      IP_MTU_DISCOVER,
467
#elif defined(DARWIN)
468
      IP_DONTFRAG,
469
#else
470
      _PR_NO_SUCH_SOCKOPT,
471
#endif
472
0
  };
473
0
  static PRInt32 socketLevels[PR_SockOpt_Last] = {
474
0
      0,          SOL_SOCKET,  SOL_SOCKET,  SOL_SOCKET, SOL_SOCKET, SOL_SOCKET,
475
0
      IPPROTO_IP, IPPROTO_IP,  IPPROTO_IP,  IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
476
0
      IPPROTO_IP, IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP};
477
478
0
  if ((optname < PR_SockOpt_Linger) || (optname >= PR_SockOpt_Last)) {
479
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
480
0
    return PR_FAILURE;
481
0
  }
482
483
0
  if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) {
484
0
    PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
485
0
    return PR_FAILURE;
486
0
  }
487
0
  *name = socketOptions[optname];
488
0
  *level = socketLevels[optname];
489
0
  return PR_SUCCESS;
490
0
} /* _PR_MapOptionName */