Coverage Report

Created: 2025-07-01 06:25

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