3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
9 * 2003, Benjamin Tse (blt@ieee.org) - Winsock port
10 * 2004, F5 Networks, Inc. - IP-address input
11 * 2005, Peter Marschall - error checks, ...
13 * LCDproc sockets code...
30 #include <sys/socket.h>
32 #include <netinet/in.h>
34 #include <arpa/inet.h>
37 #include <sys/types.h>
45 #include "shared/report.h"
46 #include "screenlist.h"
49 /****************************************************************************/
50 static fd_set active_fd_set
, read_fd_set
;
51 static int listening_fd
;
53 /* For efficiency we maintain a list of open sockets. Nodes in this list
54 * are obtained from a pre-allocated pool - this removes heap operations
55 * from the polling loop. A list of open sockets is also required under WINSOCK
56 * as sockets can be arbitrary values instead of low value integers. */
57 static LinkedList
* openSocketList
= NULL
;
58 static LinkedList
* freeClientSocketList
= NULL
;
60 struct ClientSocketMap
66 /* The memory referenced from clientSocketPoolList is obtained from the
67 * clientSocketPool array. */
68 struct ClientSocketMap
* freeClientSocketPool
;
70 /* Length of longest transmission allowed at once...*/
73 /**** Internal function declarations ****************************************/
74 int sock_read_from_client(struct ClientSocketMap
* clientSocketMap
);
77 /****************************************************************************/
79 sock_init(char* bind_addr
, int bind_port
)
84 /* Initialize the Winsock dll */
86 int startup
= WSAStartup(MAKEWORD(2, 2), &wsaData
);
89 report(RPT_ERR
, "%s: Could not start Winsock library - %s",
90 __FUNCTION__
, sock_geterror());
92 /* REVISIT: call WSACleanup(); */
95 debug(RPT_DEBUG
, "%s(bind_addr=\"%s\", port=%d)", __FUNCTION__
, bind_addr
, bind_port
);
97 /* Create the socket and set it up to accept connections. */
98 listening_fd
= sock_create_inet_socket(bind_addr
, bind_port
);
99 if (listening_fd
< 0) {
100 report(RPT_ERR
, "%s: Error creating socket - %s",
101 __FUNCTION__
, sock_geterror());
105 /* Create the socket -> Client mapping pool */
106 /* How large can FD_SETSIZE be? Even if it is ~2000 this only uses a
107 few kilobytes of memory. Let's trade size for speed! */
108 freeClientSocketPool
= (struct ClientSocketMap
*)
109 malloc(sizeof(struct ClientSocketMap
) * FD_SETSIZE
);
110 if (!freeClientSocketPool
)
112 report(RPT_ERR
, "%s: Error allocating memory for client sockets.",
117 freeClientSocketList
= LL_new();
118 for (i
= 0; i
< FD_SETSIZE
; ++i
)
120 LL_AddNode(freeClientSocketList
, (void*) &freeClientSocketPool
[i
]);
123 /* Create and initialize the open socket list with the server socket */
124 openSocketList
= LL_new();
127 report(RPT_ERR
, "%s: Error allocating memory for the open socket "
128 "list.", __FUNCTION__
);
131 struct ClientSocketMap
* entry
;
133 /* LL_DeleteNode removes an entry from the list and returns it */
134 entry
= (struct ClientSocketMap
*) LL_Pop(freeClientSocketList
);
135 entry
->socket
= listening_fd
;
136 entry
->client
= NULL
;
137 LL_AddNode(openSocketList
, (void*) entry
);
144 This code gets the send and receive buffer sizes.
150 getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, &len);
151 debug(RPT_DEBUG, "SEND buffer: %i bytes", val);
154 getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, &len);
155 debug(RPT_DEBUG, "RECV buffer: %i bytes", val);
165 debug(RPT_DEBUG
, "%s()", __FUNCTION__
);
167 /*struct ClientSocketMap* clientIt;*/
169 /* delete all clients */
170 /* This should be done by calling clients_shutdown */
172 LL_Rewind(openSocketList);
173 for (clientIt = (struct ClientSocketMap*) LL_Get(openSocketList);
175 clientIt = LL_GetNext(openSocketList))
177 if (clientIt->client)
180 /* destroying a client also closes its socket */
181 /* client_destroy(clientIt->client);
184 LL_Destroy(openSocketList);
187 LL_Destroy(freeClientSocketList
);
188 free(freeClientSocketPool
);
191 if (WSACleanup() != 0)
193 report(RPT_ERR
, "%s: Error closing Winsock library - %s",
194 __FUNCTION__
, sock_geterror());
203 /****************************************************************************/
204 /* Creates a socket in internet space */
206 sock_create_inet_socket(char *addr
, unsigned int port
)
208 struct sockaddr_in name
;
211 debug(RPT_DEBUG
, "%s(addr=\"%s\", port=%i)", __FUNCTION__
, addr
, port
);
213 /* Create the socket. */
214 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
216 if (sock
== INVALID_SOCKET
)
221 report(RPT_ERR
, "%s: Could not create socket - %s",
222 __FUNCTION__
, sock_geterror());
225 /* Set the socket so we can re-use it*/
226 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void*)&sockopt
, sizeof(sockopt
)) < 0) {
227 report(RPT_ERR
, "%s: Error setting socket option SO_REUSEADDR - %s",
228 __FUNCTION__
, sock_geterror());
232 /* Give the socket a name. */
233 memset(&name
, 0, sizeof(name
));
234 name
.sin_family
= AF_INET
;
235 name
.sin_port
= htons(port
);
237 /* REVISIT: can probably use the same code as under winsock */
238 inet_aton(addr
, &name
.sin_addr
);
240 name
.sin_addr
.S_un
.S_addr
= inet_addr(addr
);
243 if (bind(sock
, (struct sockaddr
*) &name
, sizeof(name
)) < 0)
245 report(RPT_ERR
, "%s: Could not bind to port %d at address %s - %s",
246 __FUNCTION__
, port
, addr
, sock_geterror());
249 report(RPT_NOTICE
, "Listening for queries on %s:%d", addr
, port
);
252 if (listen(sock
, 1) < 0) {
253 report(RPT_ERR
, "%s: error in attempting to listen to port "
255 __FUNCTION__
, port
, addr
, sock_geterror());
259 /* Initialize the set of active sockets. */
260 FD_ZERO(&active_fd_set
);
261 FD_SET(sock
, &active_fd_set
);
267 /* Service all clients with input pending...*/
269 sock_poll_clients(void)
272 struct sockaddr_in clientname
;
274 struct ClientSocketMap
* clientSocket
;
276 debug(RPT_DEBUG
, "%s()", __FUNCTION__
);
281 /* Block until input arrives on one or more active sockets. */
282 read_fd_set
= active_fd_set
;
284 if (select(FD_SETSIZE
, &read_fd_set
, NULL
, NULL
, &t
) < 0) {
285 report(RPT_ERR
, "%s: Select error - %s",
286 __FUNCTION__
, sock_geterror());
290 /* Service all the sockets with input pending. */
291 LL_Rewind(openSocketList
);
292 for (clientSocket
= (struct ClientSocketMap
*) LL_Get(openSocketList
);
294 clientSocket
= LL_GetNext(openSocketList
))
296 if (FD_ISSET(clientSocket
->socket
, &read_fd_set
))
298 if (clientSocket
->socket
== listening_fd
)
300 /* Connection request on original socket. */
303 socklen_t size
= sizeof(clientname
);
304 new_sock
= accept(listening_fd
, (struct sockaddr
*) &clientname
, &size
);
306 if (new_sock
== INVALID_SOCKET
)
311 report(RPT_ERR
, "%s: Accept error - %s",
312 __FUNCTION__
, sock_geterror());
315 report(RPT_NOTICE
, "Connect from host %s:%hu on socket %i",
316 inet_ntoa(clientname
.sin_addr
), ntohs(clientname
.sin_port
), new_sock
);
317 FD_SET(new_sock
, &active_fd_set
);
322 ioctlsocket(new_sock
, FIONBIO
, &tmp
);
325 fcntl(new_sock
, F_SETFL
, O_NONBLOCK
);
328 /* Create new client */
329 if ((c
= client_create(new_sock
)) == NULL
) {
330 report(RPT_ERR
, "%s: Error creating client on socket %i - %s",
331 __FUNCTION__
, clientSocket
->socket
, sock_geterror());
335 struct ClientSocketMap
* newClientSocket
;
336 newClientSocket
= (struct ClientSocketMap
*) LL_Pop(freeClientSocketList
);
339 newClientSocket
->socket
= new_sock
;
340 newClientSocket
->client
= c
;
341 LL_InsertNode(openSocketList
, (void*) newClientSocket
);
342 /* advance past the new node - check it on the next pass */
343 LL_Next(openSocketList
);
345 report(RPT_ERR
, "%s: Error - free client socket list exhausted - %d clients.",
346 __FUNCTION__
, FD_SETSIZE
);
350 if (clients_add_client(c
) != 0) {
351 report(RPT_ERR
, "%s: Could not add client on socket %i", __FUNCTION__
, clientSocket
->socket
);
355 /* Data arriving on an already-connected socket. */
358 debug(RPT_DEBUG
, "%s: reading...", __FUNCTION__
);
359 err
= sock_read_from_client(clientSocket
);
360 debug(RPT_DEBUG
, "%s: ...done", __FUNCTION__
);
363 /* Client disconnected, destroy client data */
364 /* c = clients_find_client_by_sock(); - Deprecated by clientsocketmap*/
365 if (clientSocket
->client
)
367 struct ClientSocketMap
* entry
;
369 /*sock_send_string(i, "bye\n");*/
370 report(RPT_NOTICE
, "Client on socket %i disconnected",
371 clientSocket
->socket
);
372 client_destroy(clientSocket
->client
);
373 clients_remove_client(clientSocket
->client
);
374 FD_CLR(clientSocket
->socket
, &active_fd_set
);
375 close(clientSocket
->socket
);
377 entry
= (struct ClientSocketMap
*) LL_DeleteNode(openSocketList
);
378 LL_Push(freeClientSocketList
, (void*) entry
);
380 report(RPT_ERR
, "%s: Can't find client of socket %i",
381 __FUNCTION__
, clientSocket
->socket
);
392 sock_read_from_client(struct ClientSocketMap
* clientSocketMap
)
397 debug(RPT_DEBUG
, "%s()", __FUNCTION__
);
400 nbytes
= sock_recv(clientSocketMap
->socket
, buffer
, MAXMSG
);
404 report(RPT_DEBUG
, "%s: Error on socket %d - %s",
405 __FUNCTION__
, clientSocketMap
->socket
, sock_geterror());
407 } else if (nbytes
== 0) /* EOF*/
410 } else if (nbytes
> (MAXMSG
- (MAXMSG
/ 8))) /* Very noisy client...*/
412 sock_send_error(clientSocketMap
->socket
, "Too much data received... quiet down!\n");
414 } else /* Data Read*/
417 /* Now, replace zeros with linefeeds...*/
418 for (i
= 0; i
< nbytes
; i
++)
421 /* Enqueue a "client message" here...*/
422 /* c = clients_find_client_by_sock(filedes); - Deprecated by clientsocketmap*/
423 if (clientSocketMap
->client
) {
424 client_add_message(clientSocketMap
->client
, buffer
);
426 report(RPT_DEBUG
, "%s: Can't find client %d",
427 __FUNCTION__
, clientSocketMap
->socket
);
430 report(RPT_DEBUG
, "%s: got message from client %d: \"%s\"",
431 __FUNCTION__
, clientSocketMap
->socket
, buffer
);
438 /* return 1 if addr is valid IPv4 */
439 int verify_ipv4(const char *addr
)
446 /* inet_pton returns positive value if it worked */
447 result
= inet_pton(AF_INET
, addr
, &a
);
449 return (result
> 0) ? 1 : 0;
452 /* return 1 if addr is valid IPv6 */
453 int verify_ipv6(const char *addr
)
460 /* inet_pton returns positive value if it worked */
461 result
= inet_pton(AF_INET6
, addr
, &a
);
463 return (result
> 0) ? 1 : 0;