Namespace cleanup of scheduler, remove "polling" sockets
[olsrd.git] / src / olsr_comport.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in
14  *   the documentation and/or other materials provided with the
15  *   distribution.
16  * * Neither the name of olsr.org, olsrd nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Visit http://www.olsr.org for more information.
34  *
35  * If you find this software useful feel free to make a donation
36  * to the project. For more information see the website or contact
37  * the copyright holders.
38  *
39  */
40 #include <ctype.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #ifdef WIN32
47 #include <io.h>
48 #else
49 #include <netdb.h>
50 #endif
51
52 #include "defs.h"
53 #include "olsr_logging.h"
54 #include "olsr_cfg.h"
55 #include "scheduler.h"
56 #include "olsr_memcookie.h"
57 #include "common/autobuf.h"
58 #include "common/avl.h"
59 #include "common/list.h"
60 #include "ipcalc.h"
61 #include "olsr.h"
62 #include "olsr_comport_http.h"
63 #include "olsr_comport_txt.h"
64 #include "olsr_comport.h"
65 #include "os_net.h"
66
67 #define HTTP_TIMEOUT  30000
68 #define TXT_TIMEOUT 60000
69
70 #define COMPORT_MAX_INPUTBUFFER 65536
71
72 #define OLSR_FOR_ALL_COMPORT_ENTRIES(comport, iterator) list_for_each_element_safe(&olsr_comport_head, comport, node, iterator)
73
74 struct list_entity olsr_comport_head;
75
76 /* server socket */
77 static int comsocket_http = 0;
78 static int comsocket_txt = 0;
79
80 static struct olsr_memcookie_info *connection_cookie;
81 static struct olsr_timer_info *connection_timeout;
82
83 /* counter for open connections */
84 static int connection_http_count;
85 static int connection_txt_count;
86
87 static int olsr_com_openport(int port);
88
89 static void olsr_com_parse_request(int fd, void *data, unsigned int flags);
90 static void olsr_com_parse_connection(int fd, void *data, unsigned int flags);
91 static void olsr_com_cleanup_session(struct comport_connection *con);
92
93 static void olsr_com_timeout_handler(void *);
94
95 void
96 olsr_com_init(bool failfast) {
97   connection_cookie =
98       olsr_memcookie_add("comport connections", sizeof(struct comport_connection));
99
100   connection_timeout = olsr_timer_add("comport timout",
101       &olsr_com_timeout_handler, false);
102
103   connection_http_count = 0;
104   connection_txt_count = 0;
105
106   list_init_head(&olsr_comport_head);
107
108   olsr_com_init_http();
109   olsr_com_init_txt();
110
111   if (olsr_cnf->comport_http > 0) {
112     if ((comsocket_http = olsr_com_openport(olsr_cnf->comport_http)) == -1) {
113       if (failfast) {
114         olsr_exit(1);
115       }
116     }
117     else {
118       olsr_socket_add(comsocket_http, &olsr_com_parse_request, NULL, OLSR_SOCKET_READ);
119     }
120   }
121   if (olsr_cnf->comport_txt > 0) {
122     if ((comsocket_txt = olsr_com_openport(olsr_cnf->comport_txt)) == -1) {
123       if (failfast) {
124         olsr_exit(1);
125       }
126     }
127     else {
128       olsr_socket_add(comsocket_txt, &olsr_com_parse_request, NULL, OLSR_SOCKET_READ);
129     }
130   }
131 }
132
133 void
134 olsr_com_destroy(void) {
135   struct comport_connection *con, *iterator;
136   OLSR_FOR_ALL_COMPORT_ENTRIES(con, iterator) {
137     olsr_com_cleanup_session(con);
138   }
139
140   olsr_com_destroy_http();
141   olsr_com_destroy_txt();
142 }
143
144 void
145 olsr_com_activate_output(struct comport_connection *con) {
146   olsr_socket_enable(con->fd, &olsr_com_parse_connection, OLSR_SOCKETPOLL_WRITE);
147 }
148
149 static int
150 olsr_com_openport(int port) {
151   struct sockaddr_storage sst;
152   uint32_t yes = 1;
153   socklen_t addrlen;
154
155 #if !defined REMOVE_LOG_WARN
156   char ipchar = olsr_cnf->ip_version == AF_INET ? '4' : '6';
157 #endif
158
159   /* Init ipc socket */
160   int s = socket(olsr_cnf->ip_version, SOCK_STREAM, 0);
161   if (s == -1) {
162     OLSR_WARN(LOG_COMPORT, "Cannot open %d com-socket for IPv%c: %s\n", port, ipchar, strerror(errno));
163     return -1;
164   }
165
166   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
167     OLSR_WARN(LOG_COMPORT, "Com-port %d SO_REUSEADDR for IPv%c failed: %s\n", port, ipchar, strerror(errno));
168     os_close(s);
169     return -1;
170   }
171
172   /* Bind the socket */
173
174   /* complete the socket structure */
175   memset(&sst, 0, sizeof(sst));
176   if (olsr_cnf->ip_version == AF_INET) {
177     struct sockaddr_in *addr4 = (struct sockaddr_in *) &sst;
178     addr4->sin_family = AF_INET;
179     addrlen = sizeof(*addr4);
180 #ifdef SIN6_LEN
181     addr4->sin_len = addrlen;
182 #endif
183     addr4->sin_addr.s_addr = INADDR_ANY;
184     addr4->sin_port = htons(port);
185   } else {
186     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &sst;
187     addr6->sin6_family = AF_INET6;
188     addrlen = sizeof(*addr6);
189 #ifdef SIN6_LEN
190     addr6->sin6_len = addrlen;
191 #endif
192     addr6->sin6_addr = in6addr_any;
193     addr6->sin6_port = htons(port);
194   }
195
196   /* bind the socket to the port number */
197   if (bind(s, (struct sockaddr *) &sst, addrlen) == -1) {
198     OLSR_WARN(LOG_COMPORT, "Com-port %d bind failed for IPv%c: %s\n", port, ipchar, strerror(errno));
199     os_close(s);
200     return -1;
201   }
202
203   /* show that we are willing to listen */
204   if (listen(s, 1) == -1) {
205     OLSR_WARN(LOG_COMPORT, "Com-port %d listen for IPv%c failed %s\n", port, ipchar, strerror(errno));
206     os_close(s);
207     return -1;
208   }
209
210   return s;
211 }
212
213 static void
214 olsr_com_parse_request(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
215   struct comport_connection *con;
216   struct sockaddr_storage addr;
217   socklen_t addrlen;
218   int sock;
219 #if !defined REMOVE_LOG_DEBUG
220   struct ipaddr_str buf;
221 #endif
222
223   addrlen = sizeof(addr);
224   sock = accept(fd, (struct sockaddr *) &addr, &addrlen);
225   if (sock < 0) {
226     return;
227   }
228
229   con = olsr_memcookie_malloc(connection_cookie);
230   abuf_init(&con->in, 1024);
231   abuf_init(&con->out, 0);
232
233   con->is_http = fd == comsocket_http;
234   con->fd = sock;
235
236   if (olsr_cnf->ip_version == AF_INET6) {
237     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr;
238     con->addr.v6 = addr6->sin6_addr;
239   } else {
240     struct sockaddr_in *addr4 = (struct sockaddr_in *) &addr;
241     con->addr.v4 = addr4->sin_addr;
242   }
243
244   if (con->is_http) {
245     if (connection_http_count < olsr_cnf->comport_http_limit) {
246       connection_http_count++;
247       con->state = HTTP_LOGIN;
248       con->send_as = PLAIN;
249       con->timeout_value = HTTP_TIMEOUT;
250     } else {
251       con->state = SEND_AND_QUIT;
252       con->send_as = HTTP_503_SERVICE_UNAVAILABLE;
253     }
254   } else { /* !con->is_http */
255     if (connection_txt_count < olsr_cnf->comport_txt_limit) {
256       connection_txt_count++;
257       con->state = INTERACTIVE;
258       con->send_as = PLAIN;
259       con->timeout_value = TXT_TIMEOUT;
260     } else {
261       abuf_puts(&con->out, "Too many txt connections, sorry...\n");
262       con->state = SEND_AND_QUIT;
263       con->send_as = PLAIN;
264     }
265   }
266
267   OLSR_DEBUG(LOG_COMPORT, "Got connection through socket %d from %s.\n",
268       sock, olsr_ip_to_string(&buf, &con->addr));
269
270   con->timeout = olsr_timer_start(con->timeout_value, 0, con, connection_timeout);
271
272   olsr_socket_add(sock, &olsr_com_parse_connection, con, OLSR_SOCKET_READ | OLSR_SOCKETPOLL_WRITE);
273
274   list_add_after(&olsr_comport_head, &con->node);
275 }
276
277 static void
278 olsr_com_cleanup_session(struct comport_connection *con) {
279   if (con->is_http) {
280     connection_http_count--;
281   } else {
282     connection_txt_count--;
283   }
284
285   list_remove(&con->node);
286
287   if (con->stop_handler) {
288     con->stop_handler(con);
289   }
290   olsr_socket_remove(con->fd, &olsr_com_parse_connection);
291   os_close(con->fd);
292
293   abuf_free(&con->in);
294   abuf_free(&con->out);
295
296   olsr_memcookie_free(connection_cookie, con);
297 }
298
299 static void
300 olsr_com_timeout_handler(void *data) {
301   struct comport_connection *con = data;
302   olsr_com_cleanup_session(con);
303 }
304
305 static void
306 olsr_com_parse_connection(int fd, void *data, unsigned int flags) {
307   struct comport_connection *con = data;
308 #if !defined(REMOVE_LOG_WARN)
309   struct ipaddr_str buf;
310 #endif
311
312   OLSR_DEBUG(LOG_COMPORT, "Parsing connection of socket %d\n", fd);
313   /* read data if necessary */
314   if (flags & OLSR_SOCKET_READ) {
315     char buffer[1024];
316     int len;
317
318     len = recv(fd, buffer, sizeof(buffer), 0);
319     if (len > 0) {
320       OLSR_DEBUG(LOG_COMPORT, "  recv returned %d\n", len);
321       if (con->state != SEND_AND_QUIT) {
322         abuf_memcpy(&con->in, buffer, len);
323       }
324
325       if (con->in.len > COMPORT_MAX_INPUTBUFFER) {
326         if (con->state == INTERACTIVE) {
327           abuf_puts(&con->out, "Sorry, input buffer overflow...\n");
328         } else if (con->state == HTTP_LOGIN) {
329           con->send_as = HTTP_413_REQUEST_TOO_LARGE;
330         }
331         con->state = SEND_AND_QUIT;
332       }
333     } else if (len < 0) {
334       OLSR_WARN(LOG_COMPORT, "Error while reading from communication stream with %s: %s\n",
335           olsr_ip_to_string(&buf, &con->addr), strerror(errno));
336       con->state = CLEANUP;
337     }
338     else {
339       con->state = SEND_AND_QUIT;
340     }
341   }
342
343   switch (con->state) {
344     case HTTP_LOGIN:
345       olsr_com_parse_http(con, flags);
346       break;
347     case INTERACTIVE:
348       olsr_com_parse_txt(con, flags);
349       break;
350     default:
351       break;
352   }
353
354   /* maybe we have to create an error message */
355   if (con->out.len == 0 && con->state == SEND_AND_QUIT && con->send_as != PLAIN
356       && con->send_as != HTTP_PLAIN && con->send_as != HTTP_200_OK) {
357     olsr_com_create_httperror(con);
358   }
359
360   /* send data if necessary */
361   if (con->out.len > 0) {
362     if (con->state == SEND_AND_QUIT && con->send_as != PLAIN) {
363       /* create header */
364       olsr_com_build_httpheader(con);
365       con->send_as = PLAIN;
366     }
367
368     if (flags & OLSR_SOCKETPOLL_WRITE) {
369       int len;
370
371       len = send(fd, con->out.buf, con->out.len, 0);
372       if (len > 0) {
373         OLSR_DEBUG(LOG_COMPORT, "  send returned %d\n", len);
374         abuf_pull(&con->out, len);
375       } else if (len < 0) {
376         OLSR_WARN(LOG_COMPORT, "Error while writing to communication stream with %s: %s\n",
377             olsr_ip_to_string(&buf, &con->addr), strerror(errno));
378         con->state = CLEANUP;
379       }
380     } else {
381       OLSR_DEBUG(LOG_COMPORT, "  activating output in scheduler\n");
382       olsr_socket_enable(fd, &olsr_com_parse_connection, OLSR_SOCKETPOLL_WRITE);
383     }
384   }
385   if (con->out.len == 0) {
386     OLSR_DEBUG(LOG_COMPORT, "  deactivating output in scheduler\n");
387     olsr_socket_disable(fd, &olsr_com_parse_connection, OLSR_SOCKETPOLL_WRITE);
388     if (con->state == SEND_AND_QUIT) {
389       con->state = CLEANUP;
390     }
391   }
392
393   /* end of connection ? */
394   if (con->state == CLEANUP) {
395     OLSR_DEBUG(LOG_COMPORT, "  cleanup\n");
396     /* clean up connection by calling timeout directly */
397     olsr_timer_stop(con->timeout);
398     con->timeout = NULL;
399     olsr_com_cleanup_session(con);
400   }
401   return;
402 }