|
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 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 if ((optname < PR_SockOpt_Linger) |
|
444 || (optname >= PR_SockOpt_Last)) |
|
445 { |
|
446 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
|
447 return PR_FAILURE; |
|
448 } |
|
449 |
|
450 if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) |
|
451 { |
|
452 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); |
|
453 return PR_FAILURE; |
|
454 } |
|
455 *name = socketOptions[optname]; |
|
456 *level = socketLevels[optname]; |
|
457 return PR_SUCCESS; |
|
458 } /* _PR_MapOptionName */ |