Merge branch 'master' into scheduler_cleanup
[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 "olsr_timer.h"
56 #include "olsr_socket.h"
57 #include "olsr_memcookie.h"
58 #include "common/autobuf.h"
59 #include "common/avl.h"
60 #include "common/list.h"
61 #include "ipcalc.h"
62 #include "olsr.h"
63 #include "olsr_comport_http.h"
64 #include "olsr_comport_txt.h"
65 #include "olsr_comport.h"
66 #include "os_net.h"
67
68 #define HTTP_TIMEOUT  30000
69 #define TXT_TIMEOUT 60000
70
71 #define COMPORT_MAX_INPUTBUFFER 65536
72
73 #define OLSR_FOR_ALL_COMPORT_ENTRIES(comport, iterator) list_for_each_element_safe(&olsr_comport_head, comport, node, iterator)
74
75 struct list_entity olsr_comport_head;
76
77 /* server socket */
78 static struct olsr_socket_entry *comsocket_http;
79 static struct olsr_socket_entry *comsocket_txt;
80
81 static struct olsr_memcookie_info *connection_cookie;
82 static struct olsr_timer_info *connection_timeout;
83
84 /* counter for open connections */
85 static int connection_http_count;
86 static int connection_txt_count;
87
88 static int olsr_com_openport(int port);
89
90 static void olsr_com_parse_request(int fd, void *data, unsigned int flags);
91 static void olsr_com_parse_connection(int fd, void *data, unsigned int flags);
92 static void olsr_com_cleanup_session(struct comport_connection *con);
93
94 static void olsr_com_timeout_handler(void *);
95
96 void
97 olsr_com_init(void) {
98   int sock_http, sock_txt;
99
100   connection_cookie =
101       olsr_memcookie_add("comport connections", sizeof(struct comport_connection));
102
103   connection_timeout = olsr_timer_add("comport timout",
104       &olsr_com_timeout_handler, false);
105
106   connection_http_count = 0;
107   connection_txt_count = 0;
108
109   list_init_head(&olsr_comport_head);
110
111   olsr_com_init_http();
112   olsr_com_init_txt();
113
114   if (olsr_cnf->comport_http > 0) {
115     sock_http = olsr_com_openport(olsr_cnf->comport_http);
116
117     if (NULL == (comsocket_http =
118         olsr_socket_add(sock_http, &olsr_com_parse_request, NULL, OLSR_SOCKET_READ))) {
119       OLSR_ERROR(LOG_COMPORT, "Cannot http-register socket with scheduler");
120       olsr_exit(1);
121     }
122   }
123   if (olsr_cnf->comport_txt > 0) {
124     sock_txt = olsr_com_openport(olsr_cnf->comport_txt);
125
126     if (NULL == (comsocket_txt =
127         olsr_socket_add(sock_txt, &olsr_com_parse_request, NULL, OLSR_SOCKET_READ))) {
128       OLSR_ERROR(LOG_COMPORT, "Cannot register txt-socket with scheduler");
129       olsr_exit(1);
130     }
131   }
132 }
133
134 void
135 olsr_com_destroy(void) {
136   struct comport_connection *con, *iterator;
137   OLSR_FOR_ALL_COMPORT_ENTRIES(con, iterator) {
138     olsr_com_cleanup_session(con);
139   }
140
141   olsr_com_destroy_http();
142   olsr_com_destroy_txt();
143 }
144
145 void
146 olsr_com_activate_output(struct comport_connection *con) {
147   olsr_socket_enable(con->sock, OLSR_SOCKET_WRITE);
148 }
149
150 static int
151 olsr_com_openport(int port) {
152   struct sockaddr_storage sst;
153   uint32_t yes = 1;
154   socklen_t addrlen;
155
156 #if !defined REMOVE_LOG_WARN
157   char ipchar = olsr_cnf->ip_version == AF_INET ? '4' : '6';
158 #endif
159
160   /* Init ipc socket */
161   int s = socket(olsr_cnf->ip_version, SOCK_STREAM, 0);
162   if (s == -1) {
163     OLSR_ERROR(LOG_COMPORT, "Cannot open %d com-socket for IPv%c: %s\n", port, ipchar, strerror(errno));
164     olsr_exit(1);
165   }
166
167   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
168     OLSR_ERROR(LOG_COMPORT, "Com-port %d SO_REUSEADDR for IPv%c failed: %s\n", port, ipchar, strerror(errno));
169     os_close(s);
170     olsr_exit(1);
171   }
172
173   /* Bind the socket */
174
175   /* complete the socket structure */
176   memset(&sst, 0, sizeof(sst));
177   if (olsr_cnf->ip_version == AF_INET) {
178     struct sockaddr_in *addr4 = (struct sockaddr_in *) &sst;
179     addr4->sin_family = AF_INET;
180     addrlen = sizeof(*addr4);
181 #ifdef SIN6_LEN
182     addr4->sin_len = addrlen;
183 #endif
184     addr4->sin_addr.s_addr = INADDR_ANY;
185     addr4->sin_port = htons(port);
186   } else {
187     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &sst;
188     addr6->sin6_family = AF_INET6;
189     addrlen = sizeof(*addr6);
190 #ifdef SIN6_LEN
191     addr6->sin6_len = addrlen;
192 #endif
193     addr6->sin6_addr = in6addr_any;
194     addr6->sin6_port = htons(port);
195   }
196
197   /* bind the socket to the port number */
198   if (bind(s, (struct sockaddr *) &sst, addrlen) == -1) {
199     OLSR_ERROR(LOG_COMPORT, "Com-port %d bind failed for IPv%c: %s\n", port, ipchar, strerror(errno));
200     os_close(s);
201     olsr_exit(1);
202   }
203
204   /* show that we are willing to listen */
205   if (listen(s, 1) == -1) {
206     OLSR_ERROR(LOG_COMPORT, "Com-port %d listen for IPv%c failed %s\n", port, ipchar, strerror(errno));
207     os_close(s);
208     olsr_exit(1);
209   }
210
211   return s;
212 }
213
214 static void
215 olsr_com_parse_request(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
216   struct comport_connection *con;
217   struct sockaddr_storage addr;
218   socklen_t addrlen;
219   int sock;
220 #if !defined REMOVE_LOG_DEBUG
221   struct ipaddr_str buf;
222 #endif
223
224   addrlen = sizeof(addr);
225   sock = accept(fd, (struct sockaddr *) &addr, &addrlen);
226   if (sock < 0) {
227     return;
228   }
229
230   con = olsr_memcookie_malloc(connection_cookie);
231   abuf_init(&con->in, 1024);
232   abuf_init(&con->out, 0);
233
234   con->is_http = fd == comsocket_http->fd;
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   con->sock = olsr_socket_add(sock, &olsr_com_parse_connection, con,
273       OLSR_SOCKET_READ | OLSR_SOCKET_WRITE);
274
275   list_add_after(&olsr_comport_head, &con->node);
276 }
277
278 static void
279 olsr_com_cleanup_session(struct comport_connection *con) {
280   if (con->is_http) {
281     connection_http_count--;
282   } else {
283     connection_txt_count--;
284   }
285
286   list_remove(&con->node);
287
288   if (con->stop_handler) {
289     con->stop_handler(con);
290   }
291
292   os_close(con->sock->fd);
293   olsr_socket_remove(con->sock);
294
295   abuf_free(&con->in);
296   abuf_free(&con->out);
297
298   olsr_memcookie_free(connection_cookie, con);
299 }
300
301 static void
302 olsr_com_timeout_handler(void *data) {
303   struct comport_connection *con = data;
304   olsr_com_cleanup_session(con);
305 }
306
307 static void
308 olsr_com_parse_connection(int fd, void *data, unsigned int flags) {
309   struct comport_connection *con = data;
310 #if !defined(REMOVE_LOG_WARN)
311   struct ipaddr_str buf;
312 #endif
313
314   OLSR_DEBUG(LOG_COMPORT, "Parsing connection of socket %d\n", fd);
315   /* read data if necessary */
316   if (flags & OLSR_SOCKET_READ) {
317     char buffer[1024];
318     int len;
319
320     len = recv(fd, buffer, sizeof(buffer), 0);
321     if (len > 0) {
322       OLSR_DEBUG(LOG_COMPORT, "  recv returned %d\n", len);
323       if (con->state != SEND_AND_QUIT) {
324         abuf_memcpy(&con->in, buffer, len);
325       }
326
327       if (con->in.len > COMPORT_MAX_INPUTBUFFER) {
328         if (con->state == INTERACTIVE) {
329           abuf_puts(&con->out, "Sorry, input buffer overflow...\n");
330         } else if (con->state == HTTP_LOGIN) {
331           con->send_as = HTTP_413_REQUEST_TOO_LARGE;
332         }
333         con->state = SEND_AND_QUIT;
334       }
335     } else if (len < 0) {
336       OLSR_WARN(LOG_COMPORT, "Error while reading from communication stream with %s: %s\n",
337           olsr_ip_to_string(&buf, &con->addr), strerror(errno));
338       con->state = CLEANUP;
339     }
340     else {
341       con->state = SEND_AND_QUIT;
342     }
343   }
344
345   switch (con->state) {
346     case HTTP_LOGIN:
347       olsr_com_parse_http(con, flags);
348       break;
349     case INTERACTIVE:
350       olsr_com_parse_txt(con, flags);
351       break;
352     default:
353       break;
354   }
355
356   /* maybe we have to create an error message */
357   if (con->out.len == 0 && con->state == SEND_AND_QUIT && con->send_as != PLAIN
358       && con->send_as != HTTP_PLAIN && con->send_as != HTTP_200_OK) {
359     olsr_com_create_httperror(con);
360   }
361
362   /* send data if necessary */
363   if (con->out.len > 0) {
364     if (con->state == SEND_AND_QUIT && con->send_as != PLAIN) {
365       /* create header */
366       olsr_com_build_httpheader(con);
367       con->send_as = PLAIN;
368     }
369
370     if (flags & OLSR_SOCKET_WRITE) {
371       int len;
372
373       len = send(fd, con->out.buf, con->out.len, 0);
374       if (len > 0) {
375         OLSR_DEBUG(LOG_COMPORT, "  send returned %d\n", len);
376         abuf_pull(&con->out, len);
377       } else if (len < 0) {
378         OLSR_WARN(LOG_COMPORT, "Error while writing to communication stream with %s: %s\n",
379             olsr_ip_to_string(&buf, &con->addr), strerror(errno));
380         con->state = CLEANUP;
381       }
382     } else {
383       OLSR_DEBUG(LOG_COMPORT, "  activating output in scheduler\n");
384       olsr_socket_enable(con->sock, OLSR_SOCKET_WRITE);
385     }
386   }
387   if (con->out.len == 0) {
388     OLSR_DEBUG(LOG_COMPORT, "  deactivating output in scheduler\n");
389     olsr_socket_disable(con->sock, OLSR_SOCKET_WRITE);
390     if (con->state == SEND_AND_QUIT) {
391       con->state = CLEANUP;
392     }
393   }
394
395   /* end of connection ? */
396   if (con->state == CLEANUP) {
397     OLSR_DEBUG(LOG_COMPORT, "  cleanup\n");
398     /* clean up connection by calling timeout directly */
399     olsr_timer_stop(con->timeout);
400     con->timeout = NULL;
401     olsr_com_cleanup_session(con);
402   }
403   return;
404 }