Bug Summary

File:d/binder.c
Warning:line 220, column 21
Access out-of-bound array element (buffer overflow)

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name binder.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c binder.c
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
46typedef 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
57static Ns_Mutex lock = NULL((void*)0);
58static Tcl_HashTable preboundTcp;
59static Tcl_HashTable preboundUdp;
60static Tcl_HashTable preboundRaw;
61static Tcl_HashTable preboundUnix;
62
63static bool_Bool binderRunning = NS_FALSE0;
64static NS_SOCKETint binderRequest[2] = { NS_INVALID_SOCKET(-1), NS_INVALID_SOCKET(-1) };
65static 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
71static Ns_ReturnCode PrebindSockets(const char *spec)
72 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
73
74static void Binder(void);
75
76static 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
79static 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
82static 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 */
106static struct Prebind*
107PrebindAlloc(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 < reuses; 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 */
175static bool_Bool
176PrebindGet(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') {
25
Taking false branch
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)) {
26
Assuming 'hPtr' is not equal to NULL
27
Taking true branch
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++) {
28
Assuming 'i' is < field 'count'
29
Loop condition is true. Entering loop body
206 /*
207 * Find an entry, which is usable
208 */
209 if (pPtr->sockets[i] != NS_INVALID_SOCKET(-1)) {
30
Assuming the condition is true
31
Taking true branch
210 *sockPtr = pPtr->sockets[i];
211 pPtr->sockets[i] = NS_INVALID_SOCKET(-1);
212 break;
213 }
214 }
215 if (*sockPtr != NS_INVALID_SOCKET(-1)) {
32
Taking true branch
216 /*
217 * Check, if there are more unconsumed entries.
218 */
219 for (; i
32.1
'i' is < field 'count'
< pPtr->count
; i++) {
33
Loop condition is true. Entering loop body
35
Assuming 'i' is < field 'count'
36
Loop condition is true. Entering loop body
220 if (pPtr->sockets[i] != NS_INVALID_SOCKET(-1)) {
34
Taking false branch
37
Access out-of-bound array element (buffer overflow)
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 */
256static void
257PrebindCloseSockets(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
306NS_SOCKETint
307Ns_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
377NS_SOCKETint
378Ns_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) {
22
Assuming the condition is true
23
Taking true branch
385 bool_Bool found;
386
387#ifndef _WIN32
388 found = PrebindGet("udp", saPtr, &sock);
24
Calling 'PrebindGet'
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
429NS_SOCKETint
430Ns_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
484NS_SOCKETint
485Ns_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
558NS_SOCKETint
559Ns_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
608NS_SOCKETint
609Ns_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
672NS_SOCKETint
673Ns_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
705void
706NsInitBinder(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
734Ns_ReturnCode
735NsPreBind(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
793void
794NsClosePreBound(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
897static Ns_ReturnCode
898PrebindSockets(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
1153NS_SOCKETint
1154Ns_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
1270void
1271NsForkBinder(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) {
1
Assuming the condition is false
2
Assuming the condition is false
3
Taking false branch
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) {
4
Assuming 'pid1' is >= 0
5
Taking false branch
1295 Ns_Fatal("NsForkBinder: fork() failed: '%s'", strerror(errno(*__errno_location ())));
1296
1297 } else if (pid1 == 0) {
6
Assuming 'pid1' is equal to 0
7
Taking true branch
1298 pid_t pid2;
1299
1300 pid2 = ns_fork();
1301 if (pid2 < 0) {
8
Assuming 'pid2' is >= 0
9
Taking false branch
1302 Ns_Fatal("NsForkBinder: fork() failed: '%s'", strerror(errno(*__errno_location ())));
1303 } else if (pid2 == 0) {
10
Assuming 'pid2' is equal to 0
11
Taking true branch
1304 /*
1305 * Grandchild process.
1306 */
1307 (void)ns_sockcloseclose(binderRequest[1]);
1308 (void)ns_sockcloseclose(binderResponse[0]);
1309 Binder();
12
Calling '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
1352void
1353NsStopBinder(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
1382static void
1383Binder(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 (;;) {
13
Loop condition is true. Entering loop body
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 {
15
Loop condition is false. Exiting loop
1423 n = recvmsg(binderRequest[0], &msg, 0);
1424 } while (n == -1 && errno(*__errno_location ()) == NS_EINTR4);
14
Assuming the condition is false
1425 if (n == 0) {
16
Assuming 'n' is not equal to 0
17
Taking false branch
1426 break;
1427 }
1428 if (n != REQUEST_SIZE(sizeof(int) + sizeof(int) + sizeof(int) + 46)) {
18
Assuming the condition is false
19
Taking false branch
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) {
20
Control jumps to 'case 85:' at line 1440
1440 case 'U':
1441 sock = Ns_SockListenUdp(address, port, NS_FALSE0);
21
Calling 'Ns_SockListenUdp'
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 */