2 #define CONFIG "config.h"
18 #include <netinet/in.h>
25 #include "shared_globals.h"
30 typedef ssize_t (*sendrecv_t
)(int, void*, size_t, int);
32 typedef int (WINAPI
*sendrecv_t
)(SOCKET
, void*, int, int);
36 // Send or receive a fixed number of bytes regardless if received in one or more chunks
37 int_fast8_t sendrecv(SOCKET sock
, BYTE
*data
, int len
, int_fast8_t do_send
)
40 sendrecv_t f
= do_send
46 n
= f(sock
, data
, len
, 0);
49 ( n
< 0 && socket_errno
== VLMCSD_EINTR
) || ( n
> 0 && ( data
+= n
, (len
-= n
) > 0 ) ));
55 static int_fast8_t ip2str(char *restrict result
, const size_t resultLength
, const struct sockaddr
*const restrict socketAddress
, const socklen_t socketLength
)
57 static const char *const fIPv4
= "%s:%s";
58 static const char *const fIPv6
= "[%s]:%s";
59 char ipAddress
[64], portNumber
[8];
69 NI_NUMERICHOST
| NI_NUMERICSERV
75 if ((unsigned int)snprintf(result
, resultLength
, socketAddress
->sa_family
== AF_INET6
? fIPv6
: fIPv4
, ipAddress
, portNumber
) > resultLength
) return FALSE
;
80 static int_fast8_t getSocketList(struct addrinfo
**saList
, const char *const addr
, const int flags
, const int AddressFamily
)
83 char *szHost
, *szPort
;
84 size_t len
= strlen(addr
) + 1;
86 // Don't alloca too much
87 if (len
> 264) return FALSE
;
89 char *addrcopy
= (char*)alloca(len
);
90 memcpy(addrcopy
, addr
, len
);
92 parseAddress(addrcopy
, &szHost
, &szPort
);
94 struct addrinfo hints
;
96 memset(&hints
, 0, sizeof(struct addrinfo
));
98 hints
.ai_family
= AddressFamily
;
99 hints
.ai_socktype
= SOCK_STREAM
;
100 hints
.ai_protocol
= IPPROTO_TCP
;
101 hints
.ai_flags
= flags
;
103 if ((status
= getaddrinfo(szHost
, szPort
, &hints
, saList
)))
105 printerrorf("Warning: %s: %s\n", addr
, gai_strerror(status
));
113 static int_fast8_t setBlockingEnabled(SOCKET fd
, int_fast8_t blocking
)
115 if (fd
== INVALID_SOCKET
) return FALSE
;
119 unsigned long mode
= blocking
? 0 : 1;
120 return (ioctlsocket(fd
, FIONBIO
, &mode
) == 0) ? TRUE
: FALSE
;
124 int flags
= fcntl(fd
, F_GETFL
, 0);
126 if (flags
< 0) return FALSE
;
128 flags
= blocking
? (flags
& ~O_NONBLOCK
) : (flags
| O_NONBLOCK
);
129 return (fcntl(fd
, F_SETFL
, flags
) == 0) ? TRUE
: FALSE
;
135 int_fast8_t isDisconnected(const SOCKET s
)
139 if (!setBlockingEnabled(s
, FALSE
)) return TRUE
;
141 int n
= recv(s
, buffer
, 1, MSG_PEEK
);
143 if (!setBlockingEnabled(s
, TRUE
)) return TRUE
;
144 if (n
== 0) return TRUE
;
150 // Connect to TCP address addr (e.g. "kms.example.com:1688") and return an
151 // open socket for the connection if successful or INVALID_SOCKET otherwise
152 SOCKET
connectToAddress(const char *const addr
, const int AddressFamily
, int_fast8_t showHostName
)
154 struct addrinfo
*saList
, *sa
;
155 SOCKET s
= INVALID_SOCKET
;
158 if (!getSocketList(&saList
, addr
, 0, AddressFamily
)) return INVALID_SOCKET
;
160 for (sa
= saList
; sa
; sa
= sa
->ai_next
)
162 // struct sockaddr_in* addr4 = (struct sockaddr_in*)sa->ai_addr;
163 // struct sockaddr_in6* addr6 = (struct sockaddr_in6*)sa->ai_addr;
165 if (ip2str(szAddr
, sizeof(szAddr
), sa
->ai_addr
, sa
->ai_addrlen
))
168 printf("Connecting to %s (%s) ... ", addr
, szAddr
);
170 printf("Connecting to %s ... ", szAddr
);
175 s
= socket(sa
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
);
177 # if !defined(NO_TIMEOUT) && !__minix__
178 # ifndef _WIN32 // Standard Posix timeout structure
184 # else // Windows requires a DWORD with milliseconds
190 setsockopt(s
, SOL_SOCKET
, SO_RCVTIMEO
, (sockopt_t
)&to
, sizeof(to
));
191 setsockopt(s
, SOL_SOCKET
, SO_SNDTIMEO
, (sockopt_t
)&to
, sizeof(to
));
192 # endif // !defined(NO_TIMEOUT) && !__minix__
194 if (!connect(s
, sa
->ai_addr
, sa
->ai_addrlen
))
196 printf("successful\n");
200 errorout("%s\n", socket_errno
== VLMCSD_EINPROGRESS
? "Timed out" : vlmcsd_strerror(socket_errno
));
206 freeaddrinfo(saList
);
213 // Create a Listening socket for addrinfo sa and return socket s
214 // szHost and szPort are for logging only
215 static int listenOnAddress(const struct addrinfo
*const ai
, SOCKET
*s
)
220 ip2str(ipstr
, sizeof(ipstr
), ai
->ai_addr
, ai
->ai_addrlen
);
222 //*s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
223 *s
= socket(ai
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
);
225 if (*s
== INVALID_SOCKET
)
227 error
= socket_errno
;
228 printerrorf("Warning: %s error. %s\n", ai
->ai_family
== AF_INET6
? cIPv6
: cIPv4
, vlmcsd_strerror(error
));
232 # if !defined(_WIN32) && !defined(NO_SIGHUP)
234 int flags
= fcntl(*s
, F_GETFD
, 0);
239 fcntl(*s
, F_SETFD
, flags
);
244 printerrorf("Warning: Could not set FD_CLOEXEC flag on %s: %s\n", ipstr
, vlmcsd_strerror(errno
));
248 # endif // !defined(_WIN32) && !defined(NO_SIGHUP)
250 BOOL socketOption
= TRUE
;
252 // fix for lame tomato toolchain
255 # define IPV6_V6ONLY (26)
257 # endif // IPV6_V6ONLY
260 if (ai
->ai_family
== AF_INET6
) setsockopt(*s
, IPPROTO_IPV6
, IPV6_V6ONLY
, (sockopt_t
)&socketOption
, sizeof(socketOption
));
264 setsockopt(*s
, SOL_SOCKET
, SO_REUSEADDR
, (sockopt_t
)&socketOption
, sizeof(socketOption
));
267 if (bind(*s
, ai
->ai_addr
, ai
->ai_addrlen
) || listen(*s
, SOMAXCONN
))
269 error
= socket_errno
;
270 printerrorf("Warning: %s: %s\n", ipstr
, vlmcsd_strerror(error
));
276 logger("Listening on %s\n", ipstr
);
283 // Adds a listening socket for an address string,
284 // e.g. 127.0.0.1:1688 or [2001:db8:dead:beef::1]:1688
285 BOOL
addListeningSocket(const char *const addr
)
287 struct addrinfo
*aiList
, *ai
;
289 SOCKET
*s
= SocketList
+ numsockets
;
291 if (getSocketList(&aiList
, addr
, AI_PASSIVE
| AI_NUMERICHOST
, AF_UNSPEC
))
293 for (ai
= aiList
; ai
; ai
= ai
->ai_next
)
295 // struct sockaddr_in* addr4 = (struct sockaddr_in*)sa->ai_addr;
296 // struct sockaddr_in6* addr6 = (struct sockaddr_in6*)sa->ai_addr;
298 if (numsockets
>= FD_SETSIZE
)
300 #ifdef _PEDANTIC // Do not report this error in normal builds to keep file size low
301 printerrorf("Warning: Cannot listen on %s. Your OS only supports %u listening sockets in an FD_SET.\n", addr
, FD_SETSIZE
);
306 if (!listenOnAddress(ai
, s
))
313 freeaddrinfo(aiList
);
319 // Just create some dummy sockets to see if we have a specific protocol (IPv4 or IPv6)
320 __pure
int_fast8_t checkProtocolStack(const int addressfamily
)
322 SOCKET s
; // = INVALID_SOCKET;
324 s
= socket(addressfamily
, SOCK_STREAM
, 0);
325 int_fast8_t success
= (s
!= INVALID_SOCKET
);
332 // Build an fd_set of all listening socket then use select to wait for an incoming connection
333 static SOCKET
network_accept_any()
335 fd_set ListeningSocketsList
;
336 SOCKET maxSocket
, sock
;
340 FD_ZERO(&ListeningSocketsList
);
343 for (i
= 0; i
< numsockets
; i
++)
345 FD_SET(SocketList
[i
], &ListeningSocketsList
);
346 if (SocketList
[i
] > maxSocket
) maxSocket
= SocketList
[i
];
349 status
= select(maxSocket
+ 1, &ListeningSocketsList
, NULL
, NULL
, NULL
);
351 if (status
< 0) return INVALID_SOCKET
;
353 sock
= INVALID_SOCKET
;
355 for (i
= 0; i
< numsockets
; i
++)
357 if (FD_ISSET(SocketList
[i
], &ListeningSocketsList
))
359 sock
= SocketList
[i
];
364 if (sock
== INVALID_SOCKET
)
365 return INVALID_SOCKET
;
367 return accept(sock
, NULL
, NULL
);
371 void closeAllListeningSockets()
375 for (i
= 0; i
< numsockets
; i
++)
377 shutdown(SocketList
[i
], VLMCSD_SHUT_RDWR
);
378 socketclose(SocketList
[i
]);
384 static void serveClient(const SOCKET s_client
, const DWORD RpcAssocGroup
)
386 # if !defined(NO_TIMEOUT) && !__minix__
388 # ifndef _WIN32 // Standard Posix timeout structure
391 to
.tv_sec
= ServerTimeout
;
394 #else // Windows requires a DWORD with milliseconds
396 DWORD to
= ServerTimeout
* 1000;
400 # if !defined(NO_LOG) && defined(_PEDANTIC)
403 setsockopt(s_client
, SOL_SOCKET
, SO_RCVTIMEO
, (sockopt_t
)&to
, sizeof(to
)) ||
404 setsockopt(s_client
, SOL_SOCKET
, SO_SNDTIMEO
, (sockopt_t
)&to
, sizeof(to
));
406 if (result
) logger("Warning: Set timeout failed: %s\n", vlmcsd_strerror(socket_errno
));
408 # else // !(!defined(NO_LOG) && defined(_PEDANTIC))
410 setsockopt(s_client
, SOL_SOCKET
, SO_RCVTIMEO
, (sockopt_t
)&to
, sizeof(to
));
411 setsockopt(s_client
, SOL_SOCKET
, SO_SNDTIMEO
, (sockopt_t
)&to
, sizeof(to
));
413 # endif // !(!defined(NO_LOG) && defined(_PEDANTIC))
415 # endif // !defined(NO_TIMEOUT) && !__minix__
419 struct sockaddr_storage addr
;
423 if (getpeername(s_client
, (struct sockaddr
*)&addr
, &len
) ||
424 !ip2str(ipstr
, sizeof(ipstr
), (struct sockaddr
*)&addr
, len
))
426 # if !defined(NO_LOG) && defined(_PEDANTIC)
427 logger("Fatal: Cannot determine client's IP address: %s\n", vlmcsd_strerror(errno
));
428 # endif // !defined(NO_LOG) && defined(_PEDANTIC)
429 socketclose(s_client
);
435 const char *const connection_type
= addr
.ss_family
== AF_INET6
? cIPv6
: cIPv4
;
436 static const char *const cAccepted
= "accepted";
437 static const char *const cClosed
= "closed";
438 static const char *const fIP
= "%s connection %s: %s.\n";
440 logger(fIP
, connection_type
, cAccepted
, ipstr
);
443 rpcServer(s_client
, RpcAssocGroup
, ipstr
);
446 logger(fIP
, connection_type
, cClosed
, ipstr
);
449 socketclose(s_client
);
454 static void post_sem(void)
456 #if !defined(NO_LIMIT) && !__minix__
457 if (!InetdMode
&& MaxTasks
!= SEM_VALUE_MAX
)
459 semaphore_post(Semaphore
);
461 #endif // !defined(NO_LIMIT) && !__minix__
465 static void wait_sem(void)
467 #if !defined(NO_LIMIT) && !__minix__
468 if (!InetdMode
&& MaxTasks
!= SEM_VALUE_MAX
)
470 semaphore_wait(Semaphore
);
472 #endif // !defined(NO_LIMIT) && !__minix__
476 #if defined(USE_THREADS) && !defined(NO_SOCKETS)
478 #if defined(_WIN32) || defined(__CYGWIN__) // Win32 Threads
479 static DWORD WINAPI
serveClientThreadProc(PCLDATA clData
)
480 #else // Posix threads
481 static void *serveClientThreadProc (PCLDATA clData
)
482 #endif // Thread proc is identical in WIN32 and Posix threads
484 serveClient(clData
->socket
, clData
->RpcAssocGroup
);
491 #endif // USE_THREADS
496 #if defined(USE_THREADS) && (defined(_WIN32) || defined(__CYGWIN__)) // Windows Threads
497 static int serveClientAsyncWinThreads(const PCLDATA thr_CLData
)
501 HANDLE h
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)serveClientThreadProc
, thr_CLData
, 0, NULL
);
507 socketclose(thr_CLData
->socket
);
510 return GetLastError();
515 #endif // defined(USE_THREADS) && defined(_WIN32) // Windows Threads
518 #if defined(USE_THREADS) && !defined(_WIN32) && !defined(__CYGWIN__) // Posix Threads
519 static int ServeClientAsyncPosixThreads(const PCLDATA thr_CLData
)
526 // Must set detached state to avoid memory leak
527 if (pthread_attr_init(&attr
) ||
528 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
) ||
529 pthread_create(&p_thr
, &attr
, (void * (*)(void *))serveClientThreadProc
, thr_CLData
))
531 socketclose(thr_CLData
->socket
);
539 #endif // defined(USE_THREADS) && !defined(_WIN32) // Posix Threads
541 #ifndef USE_THREADS // fork() implementation
542 static void ChildSignalHandler(const int signal
)
544 if (signal
== SIGHUP
) return;
549 logger("Warning: Child killed/crashed by %s\n", strsignal(signal
));
555 static int ServeClientAsyncFork(const SOCKET s_client
, const DWORD RpcAssocGroup
)
560 if ((pid
= fork()) < 0)
567 socketclose(s_client
);
574 // Setup a Child Handler for most common termination signals
578 sa
.sa_handler
= ChildSignalHandler
;
580 static int signallist
[] = { SIGHUP
, SIGINT
, SIGTERM
, SIGSEGV
, SIGILL
, SIGFPE
, SIGBUS
};
582 if (!sigemptyset(&sa
.sa_mask
))
586 for (i
= 0; i
< _countof(signallist
); i
++)
588 sigaction(signallist
[i
], &sa
, NULL
);
592 serveClient(s_client
, RpcAssocGroup
);
600 int serveClientAsync(const SOCKET s_client
, const DWORD RpcAssocGroup
)
602 #ifndef USE_THREADS // fork() implementation
604 return ServeClientAsyncFork(s_client
, RpcAssocGroup
);
606 #else // threads implementation
608 PCLDATA thr_CLData
= (PCLDATA
)vlmcsd_malloc(sizeof(CLDATA
));
609 thr_CLData
->socket
= s_client
;
610 thr_CLData
->RpcAssocGroup
= RpcAssocGroup
;
612 #if defined(_WIN32) || defined (__CYGWIN__) // Windows threads
614 return serveClientAsyncWinThreads(thr_CLData
);
616 #else // Posix Threads
618 return ServeClientAsyncPosixThreads(thr_CLData
);
620 #endif // Posix Threads
622 #endif // USE_THREADS
630 DWORD RpcAssocGroup
= rand32();
632 // If compiled for inetd-only mode just serve the stdin socket
634 serveClient(STDIN_FILENO
, RpcAssocGroup
);
637 // In inetd mode just handle the stdin socket
640 serveClient(STDIN_FILENO
, RpcAssocGroup
);
650 if ( (s_client
= network_accept_any()) == INVALID_SOCKET
)
652 error
= socket_errno
;
654 if (error
== VLMCSD_EINTR
|| error
== VLMCSD_ECONNABORTED
) continue;
657 if (ServiceShutdown
) return 0;
661 logger("Fatal: %s\n",vlmcsd_strerror(error
));
668 serveClientAsync(s_client
, RpcAssocGroup
);