prmapopt.c (14883B)
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 #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 PRInt32* name) { 446 static PRInt32 socketOptions[PR_SockOpt_Last] = { 447 0, 448 SO_LINGER, 449 SO_REUSEADDR, 450 SO_KEEPALIVE, 451 SO_RCVBUF, 452 SO_SNDBUF, 453 IP_TTL, 454 IP_TOS, 455 IP_ADD_MEMBERSHIP, 456 IP_DROP_MEMBERSHIP, 457 IP_MULTICAST_IF, 458 IP_MULTICAST_TTL, 459 IP_MULTICAST_LOOP, 460 TCP_NODELAY, 461 TCP_MAXSEG, 462 SO_BROADCAST, 463 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 }; 474 static PRInt32 socketLevels[PR_SockOpt_Last] = { 475 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, 476 IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, 477 IPPROTO_IP, IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP}; 478 479 if ((optname < PR_SockOpt_Linger) || (optname >= PR_SockOpt_Last)) { 480 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 481 return PR_FAILURE; 482 } 483 484 if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) { 485 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); 486 return PR_FAILURE; 487 } 488 *name = socketOptions[optname]; 489 *level = socketLevels[optname]; 490 return PR_SUCCESS; 491 } /* _PR_MapOptionName */