File: | d/binder.c |
Warning: | line 125, column 34 Access out-of-bound array element (buffer overflow) |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | ||||
2 | * The contents of this file are subject to the Mozilla Public License | ||||
3 | * Version 1.1 (the "License"); you may not use this file except in | ||||
4 | * compliance with the License. You may obtain a copy of the License at | ||||
5 | * http://mozilla.org/. | ||||
6 | * | ||||
7 | * Software distributed under the License is distributed on an "AS IS" | ||||
8 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | ||||
9 | * the License for the specific language governing rights and limitations | ||||
10 | * under the License. | ||||
11 | * | ||||
12 | * The Original Code is AOLserver Code and related documentation | ||||
13 | * distributed by AOL. | ||||
14 | * | ||||
15 | * The Initial Developer of the Original Code is America Online, | ||||
16 | * Inc. Portions created by AOL are Copyright (C) 1999 America Online, | ||||
17 | * Inc. All Rights Reserved. | ||||
18 | * | ||||
19 | * Alternatively, the contents of this file may be used under the terms | ||||
20 | * of the GNU General Public License (the "GPL"), in which case the | ||||
21 | * provisions of GPL are applicable instead of those above. If you wish | ||||
22 | * to allow use of your version of this file only under the terms of the | ||||
23 | * GPL and not to allow others to use your version of this file under the | ||||
24 | * License, indicate your decision by deleting the provisions above and | ||||
25 | * replace them with the notice and other provisions required by the GPL. | ||||
26 | * If you do not delete the provisions above, a recipient may use your | ||||
27 | * version of this file under either the License or the GPL. | ||||
28 | */ | ||||
29 | |||||
30 | |||||
31 | /* | ||||
32 | * binder.c -- | ||||
33 | * | ||||
34 | * Support for pre-bound privileged ports for Unix | ||||
35 | */ | ||||
36 | |||||
37 | #include "nsd.h" | ||||
38 | |||||
39 | #ifndef _WIN32 | ||||
40 | # include <sys/un.h> | ||||
41 | # include <sys/uio.h> | ||||
42 | |||||
43 | # define REQUEST_SIZE(sizeof(int) + sizeof(int) + sizeof(int) + 46) (sizeof(int) + sizeof(int) + sizeof(int) + NS_IPADDR_SIZE46) | ||||
44 | # define RESPONSE_SIZE(sizeof(int)) (sizeof(int)) | ||||
45 | |||||
46 | typedef struct Prebind { | ||||
47 | size_t count; | ||||
48 | NS_SOCKETint sockets[1]; | ||||
49 | } Prebind; | ||||
50 | |||||
51 | #endif | ||||
52 | |||||
53 | /* | ||||
54 | * Local variables defined in this file | ||||
55 | */ | ||||
56 | |||||
57 | static Ns_Mutex lock = NULL((void*)0); | ||||
58 | static Tcl_HashTable preboundTcp; | ||||
59 | static Tcl_HashTable preboundUdp; | ||||
60 | static Tcl_HashTable preboundRaw; | ||||
61 | static Tcl_HashTable preboundUnix; | ||||
62 | |||||
63 | static bool_Bool binderRunning = NS_FALSE0; | ||||
64 | static NS_SOCKETint binderRequest[2] = { NS_INVALID_SOCKET(-1), NS_INVALID_SOCKET(-1) }; | ||||
65 | static NS_SOCKETint binderResponse[2] = { NS_INVALID_SOCKET(-1), NS_INVALID_SOCKET(-1) }; | ||||
66 | |||||
67 | /* | ||||
68 | * Local functions defined in this file | ||||
69 | */ | ||||
70 | #ifndef _WIN32 | ||||
71 | static Ns_ReturnCode PrebindSockets(const char *spec) | ||||
72 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | ||||
73 | |||||
74 | static void Binder(void); | ||||
75 | |||||
76 | static struct Prebind* PrebindAlloc(const char *proto, size_t reuses, struct sockaddr *saPtr) | ||||
77 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))); | ||||
78 | |||||
79 | static bool_Bool PrebindGet(const char *proto, struct sockaddr *saPtr, NS_SOCKETint *sockPtr) | ||||
80 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))); | ||||
81 | |||||
82 | static void PrebindCloseSockets(const char *proto, struct sockaddr *saPtr, struct Prebind *pPtr) | ||||
83 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))); | ||||
84 | #endif | ||||
85 | |||||
86 | |||||
87 | #ifndef _WIN32 | ||||
88 | |||||
89 | /* | ||||
90 | *---------------------------------------------------------------------- | ||||
91 | * | ||||
92 | * PrebindAlloc -- | ||||
93 | * | ||||
94 | * Create a Prebind structure with potentially multiple sockets | ||||
95 | * binding to the identical port. This is needed for e.g. multiple | ||||
96 | * listeners with SO_REUSEPORT. | ||||
97 | * | ||||
98 | * Results: | ||||
99 | * Either a prebind structure or NULL inc ase of failure. | ||||
100 | * | ||||
101 | * Side effects: | ||||
102 | * Allocating memory, binding of TCP or UDP sockets. | ||||
103 | * | ||||
104 | *---------------------------------------------------------------------- | ||||
105 | */ | ||||
106 | static struct Prebind* | ||||
107 | PrebindAlloc(const char *proto, size_t reuses, struct sockaddr *saPtr) | ||||
108 | { | ||||
109 | struct Prebind *pPtr; | ||||
110 | |||||
111 | NS_NONNULL_ASSERT(proto != NULL)((void) (0)); | ||||
112 | NS_NONNULL_ASSERT(saPtr != NULL)((void) (0)); | ||||
113 | |||||
114 | pPtr = ns_malloc(sizeof(Prebind) + sizeof(NS_SOCKETint)*reuses-1); | ||||
115 | if (pPtr != NULL((void*)0)) { | ||||
116 | bool_Bool reuseport; | ||||
117 | size_t i; | ||||
118 | |||||
119 | pPtr->count = reuses; | ||||
120 | |||||
121 | reuseport = (reuses > 1); | ||||
122 | |||||
123 | for (i = 0u; i
| ||||
124 | if (*proto == 't') { | ||||
125 | pPtr->sockets[i] = Ns_SockBind(saPtr, reuseport); | ||||
| |||||
126 | } else if (*proto == 'u') { | ||||
127 | pPtr->sockets[i] = Ns_SockBindUdp(saPtr, reuseport); | ||||
128 | } else { | ||||
129 | Ns_Log(Error, "prebind: invalid protocol %s", proto); | ||||
130 | ns_free(pPtr); | ||||
131 | pPtr = NULL((void*)0); | ||||
132 | break; | ||||
133 | } | ||||
134 | |||||
135 | if (pPtr->sockets[i] == NS_INVALID_SOCKET(-1)) { | ||||
136 | Ns_LogSockaddr(Error, "prebind error on ", (const struct sockaddr *)saPtr); | ||||
137 | Ns_Log(Error, "prebind error: %s", strerror(errno(*__errno_location ()))); | ||||
138 | if (i == 0) { | ||||
139 | /* | ||||
140 | * Could not bind to a single port. Return NULL to | ||||
141 | * signal an invalid attempt. | ||||
142 | */ | ||||
143 | ns_free(pPtr); | ||||
144 | pPtr = NULL((void*)0); | ||||
145 | break; | ||||
146 | } | ||||
147 | } | ||||
148 | } | ||||
149 | } | ||||
150 | return pPtr; | ||||
151 | } | ||||
152 | |||||
153 | |||||
154 | /* | ||||
155 | *---------------------------------------------------------------------- | ||||
156 | * | ||||
157 | * PrebindGet -- | ||||
158 | * | ||||
159 | * Get a single socket from the prebind structure. In case of | ||||
160 | * success, the function returns in its last argument the prebound | ||||
161 | * socket and removes it from the set of available sockets. When | ||||
162 | * all sockets are consumed the prebind structure is freed and the | ||||
163 | * hash entry is removed. | ||||
164 | * | ||||
165 | * Results: | ||||
166 | * | ||||
167 | * NS_TRUE in case, there is a prebind structure for the provided | ||||
168 | * sockaddr or NS_FALSE on failure. | ||||
169 | * | ||||
170 | * Side effects: | ||||
171 | * Potentially freeing memory. | ||||
172 | * | ||||
173 | *---------------------------------------------------------------------- | ||||
174 | */ | ||||
175 | static bool_Bool | ||||
176 | PrebindGet(const char *proto, struct sockaddr *saPtr, NS_SOCKETint *sockPtr) | ||||
177 | { | ||||
178 | static Tcl_HashTable *tablePtr; | ||||
179 | Tcl_HashEntry *hPtr; | ||||
180 | bool_Bool foundEntry = NS_FALSE0; | ||||
181 | |||||
182 | NS_NONNULL_ASSERT(proto != NULL)((void) (0)); | ||||
183 | NS_NONNULL_ASSERT(saPtr != NULL)((void) (0)); | ||||
184 | NS_NONNULL_ASSERT(sockPtr != NULL)((void) (0)); | ||||
185 | |||||
186 | if (*proto == 't') { | ||||
187 | tablePtr = &preboundTcp; | ||||
188 | } else { | ||||
189 | tablePtr = &preboundUdp; | ||||
190 | } | ||||
191 | |||||
192 | Ns_MutexLock(&lock); | ||||
193 | hPtr = Tcl_FindHashEntry(tablePtr, (char *)saPtr)(*((tablePtr)->findProc))(tablePtr, (const char *)((char * )saPtr)); | ||||
194 | if (hPtr != NULL((void*)0)) { | ||||
195 | struct Prebind *pPtr; | ||||
196 | size_t i; | ||||
197 | bool_Bool allConsumed = NS_TRUE1; | ||||
198 | |||||
199 | /* | ||||
200 | * We found a prebound entry. | ||||
201 | */ | ||||
202 | foundEntry = NS_TRUE1; | ||||
203 | |||||
204 | pPtr = (struct Prebind *)Tcl_GetHashValue(hPtr)((hPtr)->clientData); | ||||
205 | for (i = 0u; i < pPtr->count; i++) { | ||||
206 | /* | ||||
207 | * Find an entry, which is usable | ||||
208 | */ | ||||
209 | if (pPtr->sockets[i] != NS_INVALID_SOCKET(-1)) { | ||||
210 | *sockPtr = pPtr->sockets[i]; | ||||
211 | pPtr->sockets[i] = NS_INVALID_SOCKET(-1); | ||||
212 | break; | ||||
213 | } | ||||
214 | } | ||||
215 | if (*sockPtr != NS_INVALID_SOCKET(-1)) { | ||||
216 | /* | ||||
217 | * Check, if there are more unconsumed entries. | ||||
218 | */ | ||||
219 | for (; i < pPtr->count; i++) { | ||||
220 | if (pPtr->sockets[i] != NS_INVALID_SOCKET(-1)) { | ||||
221 | /* | ||||
222 | * Yes, there are more unconsumed entries. | ||||
223 | */ | ||||
224 | allConsumed = NS_FALSE0; | ||||
225 | break; | ||||
226 | } | ||||
227 | } | ||||
228 | } | ||||
229 | if (allConsumed) { | ||||
230 | ns_free(pPtr); | ||||
231 | Tcl_DeleteHashEntry(hPtr); | ||||
232 | } | ||||
233 | } | ||||
234 | Ns_MutexUnlock(&lock); | ||||
235 | |||||
236 | return foundEntry; | ||||
237 | } | ||||
238 | |||||
239 | |||||
240 | /* | ||||
241 | *---------------------------------------------------------------------- | ||||
242 | * | ||||
243 | * PrebindCloseSockets -- | ||||
244 | * | ||||
245 | * Close the remaining prebound sockets. | ||||
246 | * | ||||
247 | * Results: | ||||
248 | * | ||||
249 | * None. | ||||
250 | * | ||||
251 | * Side effects: | ||||
252 | * Freeing memory. | ||||
253 | * | ||||
254 | *---------------------------------------------------------------------- | ||||
255 | */ | ||||
256 | static void | ||||
257 | PrebindCloseSockets(const char *proto, struct sockaddr *saPtr, struct Prebind *pPtr) | ||||
258 | { | ||||
259 | size_t i; | ||||
260 | unsigned short port; | ||||
261 | const char *addr; | ||||
262 | char ipString[NS_IPADDR_SIZE46]; | ||||
263 | int count = 0; | ||||
264 | |||||
265 | NS_NONNULL_ASSERT(proto != NULL)((void) (0)); | ||||
266 | NS_NONNULL_ASSERT(saPtr != NULL)((void) (0)); | ||||
267 | NS_NONNULL_ASSERT(pPtr != NULL)((void) (0)); | ||||
268 | |||||
269 | addr = ns_inet_ntop((struct sockaddr *)saPtr, ipString, sizeof(ipString)); | ||||
270 | port = Ns_SockaddrGetPort((struct sockaddr *)saPtr); | ||||
271 | |||||
272 | for (i = 0u; i < pPtr->count; i++) { | ||||
273 | NS_SOCKETint sock = pPtr->sockets[i]; | ||||
274 | |||||
275 | if (sock != NS_INVALID_SOCKET(-1)) { | ||||
276 | count ++; | ||||
277 | Ns_Log(Debug, "prebind closing %s socket %d\n", proto, sock); | ||||
278 | (void)ns_sockcloseclose(sock); | ||||
279 | } | ||||
280 | } | ||||
281 | ns_free(pPtr); | ||||
282 | Ns_Log(Warning, "prebind: closed unused %d %s socket(s): [%s]:%hd", | ||||
283 | count, proto, addr, port); | ||||
284 | } | ||||
285 | #endif | ||||
286 | |||||
287 | |||||
288 | /* | ||||
289 | *---------------------------------------------------------------------- | ||||
290 | * | ||||
291 | * Ns_SockListenEx -- | ||||
292 | * | ||||
293 | * Create a new TCP socket bound to the specified port and | ||||
294 | * listening for new connections. | ||||
295 | * | ||||
296 | * Results: | ||||
297 | * Socket descriptor or -1 on error. | ||||
298 | * | ||||
299 | * Side effects: | ||||
300 | * None. | ||||
301 | * | ||||
302 | *---------------------------------------------------------------------- | ||||
303 | */ | ||||
304 | |||||
305 | #ifndef _WIN32 | ||||
306 | NS_SOCKETint | ||||
307 | Ns_SockListenEx(const char *address, unsigned short port, int backlog, bool_Bool reuseport) | ||||
308 | { | ||||
309 | NS_SOCKETint sock = NS_INVALID_SOCKET(-1); | ||||
310 | struct NS_SOCKADDR_STORAGEsockaddr_storage sa; | ||||
311 | struct sockaddr *saPtr = (struct sockaddr *)&sa; | ||||
312 | |||||
313 | if (Ns_GetSockAddr(saPtr, address, port) == NS_OK) { | ||||
314 | bool_Bool found; | ||||
315 | |||||
316 | found = PrebindGet("tcp", saPtr, &sock); | ||||
317 | if (!found) { | ||||
318 | /* | ||||
319 | * Prebind did not find a prebound entry, try to bind now. | ||||
320 | */ | ||||
321 | sock = Ns_SockBind(saPtr, reuseport); | ||||
322 | //fprintf(stderr, "listen on port %hd binding with reuseport %d\n", port, reuseport); | ||||
323 | } else { | ||||
324 | //fprintf(stderr, "listen on port %hd already prebound\n", port); | ||||
325 | } | ||||
326 | |||||
327 | if (sock != NS_INVALID_SOCKET(-1) && listen(sock, backlog) == -1) { | ||||
328 | /* | ||||
329 | * Can't listen; close the opened socket | ||||
330 | */ | ||||
331 | ns_sockerrno_t err = ns_sockerrno(*__errno_location ()); | ||||
332 | |||||
333 | (void)ns_sockcloseclose(sock); | ||||
334 | errno(*__errno_location ()) = err; | ||||
335 | sock = NS_INVALID_SOCKET(-1); | ||||
336 | Ns_SetSockErrno(err); | ||||
337 | } | ||||
338 | } else { | ||||
339 | /* | ||||
340 | * We could not even get the sockaddr, so make clear, that saPtr | ||||
341 | * is invalid. | ||||
342 | */ | ||||
343 | saPtr = NULL((void*)0); | ||||
344 | } | ||||
345 | |||||
346 | /* | ||||
347 | * If forked binder is running and we could not allocate socket | ||||
348 | * directly, try to do it through the binder | ||||
349 | */ | ||||
350 | if (sock == NS_INVALID_SOCKET(-1) && binderRunning && saPtr != NULL((void*)0)) { | ||||
351 | sock = Ns_SockBinderListen('T', address, port, backlog); | ||||
352 | } | ||||
353 | |||||
354 | return sock; | ||||
355 | } | ||||
356 | #endif /* _WIN32 */ | ||||
357 | |||||
358 | |||||
359 | /* | ||||
360 | *---------------------------------------------------------------------- | ||||
361 | * | ||||
362 | * Ns_SockListenUdp -- | ||||
363 | * | ||||
364 | * Listen on the UDP socket for the given IP address and port. The | ||||
365 | * given address might be NULL, which implies the unspecified IP | ||||
366 | * address ("0.0.0.0" or "::"). | ||||
367 | * | ||||
368 | * Results: | ||||
369 | * Socket descriptor or -1 on error. | ||||
370 | * | ||||
371 | * Side effects: | ||||
372 | * May create a new socket if none prebound. | ||||
373 | * | ||||
374 | *---------------------------------------------------------------------- | ||||
375 | */ | ||||
376 | |||||
377 | NS_SOCKETint | ||||
378 | Ns_SockListenUdp(const char *address, unsigned short port, bool_Bool reuseport) | ||||
379 | { | ||||
380 | NS_SOCKETint sock = NS_INVALID_SOCKET(-1); | ||||
381 | struct NS_SOCKADDR_STORAGEsockaddr_storage sa; | ||||
382 | struct sockaddr *saPtr = (struct sockaddr *)&sa; | ||||
383 | |||||
384 | if (Ns_GetSockAddr(saPtr, address, port) == NS_OK) { | ||||
385 | bool_Bool found; | ||||
386 | |||||
387 | #ifndef _WIN32 | ||||
388 | found = PrebindGet("udp", saPtr, &sock); | ||||
389 | #else | ||||
390 | found = NS_FALSE0; | ||||
391 | #endif | ||||
392 | if (!found) { | ||||
393 | /* | ||||
394 | * Not prebound, bind now | ||||
395 | */ | ||||
396 | sock = Ns_SockBindUdp(saPtr, reuseport); | ||||
397 | } | ||||
398 | } | ||||
399 | |||||
400 | /* | ||||
401 | * If forked binder is running and we could not allocate socket | ||||
402 | * directly, try to do it through the binder | ||||
403 | */ | ||||
404 | |||||
405 | if (sock == NS_INVALID_SOCKET(-1) && binderRunning) { | ||||
406 | sock = Ns_SockBinderListen('U', address, port, 0); | ||||
407 | } | ||||
408 | |||||
409 | return sock; | ||||
410 | } | ||||
411 | |||||
412 | |||||
413 | /* | ||||
414 | *---------------------------------------------------------------------- | ||||
415 | * | ||||
416 | * Ns_SockListenRaw -- | ||||
417 | * | ||||
418 | * Listen on the raw socket addressed by the given protocol. | ||||
419 | * | ||||
420 | * Results: | ||||
421 | * Socket descriptor or -1 on error. | ||||
422 | * | ||||
423 | * Side effects: | ||||
424 | * May create a new socket if none prebound. | ||||
425 | * | ||||
426 | *---------------------------------------------------------------------- | ||||
427 | */ | ||||
428 | |||||
429 | NS_SOCKETint | ||||
430 | Ns_SockListenRaw(int proto) | ||||
431 | { | ||||
432 | NS_SOCKETint sock = NS_INVALID_SOCKET(-1); | ||||
433 | Tcl_HashEntry *hPtr; | ||||
434 | Tcl_HashSearch search; | ||||
435 | |||||
436 | Ns_MutexLock(&lock); | ||||
437 | hPtr = Tcl_FirstHashEntry(&preboundRaw, &search); | ||||
438 | while (hPtr != NULL((void*)0)) { | ||||
439 | if (proto == PTR2INT(Tcl_GetHashValue(hPtr))((int)(intptr_t)(((hPtr)->clientData)))) { | ||||
440 | sock = PTR2NSSOCK(Tcl_GetHashKey(&preboundRaw, hPtr))((int)(intptr_t)(((void *) (((&preboundRaw)->keyType == (1) || (&preboundRaw)->keyType == (-1)) ? (hPtr)-> key.oneWordValue : (hPtr)->key.string)))); | ||||
441 | Tcl_DeleteHashEntry(hPtr); | ||||
442 | break; | ||||
443 | } | ||||
444 | hPtr = Tcl_NextHashEntry(&search); | ||||
445 | } | ||||
446 | Ns_MutexUnlock(&lock); | ||||
447 | if (hPtr == NULL((void*)0)) { | ||||
448 | /* | ||||
449 | * Not prebound, bind now | ||||
450 | */ | ||||
451 | sock = Ns_SockBindRaw(proto); | ||||
452 | } | ||||
453 | |||||
454 | /* | ||||
455 | * If forked binder is running and we could not allocate socket | ||||
456 | * directly, try to do it through the binder | ||||
457 | */ | ||||
458 | |||||
459 | if (sock == NS_INVALID_SOCKET(-1) && binderRunning) { | ||||
460 | sock = Ns_SockBinderListen('R', NULL((void*)0), 0u, proto); | ||||
461 | } | ||||
462 | |||||
463 | return sock; | ||||
464 | } | ||||
465 | |||||
466 | |||||
467 | /* | ||||
468 | *---------------------------------------------------------------------- | ||||
469 | * | ||||
470 | * Ns_SockListenUnix -- | ||||
471 | * | ||||
472 | * Listen on the Unix-domain socket addressed by the given path. | ||||
473 | * | ||||
474 | * Results: | ||||
475 | * Socket descriptor or -1 on error. | ||||
476 | * | ||||
477 | * Side effects: | ||||
478 | * May create a new socket if none prebound. If backlog is zero, | ||||
479 | * DGRAM socket will be created otherwise STREAM socket | ||||
480 | * | ||||
481 | *---------------------------------------------------------------------- | ||||
482 | */ | ||||
483 | |||||
484 | NS_SOCKETint | ||||
485 | Ns_SockListenUnix(const char *path, int backlog, unsigned short mode) | ||||
486 | { | ||||
487 | NS_SOCKETint sock = NS_INVALID_SOCKET(-1); | ||||
488 | #ifndef _WIN32 | ||||
489 | Tcl_HashEntry *hPtr; | ||||
490 | Tcl_HashSearch search; | ||||
491 | |||||
492 | NS_NONNULL_ASSERT(path != NULL)((void) (0)); | ||||
493 | |||||
494 | /* | ||||
495 | * Check if already prebound | ||||
496 | */ | ||||
497 | Ns_MutexLock(&lock); | ||||
498 | hPtr = Tcl_FirstHashEntry(&preboundUnix, &search); | ||||
499 | while (hPtr != NULL((void*)0)) { | ||||
500 | const char *value = (char*) Tcl_GetHashValue(hPtr)((hPtr)->clientData); | ||||
501 | |||||
502 | if (STREQ(path, value)(((*(path)) == (*(value))) && (strcmp((path),(value)) == 0))) { | ||||
503 | sock = PTR2NSSOCK(Tcl_GetHashKey(&preboundRaw, hPtr))((int)(intptr_t)(((void *) (((&preboundRaw)->keyType == (1) || (&preboundRaw)->keyType == (-1)) ? (hPtr)-> key.oneWordValue : (hPtr)->key.string)))); | ||||
504 | Tcl_DeleteHashEntry(hPtr); | ||||
505 | break; | ||||
506 | } | ||||
507 | hPtr = Tcl_NextHashEntry(&search); | ||||
508 | } | ||||
509 | Ns_MutexUnlock(&lock); | ||||
510 | |||||
511 | if (hPtr == NULL((void*)0)) { | ||||
512 | /* | ||||
513 | * Not prebound, bind now | ||||
514 | */ | ||||
515 | sock = Ns_SockBindUnix(path, backlog > 0 ? SOCK_STREAMSOCK_STREAM : SOCK_DGRAMSOCK_DGRAM, mode); | ||||
516 | } | ||||
517 | if (sock >= 0 && backlog > 0 && listen(sock, backlog) == -1) { | ||||
518 | /* | ||||
519 | * Can't listen; close the opened socket | ||||
520 | */ | ||||
521 | ns_sockerrno_t err = ns_sockerrno(*__errno_location ()); | ||||
522 | |||||
523 | (void)ns_sockcloseclose(sock); | ||||
524 | errno(*__errno_location ()) = err; | ||||
525 | sock = NS_INVALID_SOCKET(-1); | ||||
526 | Ns_SetSockErrno(err); | ||||
527 | } | ||||
528 | |||||
529 | /* | ||||
530 | * If forked binder is running and we could not allocate socket | ||||
531 | * directly, try to do it through the binder | ||||
532 | */ | ||||
533 | |||||
534 | if (sock == NS_INVALID_SOCKET(-1) && binderRunning) { | ||||
535 | sock = Ns_SockBinderListen('D', path, mode, backlog); | ||||
536 | } | ||||
537 | #endif /* _WIN32 */ | ||||
538 | return sock; | ||||
539 | } | ||||
540 | |||||
541 | |||||
542 | /* | ||||
543 | *---------------------------------------------------------------------- | ||||
544 | * | ||||
545 | * Ns_SockBindUdp -- | ||||
546 | * | ||||
547 | * Create a UDP socket and bind it to the passed-in address. | ||||
548 | * | ||||
549 | * Results: | ||||
550 | * Socket descriptor or -1 on error. | ||||
551 | * | ||||
552 | * Side effects: | ||||
553 | * None. | ||||
554 | * | ||||
555 | *---------------------------------------------------------------------- | ||||
556 | */ | ||||
557 | |||||
558 | NS_SOCKETint | ||||
559 | Ns_SockBindUdp(const struct sockaddr *saPtr, bool_Bool reusePort) | ||||
560 | { | ||||
561 | NS_SOCKETint sock; | ||||
562 | int n = 1; | ||||
563 | |||||
564 | NS_NONNULL_ASSERT(saPtr != NULL)((void) (0)); | ||||
565 | |||||
566 | sock = (NS_SOCKETint)socket((int)saPtr->sa_family, SOCK_DGRAMSOCK_DGRAM, 0); | ||||
567 | |||||
568 | if (sock != NS_INVALID_SOCKET(-1) | ||||
569 | && (setsockopt(sock, SOL_SOCKET1, SO_REUSEADDR2, (char*)&n, (socklen_t)sizeof(n)) == -1 | ||||
570 | || setsockopt(sock, SOL_SOCKET1, SO_BROADCAST6, (char*)&n, (socklen_t)sizeof(n)) == -1 | ||||
571 | || bind(sock, saPtr, Ns_SockaddrGetSockLen(saPtr)) == -1) | ||||
572 | ) { | ||||
573 | ns_sockerrno_t err = ns_sockerrno(*__errno_location ()); | ||||
574 | |||||
575 | (void)ns_sockcloseclose(sock); | ||||
576 | sock = NS_INVALID_SOCKET(-1); | ||||
577 | Ns_SetSockErrno(err); | ||||
578 | } else { | ||||
579 | #if defined(SO_REUSEPORT15) | ||||
580 | if (reusePort) { | ||||
581 | int optval = 1; | ||||
582 | setsockopt(sock, SOL_SOCKET1, SO_REUSEPORT15, &optval, (socklen_t)sizeof(optval)); | ||||
583 | } | ||||
584 | #endif | ||||
585 | } | ||||
586 | |||||
587 | return sock; | ||||
588 | } | ||||
589 | |||||
590 | |||||
591 | /* | ||||
592 | *---------------------------------------------------------------------- | ||||
593 | * | ||||
594 | * Ns_SockBindUnix -- | ||||
595 | * | ||||
596 | * Create a Unix-domain socket and bind it to the passed-in | ||||
597 | * file path. | ||||
598 | * | ||||
599 | * Results: | ||||
600 | * Socket descriptor or -1 on error. | ||||
601 | * | ||||
602 | * Side effects: | ||||
603 | * None. | ||||
604 | * | ||||
605 | *---------------------------------------------------------------------- | ||||
606 | */ | ||||
607 | |||||
608 | NS_SOCKETint | ||||
609 | Ns_SockBindUnix(const char *path, int socktype, unsigned short mode) | ||||
610 | { | ||||
611 | #ifdef _WIN32 | ||||
612 | return NS_INVALID_SOCKET(-1); | ||||
613 | #else | ||||
614 | NS_SOCKETint sock; | ||||
615 | struct sockaddr_un addr; | ||||
616 | size_t pathLength; | ||||
617 | |||||
618 | NS_NONNULL_ASSERT(path != NULL)((void) (0)); | ||||
619 | pathLength = strlen(path); | ||||
620 | |||||
621 | if (pathLength >= sizeof(addr.sun_path)) { | ||||
622 | Ns_Log(Error, "provided path exceeds maximum length: %s\n", path); | ||||
623 | return NS_INVALID_SOCKET(-1); | ||||
624 | } | ||||
625 | |||||
626 | memset(&addr, 0, sizeof(addr)); | ||||
627 | addr.sun_family = AF_UNIX1; | ||||
628 | memcpy(addr.sun_path, path, pathLength + 1); | ||||
629 | unlink(path); | ||||
630 | |||||
631 | sock = socket(AF_UNIX1, socktype > 0 ? socktype : SOCK_STREAMSOCK_STREAM, 0); | ||||
632 | /* | ||||
633 | * There is a small race condition below, since the permissions on | ||||
634 | * the socket are checked not in an atomic fashion and might be | ||||
635 | * changed immediately after the bind operation. Unfortunately, | ||||
636 | * fchmod is not portable. | ||||
637 | */ | ||||
638 | if (sock != NS_INVALID_SOCKET(-1) | ||||
639 | && (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1 | ||||
640 | || (mode != 0u && chmod(path, mode) == -1)) | ||||
641 | ) { | ||||
642 | ns_sockerrno_t err = errno(*__errno_location ()); | ||||
643 | |||||
644 | (void)ns_sockcloseclose(sock); | ||||
645 | sock = NS_INVALID_SOCKET(-1); | ||||
646 | Ns_SetSockErrno(err); | ||||
647 | } | ||||
648 | |||||
649 | return sock; | ||||
650 | #endif /* _WIN32 */ | ||||
651 | } | ||||
652 | |||||
653 | |||||
654 | /* | ||||
655 | *---------------------------------------------------------------------- | ||||
656 | * | ||||
657 | * Ns_SockBindRaw -- | ||||
658 | * | ||||
659 | * Create a raw socket. It does not bind, hence the call name | ||||
660 | * is not entirely correct but is on-pair with other types of | ||||
661 | * sockets (udp, tcp, unix). | ||||
662 | * | ||||
663 | * Results: | ||||
664 | * Socket descriptor or -1 on error. | ||||
665 | * | ||||
666 | * Side effects: | ||||
667 | * None. | ||||
668 | * | ||||
669 | *---------------------------------------------------------------------- | ||||
670 | */ | ||||
671 | |||||
672 | NS_SOCKETint | ||||
673 | Ns_SockBindRaw(int proto) | ||||
674 | { | ||||
675 | NS_SOCKETint sock; | ||||
676 | |||||
677 | sock = (NS_SOCKETint)socket(AF_INET2, SOCK_RAWSOCK_RAW, proto); | ||||
678 | |||||
679 | if (sock == NS_INVALID_SOCKET(-1)) { | ||||
680 | ns_sockerrno_t err = ns_sockerrno(*__errno_location ()); | ||||
681 | |||||
682 | Ns_SetSockErrno(err); | ||||
683 | } | ||||
684 | |||||
685 | return sock; | ||||
686 | } | ||||
687 | |||||
688 | |||||
689 | /* | ||||
690 | *---------------------------------------------------------------------- | ||||
691 | * | ||||
692 | * NsInitBinder -- | ||||
693 | * | ||||
694 | * Initialize the pre-bind tables. | ||||
695 | * | ||||
696 | * Results: | ||||
697 | * None. | ||||
698 | * | ||||
699 | * Side effects: | ||||
700 | * None. | ||||
701 | * | ||||
702 | *---------------------------------------------------------------------- | ||||
703 | */ | ||||
704 | |||||
705 | void | ||||
706 | NsInitBinder(void) | ||||
707 | { | ||||
708 | Ns_MutexInit(&lock); | ||||
709 | Ns_MutexSetName(&lock, "binder"); | ||||
710 | |||||
711 | Tcl_InitHashTable(&preboundTcp, (int)(sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage) / sizeof(int))); | ||||
712 | Tcl_InitHashTable(&preboundUdp, (int)(sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage) / sizeof(int))); | ||||
713 | Tcl_InitHashTable(&preboundRaw, TCL_ONE_WORD_KEYS(1)); | ||||
714 | Tcl_InitHashTable(&preboundUnix, TCL_STRING_KEYS(0)); | ||||
715 | } | ||||
716 | |||||
717 | |||||
718 | /* | ||||
719 | *---------------------------------------------------------------------- | ||||
720 | * | ||||
721 | * NsPreBind -- | ||||
722 | * | ||||
723 | * Pre-bind any requested ports (called from Ns_Main at startup). | ||||
724 | * | ||||
725 | * Results: | ||||
726 | * None. | ||||
727 | * | ||||
728 | * Side effects: | ||||
729 | * May pre-bind to one or more ports. | ||||
730 | * | ||||
731 | *---------------------------------------------------------------------- | ||||
732 | */ | ||||
733 | |||||
734 | Ns_ReturnCode | ||||
735 | NsPreBind(const char *args, const char *file) | ||||
736 | { | ||||
737 | Ns_ReturnCode status = NS_OK; | ||||
738 | |||||
739 | #ifndef _WIN32 | ||||
740 | |||||
741 | if (args != NULL((void*)0)) { | ||||
| |||||
742 | status = PrebindSockets(args); | ||||
743 | } | ||||
744 | |||||
745 | /* | ||||
746 | * Check, if the bind options were provided via file. If so, parse | ||||
747 | * and interpret it. | ||||
748 | */ | ||||
749 | if (status == NS_OK && file != NULL((void*)0)) { | ||||
750 | Tcl_Channel chan = Tcl_OpenFileChannel(NULL((void*)0), file, "r", 0); | ||||
751 | |||||
752 | if (chan == NULL((void*)0)) { | ||||
753 | Ns_Log(Error, "NsPreBind: can't open file '%s': '%s'", file, | ||||
754 | strerror(Tcl_GetErrno())); | ||||
755 | } else { | ||||
756 | Tcl_DString line; | ||||
757 | |||||
758 | Tcl_DStringInit(&line); | ||||
759 | while (Tcl_Eof(chan) == 0) { | ||||
760 | Tcl_DStringSetLength(&line, 0); | ||||
761 | if (Tcl_Gets(chan, &line) > 0) { | ||||
762 | status = PrebindSockets(Tcl_DStringValue(&line)((&line)->string)); | ||||
763 | if (status != NS_OK) { | ||||
764 | break; | ||||
765 | } | ||||
766 | } | ||||
767 | } | ||||
768 | Tcl_DStringFree(&line); | ||||
769 | Tcl_Close(NULL((void*)0), chan); | ||||
770 | } | ||||
771 | } | ||||
772 | #endif /* _WIN32 */ | ||||
773 | return status; | ||||
774 | } | ||||
775 | |||||
776 | |||||
777 | /* | ||||
778 | *---------------------------------------------------------------------- | ||||
779 | * | ||||
780 | * NsClosePreBound -- | ||||
781 | * | ||||
782 | * Close remaining pre-bound sockets not consumed by anybody. | ||||
783 | * | ||||
784 | * Results: | ||||
785 | * None. | ||||
786 | * | ||||
787 | * Side effects: | ||||
788 | * Pre-bind hash-tables are cleaned and re-initialized. | ||||
789 | * | ||||
790 | *---------------------------------------------------------------------- | ||||
791 | */ | ||||
792 | |||||
793 | void | ||||
794 | NsClosePreBound(void) | ||||
795 | { | ||||
796 | #ifndef _WIN32 | ||||
797 | Tcl_HashEntry *hPtr; | ||||
798 | Tcl_HashSearch search; | ||||
799 | NS_SOCKETint sock; | ||||
800 | struct sockaddr *saPtr; | ||||
801 | |||||
802 | Ns_MutexLock(&lock); | ||||
803 | |||||
804 | /* | ||||
805 | * Close TCP sockets | ||||
806 | */ | ||||
807 | hPtr = Tcl_FirstHashEntry(&preboundTcp, &search); | ||||
808 | while (hPtr != NULL((void*)0)) { | ||||
809 | saPtr = (struct sockaddr *)Tcl_GetHashKey(&preboundTcp, hPtr)((void *) (((&preboundTcp)->keyType == (1) || (&preboundTcp )->keyType == (-1)) ? (hPtr)->key.oneWordValue : (hPtr) ->key.string)); | ||||
810 | PrebindCloseSockets("tcp", saPtr, Tcl_GetHashValue(hPtr)((hPtr)->clientData)); | ||||
811 | Tcl_DeleteHashEntry(hPtr); | ||||
812 | hPtr = Tcl_NextHashEntry(&search); | ||||
813 | } | ||||
814 | Tcl_DeleteHashTable(&preboundTcp); | ||||
815 | Tcl_InitHashTable(&preboundTcp, sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage)/sizeof(int)); | ||||
816 | |||||
817 | /* | ||||
818 | * Close UDP sockets | ||||
819 | */ | ||||
820 | hPtr = Tcl_FirstHashEntry(&preboundUdp, &search); | ||||
821 | while (hPtr != NULL((void*)0)) { | ||||
822 | saPtr = (struct sockaddr *)Tcl_GetHashKey(&preboundUdp, hPtr)((void *) (((&preboundUdp)->keyType == (1) || (&preboundUdp )->keyType == (-1)) ? (hPtr)->key.oneWordValue : (hPtr) ->key.string)); | ||||
823 | PrebindCloseSockets("udp", saPtr, Tcl_GetHashValue(hPtr)((hPtr)->clientData)); | ||||
824 | Tcl_DeleteHashEntry(hPtr); | ||||
825 | hPtr = Tcl_NextHashEntry(&search); | ||||
826 | } | ||||
827 | Tcl_DeleteHashTable(&preboundUdp); | ||||
828 | Tcl_InitHashTable(&preboundUdp, sizeof(struct NS_SOCKADDR_STORAGEsockaddr_storage)/sizeof(int)); | ||||
829 | |||||
830 | /* | ||||
831 | * Close raw sockets | ||||
832 | */ | ||||
833 | hPtr = Tcl_FirstHashEntry(&preboundRaw, &search); | ||||
834 | while (hPtr != NULL((void*)0)) { | ||||
835 | int port; | ||||
836 | |||||
837 | sock = PTR2NSSOCK(Tcl_GetHashKey(&preboundRaw, hPtr))((int)(intptr_t)(((void *) (((&preboundRaw)->keyType == (1) || (&preboundRaw)->keyType == (-1)) ? (hPtr)-> key.oneWordValue : (hPtr)->key.string)))); | ||||
838 | port = PTR2INT(Tcl_GetHashValue(hPtr))((int)(intptr_t)(((hPtr)->clientData))); | ||||
839 | Ns_Log(Warning, "prebind: closed unused raw socket: %d = %d", | ||||
840 | port, sock); | ||||
841 | (void)ns_sockcloseclose(sock); | ||||
842 | Tcl_DeleteHashEntry(hPtr); | ||||
843 | hPtr = Tcl_NextHashEntry(&search); | ||||
844 | } | ||||
845 | Tcl_DeleteHashTable(&preboundRaw); | ||||
846 | Tcl_InitHashTable(&preboundRaw, TCL_ONE_WORD_KEYS(1)); | ||||
847 | |||||
848 | /* | ||||
849 | * Close Unix-domain sockets | ||||
850 | */ | ||||
851 | hPtr = Tcl_FirstHashEntry(&preboundUnix, &search); | ||||
852 | while (hPtr != NULL((void*)0)) { | ||||
853 | const char *addr = (char *) Tcl_GetHashKey(&preboundUnix, hPtr)((void *) (((&preboundUnix)->keyType == (1) || (&preboundUnix )->keyType == (-1)) ? (hPtr)->key.oneWordValue : (hPtr) ->key.string)); | ||||
854 | |||||
855 | sock = PTR2NSSOCK(Tcl_GetHashValue(hPtr))((int)(intptr_t)(((hPtr)->clientData))); | ||||
856 | Ns_Log(Warning, "prebind: closed unused Unix-domain socket: [%s] %d", | ||||
857 | addr, sock); | ||||
858 | (void)ns_sockcloseclose(sock); | ||||
859 | Tcl_DeleteHashEntry(hPtr); | ||||
860 | hPtr = Tcl_NextHashEntry(&search); | ||||
861 | } | ||||
862 | Tcl_DeleteHashTable(&preboundUnix); | ||||
863 | Tcl_InitHashTable(&preboundUnix, TCL_STRING_KEYS(0)); | ||||
864 | |||||
865 | Ns_MutexUnlock(&lock); | ||||
866 | #endif /* _WIN32 */ | ||||
867 | } | ||||
868 | |||||
869 | |||||
870 | /* | ||||
871 | *---------------------------------------------------------------------- | ||||
872 | * | ||||
873 | * PreBind -- | ||||
874 | * | ||||
875 | * Pre-bind to one or more ports in a comma-separated list: | ||||
876 | * | ||||
877 | * addr:port[/protocol][#number] | ||||
878 | * port[/protocol][#number] | ||||
879 | * 0/icmp[/count] | ||||
880 | * /path[|mode] | ||||
881 | * | ||||
882 | * protocol: tcp|udp | ||||
883 | * mode: mode bits as used by "chmod" specified as octal value | ||||
884 | * | ||||
885 | * Example: nsd -c -b /tmp/foo,localhost:9999/tcp#2,localhost:9998,udp:9997 | ||||
886 | * Results: | ||||
887 | * None. | ||||
888 | * | ||||
889 | * Side effects: | ||||
890 | * Sockets are left in bound state for later listen | ||||
891 | * in Ns_SockListen*(). | ||||
892 | * | ||||
893 | *---------------------------------------------------------------------- | ||||
894 | */ | ||||
895 | #ifndef _WIN32 | ||||
896 | |||||
897 | static Ns_ReturnCode | ||||
898 | PrebindSockets(const char *spec) | ||||
899 | { | ||||
900 | Tcl_HashEntry *hPtr; | ||||
901 | int isNew = 0, specCount = 0; | ||||
902 | char *next, *line, *lines; | ||||
903 | Ns_ReturnCode status = NS_OK; | ||||
904 | struct NS_SOCKADDR_STORAGEsockaddr_storage sa; | ||||
905 | struct sockaddr *saPtr = (struct sockaddr *)&sa; | ||||
906 | |||||
907 | NS_NONNULL_ASSERT(spec != NULL)((void) (0)); | ||||
908 | |||||
909 | line = lines = ns_strdup(spec); | ||||
910 | |||||
911 | for (; line != NULL((void*)0); line = next) { | ||||
912 | const char *proto; | ||||
913 | char *addr, *p, *str = NULL((void*)0), *end; | ||||
914 | unsigned short port = 0u; | ||||
915 | long reuses; | ||||
916 | struct Prebind *pPtr; | ||||
917 | |||||
918 | specCount ++; | ||||
919 | /* | ||||
920 | * Find the next comma separated token. | ||||
921 | */ | ||||
922 | next = strchr(line, INTCHAR(',')((int)((unsigned char)((','))))); | ||||
923 | if (next != NULL((void*)0)) { | ||||
924 | *next++ = '\0'; | ||||
925 | } | ||||
926 | |||||
927 | /* | ||||
928 | * Set default proto and addr. | ||||
929 | */ | ||||
930 | proto = "tcp"; | ||||
931 | addr = (char *)NS_IP_UNSPECIFIED"::"; | ||||
932 | reuses = 1; | ||||
933 | |||||
934 | /* | ||||
935 | * Parse reuses count | ||||
936 | */ | ||||
937 | p = strrchr(line, INTCHAR('#')((int)((unsigned char)(('#'))))); | ||||
938 | if (p != NULL((void*)0)) { | ||||
939 | *p++ = '\0'; | ||||
940 | reuses = strtol(p, NULL((void*)0), 10); | ||||
941 | if (reuses < 1) { | ||||
942 | Ns_Log(Warning, "prebind: ignore invalid number of protoport reuses: '%s'", p); | ||||
943 | reuses = 1; | ||||
944 | } | ||||
945 | } | ||||
946 | |||||
947 | /* | ||||
948 | * Parse "addr:port" or "port" | ||||
949 | * | ||||
950 | * addr:port[/protocol][#number] | ||||
951 | * port[/protocol][#number] | ||||
952 | * 0/icmp[/count] | ||||
953 | */ | ||||
954 | { | ||||
955 | char *portStr; | ||||
956 | bool_Bool hostParsedOk = Ns_HttpParseHost2(line, NS_TRUE1, &addr, &portStr, &end); | ||||
957 | |||||
958 | if (hostParsedOk && line != end && addr != portStr ) { | ||||
959 | long l; | ||||
960 | |||||
961 | if (portStr != NULL((void*)0)) { | ||||
962 | l = strtol(portStr, NULL((void*)0), 10); | ||||
963 | } else { | ||||
964 | assert(addr != NULL)((void) (0)); | ||||
965 | l = strtol(addr, NULL((void*)0), 10); | ||||
966 | addr = (char *)NS_IP_UNSPECIFIED"::"; | ||||
967 | } | ||||
968 | port = (l >= 0) ? (unsigned short)l : 0u; | ||||
969 | |||||
970 | /* | ||||
971 | * Parse protocol | ||||
972 | */ | ||||
973 | if (*line != '/' && (str = strchr(line, INTCHAR('/')((int)((unsigned char)(('/')))))) != NULL((void*)0)) { | ||||
974 | *str++ = '\0'; | ||||
975 | proto = str; | ||||
976 | } | ||||
977 | } else { | ||||
978 | Ns_Log(Debug, "prebind: line <%s> was not parsed ok, must be UNIX", line); | ||||
979 | proto = "unix"; | ||||
980 | } | ||||
981 | /* | ||||
982 | * Continue parsing after "addr:port|port" | ||||
983 | */ | ||||
984 | line = end; | ||||
985 | } | ||||
986 | |||||
987 | /* | ||||
988 | * TCP | ||||
989 | */ | ||||
990 | Ns_Log(Notice, "prebind: try proto %s addr %s port %d reuses %ld", | ||||
991 | proto, addr, port, reuses); | ||||
992 | |||||
993 | if (STREQ(proto, "tcp")(((*(proto)) == (*("tcp"))) && (strcmp((proto),("tcp" )) == 0)) && port > 0) { | ||||
994 | if (Ns_GetSockAddr(saPtr, addr, port) != NS_OK) { | ||||
995 | Ns_Log(Error, "prebind: tcp: invalid address: [%s]:%d", addr, port); | ||||
996 | continue; | ||||
997 | } | ||||
998 | hPtr = Tcl_CreateHashEntry(&preboundTcp, (char *) &sa, &isNew)(*((&preboundTcp)->createProc))(&preboundTcp, (const char *)((char *) &sa), &isNew); | ||||
999 | if (isNew == 0) { | ||||
1000 | Ns_Log(Error, "prebind: tcp: duplicate entry: [%s]:%d", | ||||
1001 | addr, port); | ||||
1002 | continue; | ||||
1003 | } | ||||
1004 | |||||
1005 | Ns_LogSockaddr(Notice, "prebind adds", (const struct sockaddr *)saPtr); | ||||
1006 | |||||
1007 | pPtr = PrebindAlloc(proto, (size_t)reuses, saPtr); | ||||
1008 | if (pPtr == NULL((void*)0)) { | ||||
1009 | Tcl_DeleteHashEntry(hPtr); | ||||
1010 | status = NS_ERROR; | ||||
1011 | break; | ||||
1012 | } | ||||
1013 | Tcl_SetHashValue(hPtr, pPtr)((hPtr)->clientData = (ClientData) (pPtr)); | ||||
1014 | Ns_Log(Notice, "prebind: tcp: [%s]:%d", addr, port); | ||||
1015 | } | ||||
1016 | |||||
1017 | /* | ||||
1018 | * UDP | ||||
1019 | */ | ||||
1020 | if (STREQ(proto, "udp")(((*(proto)) == (*("udp"))) && (strcmp((proto),("udp" )) == 0)) && port > 0) { | ||||
1021 | if (Ns_GetSockAddr(saPtr, addr, port) != NS_OK) { | ||||
1022 | Ns_Log(Error, "prebind: udp: invalid address: [%s]:%d", | ||||
1023 | addr, port); | ||||
1024 | continue; | ||||
1025 | } | ||||
1026 | hPtr = Tcl_CreateHashEntry(&preboundUdp, (char *)saPtr, &isNew)(*((&preboundUdp)->createProc))(&preboundUdp, (const char *)((char *)saPtr), &isNew); | ||||
1027 | if (isNew == 0) { | ||||
1028 | Ns_Log(Error, "prebind: udp: duplicate entry: [%s]:%d", | ||||
1029 | addr, port); | ||||
1030 | continue; | ||||
1031 | } | ||||
1032 | pPtr = PrebindAlloc(proto, (size_t)reuses, saPtr); | ||||
1033 | if (pPtr == NULL((void*)0)) { | ||||
1034 | Tcl_DeleteHashEntry(hPtr); | ||||
1035 | status = NS_ERROR; | ||||
1036 | break; | ||||
1037 | } | ||||
1038 | Tcl_SetHashValue(hPtr, pPtr)((hPtr)->clientData = (ClientData) (pPtr)); | ||||
1039 | Ns_Log(Notice, "prebind: udp: [%s]:%d", addr, port); | ||||
1040 | } | ||||
1041 | |||||
1042 | /* | ||||
1043 | * ICMP | ||||
1044 | * | ||||
1045 | * Example: | ||||
1046 | * 0/icmp[/count] | ||||
1047 | */ | ||||
1048 | if (strncmp(proto, "icmp", 4u) == 0) { | ||||
1049 | long count = 1; | ||||
1050 | |||||
1051 | /* | ||||
1052 | * Parse count | ||||
1053 | */ | ||||
1054 | if (str != NULL((void*)0)) { | ||||
1055 | str = strchr(str, INTCHAR('/')((int)((unsigned char)(('/'))))); | ||||
1056 | if (str != NULL((void*)0)) { | ||||
1057 | *(str++) = '\0'; | ||||
1058 | count = strtol(str, NULL((void*)0), 10); | ||||
1059 | } | ||||
1060 | } | ||||
1061 | while (count--) { | ||||
1062 | NS_SOCKETint sock = Ns_SockBindRaw(IPPROTO_ICMPIPPROTO_ICMP); | ||||
1063 | if (sock == NS_INVALID_SOCKET(-1)) { | ||||
1064 | Ns_Log(Error, "prebind: bind error for icmp: %s", strerror(errno(*__errno_location ()))); | ||||
1065 | continue; | ||||
1066 | } | ||||
1067 | hPtr = Tcl_CreateHashEntry(&preboundRaw, NSSOCK2PTR(sock), &isNew)(*((&preboundRaw)->createProc))(&preboundRaw, (const char *)(((void *)(intptr_t)(sock))), &isNew); | ||||
1068 | if (isNew == 0) { | ||||
1069 | Ns_Log(Error, "prebind: icmp: duplicate entry"); | ||||
1070 | (void)ns_sockcloseclose(sock); | ||||
1071 | continue; | ||||
1072 | } | ||||
1073 | Tcl_SetHashValue(hPtr, IPPROTO_ICMP)((hPtr)->clientData = (ClientData) (IPPROTO_ICMP)); | ||||
1074 | Ns_Log(Notice, "prebind: icmp: %d", sock); | ||||
1075 | } | ||||
1076 | } | ||||
1077 | |||||
1078 | /* | ||||
1079 | * Unix-domain socket | ||||
1080 | * a line starting with a '/' means: path, which | ||||
1081 | * implies a unix-domain socket. | ||||
1082 | */ | ||||
1083 | if (STREQ(proto, "unix")(((*(proto)) == (*("unix"))) && (strcmp((proto),("unix" )) == 0))) { | ||||
1084 | if (Ns_PathIsAbsolute(line) == NS_TRUE1) { | ||||
1085 | unsigned short mode = 0u; | ||||
1086 | NS_SOCKETint sock; | ||||
1087 | |||||
1088 | Ns_Log(Debug, "prebind: Unix-domain socket <%s>\n", line); | ||||
1089 | |||||
1090 | /* | ||||
1091 | * Parse mode | ||||
1092 | */ | ||||
1093 | str = strchr(line, INTCHAR('|')((int)((unsigned char)(('|'))))); | ||||
1094 | if (str != NULL((void*)0)) { | ||||
1095 | long l; | ||||
1096 | |||||
1097 | *(str++) = '\0'; | ||||
1098 | l = strtol(str, NULL((void*)0), 8); | ||||
1099 | if (l > 0) { | ||||
1100 | mode = (unsigned short)l; | ||||
1101 | } else { | ||||
1102 | Ns_Log(Error, "prebind: unix: ignore invalid mode value: %s", line); | ||||
1103 | } | ||||
1104 | } | ||||
1105 | hPtr = Tcl_CreateHashEntry(&preboundUnix, (char *) line, &isNew)(*((&preboundUnix)->createProc))(&preboundUnix, (const char *)((char *) line), &isNew); | ||||
1106 | if (isNew == 0) { | ||||
1107 | Ns_Log(Error, "prebind: unix: duplicate entry: %s", line); | ||||
1108 | continue; | ||||
1109 | } | ||||
1110 | sock = Ns_SockBindUnix(line, SOCK_STREAMSOCK_STREAM, mode); | ||||
1111 | if (sock == NS_INVALID_SOCKET(-1)) { | ||||
1112 | Ns_Log(Error, "prebind: unix: %s: %s", proto, strerror(errno(*__errno_location ()))); | ||||
1113 | Tcl_DeleteHashEntry(hPtr); | ||||
1114 | continue; | ||||
1115 | } | ||||
1116 | Tcl_SetHashValue(hPtr, NSSOCK2PTR(sock))((hPtr)->clientData = (ClientData) (((void *)(intptr_t)(sock )))); | ||||
1117 | Ns_Log(Notice, "prebind: unix: %s = %d", line, sock); | ||||
1118 | } else { | ||||
1119 | Ns_Log(Warning, "prebind: invalid entry #%d: '%s'", specCount, spec); | ||||
1120 | } | ||||
1121 | } | ||||
1122 | } | ||||
1123 | ns_free(lines); | ||||
1124 | |||||
1125 | return status; | ||||
1126 | } | ||||
1127 | #endif | ||||
1128 | |||||
1129 | |||||
1130 | /* | ||||
1131 | *---------------------------------------------------------------------- | ||||
1132 | * | ||||
1133 | * Ns_SockBinderListen -- | ||||
1134 | * | ||||
1135 | * Create a new TCP/UDP/Unix socket bound to the specified port | ||||
1136 | * and listening for new connections. | ||||
1137 | * | ||||
1138 | * The following types are defined: | ||||
1139 | * T - TCP socket | ||||
1140 | * U - UDP socket | ||||
1141 | * D - Unix domain socket | ||||
1142 | * R - raw socket | ||||
1143 | * | ||||
1144 | * Results: | ||||
1145 | * Socket descriptor or -1 on error. | ||||
1146 | * | ||||
1147 | * Side effects: | ||||
1148 | * None. | ||||
1149 | * | ||||
1150 | *---------------------------------------------------------------------- | ||||
1151 | */ | ||||
1152 | |||||
1153 | NS_SOCKETint | ||||
1154 | Ns_SockBinderListen(char type, const char *address, unsigned short port, int options) | ||||
1155 | { | ||||
1156 | NS_SOCKETint sock = NS_INVALID_SOCKET(-1); | ||||
1157 | #ifndef _WIN32 | ||||
1158 | ns_sockerrno_t err = 0; | ||||
1159 | ssize_t n; | ||||
1160 | char data[NS_IPADDR_SIZE46]; | ||||
1161 | struct msghdr msg; | ||||
1162 | struct iovec iov[4]; | ||||
1163 | |||||
1164 | if (address == NULL((void*)0)) { | ||||
1165 | address = NS_IP_UNSPECIFIED"::"; | ||||
1166 | } | ||||
1167 | |||||
1168 | /* | ||||
1169 | * Build and send message. | ||||
1170 | */ | ||||
1171 | iov[0].iov_base = (void*) &options; | ||||
1172 | iov[0].iov_len = sizeof(options); | ||||
1173 | iov[1].iov_base = (void*) &port; | ||||
1174 | iov[1].iov_len = sizeof(port); | ||||
1175 | iov[2].iov_base = (void*) &type; | ||||
1176 | iov[2].iov_len = sizeof(type); | ||||
1177 | iov[3].iov_base = (void*) data; | ||||
1178 | iov[3].iov_len = sizeof(data); | ||||
1179 | |||||
1180 | strncpy(data, address, sizeof(data)-1); | ||||
1181 | memset(&msg, 0, sizeof(msg)); | ||||
1182 | msg.msg_iov = iov; | ||||
1183 | msg.msg_iovlen = 4; | ||||
1184 | n = sendmsg(binderRequest[1], &msg, 0); | ||||
1185 | if (n != REQUEST_SIZE(sizeof(int) + sizeof(int) + sizeof(int) + 46)) { | ||||
1186 | Ns_Log(Error, "Ns_SockBinderListen: sendmsg() failed: sent %" PRIdz"zd" " bytes, '%s'", | ||||
1187 | n, strerror(errno(*__errno_location ()))); | ||||
1188 | return -1; | ||||
1189 | } | ||||
1190 | |||||
1191 | /* | ||||
1192 | * Revive reply. | ||||
1193 | */ | ||||
1194 | iov[0].iov_base = (void*) &err; | ||||
1195 | iov[0].iov_len = sizeof(int); | ||||
1196 | memset(&msg, 0, sizeof(msg)); | ||||
1197 | msg.msg_iov = iov; | ||||
1198 | msg.msg_iovlen = 1; | ||||
1199 | #ifdef HAVE_CMMSG1 | ||||
1200 | msg.msg_control = (void *) data; | ||||
1201 | msg.msg_controllen = sizeof(data); | ||||
1202 | #else | ||||
1203 | msg.msg_accrights = (void*) &sock; | ||||
1204 | msg.msg_accrightslen = sizeof(sock); | ||||
1205 | #endif | ||||
1206 | n = recvmsg(binderResponse[0], &msg, 0); | ||||
1207 | if (n != RESPONSE_SIZE(sizeof(int))) { | ||||
1208 | Ns_Log(Error, "Ns_SockBinderListen: recvmsg() failed: recv %" PRIdz"zd" " bytes, '%s'", | ||||
1209 | n, strerror(errno(*__errno_location ()))); | ||||
1210 | return -1; | ||||
1211 | } | ||||
1212 | |||||
1213 | #ifdef HAVE_CMMSG1 | ||||
1214 | { | ||||
1215 | struct cmsghdr *c = CMSG_FIRSTHDR(&msg)((size_t) (&msg)->msg_controllen >= sizeof (struct cmsghdr ) ? (struct cmsghdr *) (&msg)->msg_control : (struct cmsghdr *) 0); | ||||
1216 | if ((c != NULL((void*)0)) && c->cmsg_type == SCM_RIGHTSSCM_RIGHTS) { | ||||
1217 | int *ptr; | ||||
1218 | /* | ||||
1219 | * Use memcpy to avoid alignment problems. | ||||
1220 | */ | ||||
1221 | memcpy(&ptr, CMSG_DATA(c)((c)->__cmsg_data), sizeof(int*)); | ||||
1222 | sock = *ptr; | ||||
1223 | } | ||||
1224 | } | ||||
1225 | #endif | ||||
1226 | |||||
1227 | /* | ||||
1228 | * Close-on-exec, while set in the binder process by default | ||||
1229 | * with Ns_SockBind, is not transmitted in the sendmsg and | ||||
1230 | * must be set again. | ||||
1231 | */ | ||||
1232 | |||||
1233 | if (sock != NS_INVALID_SOCKET(-1) && Ns_CloseOnExec(sock) != NS_OK) { | ||||
1234 | (void)ns_sockcloseclose(sock); | ||||
1235 | sock = NS_INVALID_SOCKET(-1); | ||||
1236 | } | ||||
1237 | if (err == 0) { | ||||
1238 | Ns_Log(Notice, "Ns_SockBinderListen: listen(%s,%hu) = %d", | ||||
1239 | address, port, sock); | ||||
1240 | } else { | ||||
1241 | Ns_SetSockErrno(err); | ||||
1242 | sock = NS_INVALID_SOCKET(-1); | ||||
1243 | Ns_Log(Error, "Ns_SockBinderListen: listen(%s,%hu) failed: '%s'", | ||||
1244 | address, port, ns_sockstrerrorstrerror(ns_sockerrno(*__errno_location ()))); | ||||
1245 | } | ||||
1246 | #endif /* _WIN32 */ | ||||
1247 | return sock; | ||||
1248 | } | ||||
1249 | |||||
1250 | |||||
1251 | /* | ||||
1252 | *---------------------------------------------------------------------- | ||||
1253 | * | ||||
1254 | * NsForkBinder -- | ||||
1255 | * | ||||
1256 | * Fork of the bind/listen process. This routine is called | ||||
1257 | * by main() when the server starts as root. | ||||
1258 | * | ||||
1259 | * Results: | ||||
1260 | * None. | ||||
1261 | * | ||||
1262 | * Side effects: | ||||
1263 | * | ||||
1264 | * The binderRunning, binderRequest, binderResponse static | ||||
1265 | * variables are updated. | ||||
1266 | * | ||||
1267 | *---------------------------------------------------------------------- | ||||
1268 | */ | ||||
1269 | |||||
1270 | void | ||||
1271 | NsForkBinder(void) | ||||
1272 | { | ||||
1273 | #ifndef _WIN32 | ||||
1274 | pid_t pid1; | ||||
1275 | int status; | ||||
1276 | |||||
1277 | /* | ||||
1278 | * Create two socket pipes, one for sending the request and one for | ||||
1279 | * receiving the response. | ||||
1280 | */ | ||||
1281 | |||||
1282 | if (ns_sockpair(binderRequest) != 0 || ns_sockpair(binderResponse) != 0) { | ||||
1283 | Ns_Fatal("NsForkBinder: ns_sockpair() failed: '%s'", strerror(errno(*__errno_location ()))); | ||||
1284 | } | ||||
1285 | |||||
1286 | /* | ||||
1287 | * Double-fork and run as a binder until the socket pairs are | ||||
1288 | * closed. The server double forks to avoid problems waiting for a | ||||
1289 | * child root process after the parent does a setuid(), something | ||||
1290 | * which appears to confuse the process-based Linux and SGI threads. | ||||
1291 | */ | ||||
1292 | |||||
1293 | pid1 = ns_fork(); | ||||
1294 | if (pid1 < 0) { | ||||
1295 | Ns_Fatal("NsForkBinder: fork() failed: '%s'", strerror(errno(*__errno_location ()))); | ||||
1296 | |||||
1297 | } else if (pid1 == 0) { | ||||
1298 | pid_t pid2; | ||||
1299 | |||||
1300 | pid2 = ns_fork(); | ||||
1301 | if (pid2 < 0) { | ||||
1302 | Ns_Fatal("NsForkBinder: fork() failed: '%s'", strerror(errno(*__errno_location ()))); | ||||
1303 | } else if (pid2 == 0) { | ||||
1304 | /* | ||||
1305 | * Grandchild process. | ||||
1306 | */ | ||||
1307 | (void)ns_sockcloseclose(binderRequest[1]); | ||||
1308 | (void)ns_sockcloseclose(binderResponse[0]); | ||||
1309 | Binder(); | ||||
1310 | } else { | ||||
1311 | /* | ||||
1312 | * Child process. | ||||
1313 | */ | ||||
1314 | } | ||||
1315 | exit(0); | ||||
1316 | |||||
1317 | } else { | ||||
1318 | /* | ||||
1319 | * Parent process. | ||||
1320 | */ | ||||
1321 | if (Ns_WaitForProcess(pid1, &status) != NS_OK) { | ||||
1322 | Ns_Fatal("NsForkBinder: Ns_WaitForProcess(%d) failed: '%s'", | ||||
1323 | pid1, strerror(errno(*__errno_location ()))); | ||||
1324 | } else if (status != 0) { | ||||
1325 | Ns_Fatal("NsForkBinder: process %d exited with nonzero status: %d", | ||||
1326 | pid1, status); | ||||
1327 | } | ||||
1328 | binderRunning = NS_TRUE1; | ||||
1329 | } | ||||
1330 | #endif /* _WIN32 */ | ||||
1331 | } | ||||
1332 | |||||
1333 | |||||
1334 | /* | ||||
1335 | *---------------------------------------------------------------------- | ||||
1336 | * | ||||
1337 | * NsStopBinder -- | ||||
1338 | * | ||||
1339 | * Close the socket to the binder after startup. This is done | ||||
1340 | * to avoid a possible security risk of binding to privileged | ||||
1341 | * ports after startup. | ||||
1342 | * | ||||
1343 | * Results: | ||||
1344 | * None. | ||||
1345 | * | ||||
1346 | * Side effects: | ||||
1347 | * Binder process will exit. | ||||
1348 | * | ||||
1349 | *---------------------------------------------------------------------- | ||||
1350 | */ | ||||
1351 | |||||
1352 | void | ||||
1353 | NsStopBinder(void) | ||||
1354 | { | ||||
1355 | if (binderRunning) { | ||||
1356 | (void)ns_sockcloseclose(binderRequest[1]); | ||||
1357 | (void)ns_sockcloseclose(binderResponse[0]); | ||||
1358 | (void)ns_sockcloseclose(binderRequest[0]); | ||||
1359 | (void)ns_sockcloseclose(binderResponse[1]); | ||||
1360 | binderRunning = NS_FALSE0; | ||||
1361 | } | ||||
1362 | } | ||||
1363 | |||||
1364 | |||||
1365 | /* | ||||
1366 | *---------------------------------------------------------------------- | ||||
1367 | * | ||||
1368 | * Binder -- | ||||
1369 | * | ||||
1370 | * Child process bind/listen loop. | ||||
1371 | * | ||||
1372 | * Results: | ||||
1373 | * None. | ||||
1374 | * | ||||
1375 | * Side effects: | ||||
1376 | * Sockets are created and sent to the parent on request. | ||||
1377 | * | ||||
1378 | *---------------------------------------------------------------------- | ||||
1379 | */ | ||||
1380 | |||||
1381 | #ifndef _WIN32 | ||||
1382 | static void | ||||
1383 | Binder(void) | ||||
1384 | { | ||||
1385 | int options, err, sock; | ||||
1386 | unsigned short port; | ||||
1387 | ssize_t n; | ||||
1388 | char type, address[NS_IPADDR_SIZE46]; | ||||
1389 | struct msghdr msg; | ||||
1390 | struct iovec iov[4]; | ||||
1391 | |||||
1392 | #ifdef HAVE_CMMSG1 | ||||
1393 | struct cmsghdr *c; | ||||
1394 | #endif | ||||
1395 | |||||
1396 | Ns_Log(Notice, "binder: started"); | ||||
1397 | Ns_ThreadSetName("binder"); | ||||
1398 | |||||
1399 | /* | ||||
1400 | * Endlessly listen for socket bind requests. | ||||
1401 | */ | ||||
1402 | |||||
1403 | for (;;) { | ||||
1404 | /* | ||||
1405 | * Receive a message with the following contents. | ||||
1406 | */ | ||||
1407 | iov[0].iov_base = (void*) &options; | ||||
1408 | iov[0].iov_len = sizeof(options); | ||||
1409 | iov[1].iov_base = (void*) &port; | ||||
1410 | iov[1].iov_len = sizeof(port); | ||||
1411 | iov[2].iov_base = (void*) &type; | ||||
1412 | iov[2].iov_len = sizeof(type); | ||||
1413 | iov[3].iov_base = (void*) address; | ||||
1414 | iov[3].iov_len = sizeof(address); | ||||
1415 | memset(&msg, 0, sizeof(msg)); | ||||
1416 | msg.msg_iov = iov; | ||||
1417 | msg.msg_iovlen = 4; | ||||
1418 | options = 0; | ||||
1419 | port = 0u; | ||||
1420 | type = '\0'; | ||||
1421 | err = 0; | ||||
1422 | do { | ||||
1423 | n = recvmsg(binderRequest[0], &msg, 0); | ||||
1424 | } while (n == -1 && errno(*__errno_location ()) == NS_EINTR4); | ||||
1425 | if (n == 0) { | ||||
1426 | break; | ||||
1427 | } | ||||
1428 | if (n != REQUEST_SIZE(sizeof(int) + sizeof(int) + sizeof(int) + 46)) { | ||||
1429 | Ns_Fatal("binder: recvmsg() failed: recv %" PRIdz"zd" " bytes, '%s'", n, strerror(errno(*__errno_location ()))); | ||||
1430 | } | ||||
1431 | |||||
1432 | /* | ||||
1433 | * NB: Due to a bug in Solaris the child process must | ||||
1434 | * call both bind() and listen() before returning the | ||||
1435 | * socket. All other Unix versions would actually allow | ||||
1436 | * just performing the bind() in the child and allowing | ||||
1437 | * the parent to perform the listen(). | ||||
1438 | */ | ||||
1439 | switch (type) { | ||||
1440 | case 'U': | ||||
1441 | sock = Ns_SockListenUdp(address, port, NS_FALSE0); | ||||
1442 | break; | ||||
1443 | case 'D': | ||||
1444 | sock = Ns_SockListenUnix(address, options, port); | ||||
1445 | break; | ||||
1446 | case 'R': | ||||
1447 | sock = Ns_SockListenRaw(options); | ||||
1448 | break; | ||||
1449 | case 'T': | ||||
1450 | default: | ||||
1451 | sock = Ns_SockListenEx(address, port, options, NS_FALSE0); | ||||
1452 | } | ||||
1453 | Ns_Log(Notice, "bind type %c addr %s port %d options %d to socket %d", | ||||
1454 | type, address, port, options, sock); | ||||
1455 | |||||
1456 | if (sock < 0) { | ||||
1457 | err = errno(*__errno_location ()); | ||||
1458 | } | ||||
1459 | |||||
1460 | iov[0].iov_base = (void*) &err; | ||||
1461 | iov[0].iov_len = sizeof(err); | ||||
1462 | memset(&msg, 0, sizeof(msg)); | ||||
1463 | msg.msg_iov = iov; | ||||
1464 | msg.msg_iovlen = 1; | ||||
1465 | |||||
1466 | if (sock != -1) { | ||||
1467 | #ifdef HAVE_CMMSG1 | ||||
1468 | int *pfd; | ||||
1469 | |||||
1470 | msg.msg_control = address; | ||||
1471 | msg.msg_controllen = sizeof(address); | ||||
1472 | c = CMSG_FIRSTHDR(&msg)((size_t) (&msg)->msg_controllen >= sizeof (struct cmsghdr ) ? (struct cmsghdr *) (&msg)->msg_control : (struct cmsghdr *) 0); | ||||
1473 | c->cmsg_level = SOL_SOCKET1; | ||||
1474 | c->cmsg_type = SCM_RIGHTSSCM_RIGHTS; | ||||
1475 | /* | ||||
1476 | * Use memcpy to avoid alignment problems. | ||||
1477 | */ | ||||
1478 | memcpy(&pfd, CMSG_DATA(c)((c)->__cmsg_data), sizeof(int*)); | ||||
1479 | |||||
1480 | *pfd = sock; | ||||
1481 | c->cmsg_len = CMSG_LEN(sizeof(int))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t ) ~(sizeof (size_t) - 1)) + (sizeof(int))); | ||||
1482 | msg.msg_controllen = c->cmsg_len; | ||||
1483 | #else | ||||
1484 | msg.msg_accrights = (void*) &sock; | ||||
1485 | msg.msg_accrightslen = sizeof(sock); | ||||
1486 | #endif | ||||
1487 | } | ||||
1488 | |||||
1489 | do { | ||||
1490 | n = sendmsg(binderResponse[1], &msg, 0); | ||||
1491 | } while (n == -1 && errno(*__errno_location ()) == NS_EINTR4); | ||||
1492 | if (n != RESPONSE_SIZE(sizeof(int))) { | ||||
1493 | Ns_Fatal("binder: sendmsg() failed: sent %" PRIdz"zd" " bytes, '%s'", n, strerror(errno(*__errno_location ()))); | ||||
1494 | } | ||||
1495 | if (sock != -1) { | ||||
1496 | /* | ||||
1497 | * Close the socket as it won't be needed in the child. | ||||
1498 | */ | ||||
1499 | (void)ns_sockcloseclose(sock); | ||||
1500 | } | ||||
1501 | } | ||||
1502 | Ns_Log(Notice, "binder: stopped"); | ||||
1503 | } | ||||
1504 | #endif /* _WIN32 */ | ||||
1505 | |||||
1506 | /* | ||||
1507 | * Local Variables: | ||||
1508 | * mode: c | ||||
1509 | * c-basic-offset: 4 | ||||
1510 | * fill-column: 72 | ||||
1511 | * indent-tabs-mode: nil | ||||
1512 | * End: | ||||
1513 | */ |