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