Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / sock.c
blob47a15fdf8c82fbd9ff38eebece728fcac865b2c6
1 /*
2 * sock.c
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...
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <unistd.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #ifdef WINSOCK2
28 #include <winsock2.h>
29 #else
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <arpa/inet.h>
35 #endif /* WINSOCK */
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <fcntl.h>
39 #include <string.h>
41 #include "sock.h"
42 #include "client.h"
43 #include "clients.h"
44 #include "screen.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
62 int socket;
63 Client* client;
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...*/
71 #define MAXMSG 8192
73 /**** Internal function declarations ****************************************/
74 int sock_read_from_client(struct ClientSocketMap* clientSocketMap);
77 /****************************************************************************/
78 int
79 sock_init(char* bind_addr, int bind_port)
81 int i;
83 #ifdef WINSOCK2
84 /* Initialize the Winsock dll */
85 WSADATA wsaData;
86 int startup = WSAStartup(MAKEWORD(2, 2), &wsaData);
87 if (startup != 0)
89 report(RPT_ERR, "%s: Could not start Winsock library - %s",
90 __FUNCTION__, sock_geterror());
92 /* REVISIT: call WSACleanup(); */
93 #endif
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());
102 return -1;
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.",
113 __FUNCTION__);
114 return -1;
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();
125 if (!openSocketList)
127 report(RPT_ERR, "%s: Error allocating memory for the open socket "
128 "list.", __FUNCTION__);
129 return -1;
130 } else {
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);
140 return 0;
144 This code gets the send and receive buffer sizes.
146 int val, len, sock;
147 sock = new;
149 len = sizeof(int);
150 getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, &len);
151 debug(RPT_DEBUG, "SEND buffer: %i bytes", val);
153 len = sizeof(int);
154 getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, &len);
155 debug(RPT_DEBUG, "RECV buffer: %i bytes", val);
161 sock_shutdown(void)
163 int retVal = 0;
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);
174 clientIt;
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);
186 close(listening_fd);
187 LL_Destroy(freeClientSocketList);
188 free(freeClientSocketPool);
190 #ifdef WINSOCK2
191 if (WSACleanup() != 0)
193 report(RPT_ERR, "%s: Error closing Winsock library - %s",
194 __FUNCTION__, sock_geterror());
195 retVal = -1;
197 #endif
199 return retVal;
203 /****************************************************************************/
204 /* Creates a socket in internet space */
206 sock_create_inet_socket(char *addr, unsigned int port)
208 struct sockaddr_in name;
209 int sock, sockopt=1;
211 debug(RPT_DEBUG, "%s(addr=\"%s\", port=%i)", __FUNCTION__, addr, port);
213 /* Create the socket. */
214 sock = socket(PF_INET, SOCK_STREAM, 0);
215 #ifdef WINSOCK2
216 if (sock == INVALID_SOCKET)
217 #else
218 if (sock < 0)
219 #endif
221 report(RPT_ERR, "%s: Could not create socket - %s",
222 __FUNCTION__, sock_geterror());
223 return -1;
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());
229 return -1;
232 /* Give the socket a name. */
233 memset(&name, 0, sizeof(name));
234 name.sin_family = AF_INET;
235 name.sin_port = htons(port);
236 #ifndef WINSOCK2
237 /* REVISIT: can probably use the same code as under winsock */
238 inet_aton(addr, &name.sin_addr);
239 #else
240 name.sin_addr.S_un.S_addr = inet_addr(addr);
241 #endif
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());
247 return -1;
248 } else {
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 "
254 "%d at %s - %s",
255 __FUNCTION__, port, addr, sock_geterror());
256 return -1;
259 /* Initialize the set of active sockets. */
260 FD_ZERO(&active_fd_set);
261 FD_SET(sock, &active_fd_set);
263 return sock;
267 /* Service all clients with input pending...*/
269 sock_poll_clients(void)
271 int err;
272 struct sockaddr_in clientname;
273 struct timeval t;
274 struct ClientSocketMap* clientSocket;
276 debug(RPT_DEBUG, "%s()", __FUNCTION__);
278 t.tv_sec = 0;
279 t.tv_usec = 0;
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());
287 return -1;
290 /* Service all the sockets with input pending. */
291 LL_Rewind(openSocketList);
292 for (clientSocket = (struct ClientSocketMap*) LL_Get(openSocketList);
293 clientSocket;
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. */
301 Client* c;
302 int new_sock;
303 socklen_t size = sizeof(clientname);
304 new_sock = accept(listening_fd, (struct sockaddr *) &clientname, &size);
305 #ifdef WINSOCK2
306 if (new_sock == INVALID_SOCKET)
307 #else
308 if (new_sock < 0)
309 #endif
311 report(RPT_ERR, "%s: Accept error - %s",
312 __FUNCTION__, sock_geterror());
313 return -1;
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);
319 #ifdef WINSOCK2
321 unsigned long tmp;
322 ioctlsocket(new_sock, FIONBIO, &tmp);
324 #else
325 fcntl(new_sock, F_SETFL, O_NONBLOCK);
326 #endif
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());
332 return -1;
333 } else {
334 /* add new_sock */
335 struct ClientSocketMap* newClientSocket;
336 newClientSocket = (struct ClientSocketMap*) LL_Pop(freeClientSocketList);
337 if (newClientSocket)
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);
344 } else {
345 report(RPT_ERR, "%s: Error - free client socket list exhausted - %d clients.",
346 __FUNCTION__, FD_SETSIZE);
347 return -1;
350 if (clients_add_client(c) != 0) {
351 report(RPT_ERR, "%s: Could not add client on socket %i", __FUNCTION__, clientSocket->socket);
352 return -1;
354 } else {
355 /* Data arriving on an already-connected socket. */
356 err = 0;
357 do {
358 debug(RPT_DEBUG, "%s: reading...", __FUNCTION__);
359 err = sock_read_from_client(clientSocket);
360 debug(RPT_DEBUG, "%s: ...done", __FUNCTION__);
361 if (err < 0)
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);
379 } else {
380 report(RPT_ERR, "%s: Can't find client of socket %i",
381 __FUNCTION__, clientSocket->socket);
384 } while (err > 0);
388 return 0;
392 sock_read_from_client(struct ClientSocketMap* clientSocketMap)
394 char buffer[MAXMSG];
395 int nbytes, i;
397 debug(RPT_DEBUG, "%s()", __FUNCTION__);
399 errno = 0;
400 nbytes = sock_recv(clientSocketMap->socket, buffer, MAXMSG);
401 if (nbytes < 0)
403 if (errno != EAGAIN)
404 report(RPT_DEBUG, "%s: Error on socket %d - %s",
405 __FUNCTION__, clientSocketMap->socket, sock_geterror());
406 return 0;
407 } else if (nbytes == 0) /* EOF*/
409 return -1;
410 } else if (nbytes > (MAXMSG - (MAXMSG / 8))) /* Very noisy client...*/
412 sock_send_error(clientSocketMap->socket, "Too much data received... quiet down!\n");
413 return -1;
414 } else /* Data Read*/
416 buffer[nbytes] = 0;
417 /* Now, replace zeros with linefeeds...*/
418 for (i = 0; i < nbytes; i++)
419 if (buffer[i] == 0)
420 buffer[i] = '\n';
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);
425 } else {
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);
432 return nbytes;
434 return nbytes;
438 /* return 1 if addr is valid IPv4 */
439 int verify_ipv4(const char *addr)
441 int result = -1;
443 if (addr != NULL) {
444 struct in_addr a;
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)
455 int result = 0;
457 if (addr != NULL) {
458 struct in6_addr a;
460 /* inet_pton returns positive value if it worked */
461 result = inet_pton(AF_INET6, addr, &a);
463 return (result > 0) ? 1 : 0;