0f6bf1d29202ec8f7455844f6387bcbe33d692d0
[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_cookie.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.loop, iterator.safe)
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_cookie_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_create_memcookie("comport connections", sizeof(struct comport_connection));
99
100   connection_timeout = olsr_alloc_timerinfo("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       add_olsr_socket(comsocket_http, &olsr_com_parse_request, NULL, NULL,
119           SP_PR_READ);
120     }
121   }
122   if (olsr_cnf->comport_txt > 0) {
123     if ((comsocket_txt = olsr_com_openport(olsr_cnf->comport_txt)) == -1) {
124       if (failfast) {
125         olsr_exit(1);
126       }
127     }
128     else {
129       add_olsr_socket(comsocket_txt, &olsr_com_parse_request, NULL, NULL,
130           SP_PR_READ);
131     }
132   }
133 }
134
135 void
136 olsr_com_destroy(void) {
137   struct comport_connection *con;
138   struct list_iterator iterator;
139   OLSR_FOR_ALL_COMPORT_ENTRIES(con, iterator) {
140     olsr_com_cleanup_session(con);
141   }
142
143   olsr_com_destroy_http();
144   olsr_com_destroy_txt();
145 }
146
147 void
148 olsr_com_activate_output(struct comport_connection *con) {
149   enable_olsr_socket(con->fd, &olsr_com_parse_connection, NULL, SP_PR_WRITE);
150 }
151
152 static int
153 olsr_com_openport(int port) {
154   struct sockaddr_storage sst;
155   uint32_t yes = 1;
156   socklen_t addrlen;
157
158 #if !defined REMOVE_LOG_WARN
159   char ipchar = olsr_cnf->ip_version == AF_INET ? '4' : '6';
160 #endif
161
162   /* Init ipc socket */
163   int s = socket(olsr_cnf->ip_version, SOCK_STREAM, 0);
164   if (s == -1) {
165     OLSR_WARN(LOG_COMPORT, "Cannot open %d com-socket for IPv%c: %s\n", port, ipchar, strerror(errno));
166     return -1;
167   }
168
169   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
170     OLSR_WARN(LOG_COMPORT, "Com-port %d SO_REUSEADDR for IPv%c failed: %s\n", port, ipchar, strerror(errno));
171     os_close(s);
172     return -1;
173   }
174
175   /* Bind the socket */
176
177   /* complete the socket structure */
178   memset(&sst, 0, sizeof(sst));
179   if (olsr_cnf->ip_version == AF_INET) {
180     struct sockaddr_in *addr4 = (struct sockaddr_in *) &sst;
181     addr4->sin_family = AF_INET;
182     addrlen = sizeof(*addr4);
183 #ifdef SIN6_LEN
184     addr4->sin_len = addrlen;
185 #endif
186     addr4->sin_addr.s_addr = INADDR_ANY;
187     addr4->sin_port = htons(port);
188   } else {
189     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &sst;
190     addr6->sin6_family = AF_INET6;
191     addrlen = sizeof(*addr6);
192 #ifdef SIN6_LEN
193     addr6->sin6_len = addrlen;
194 #endif
195     addr6->sin6_addr = in6addr_any;
196     addr6->sin6_port = htons(port);
197   }
198
199   /* bind the socket to the port number */
200   if (bind(s, (struct sockaddr *) &sst, addrlen) == -1) {
201     OLSR_WARN(LOG_COMPORT, "Com-port %d bind failed for IPv%c: %s\n", port, ipchar, strerror(errno));
202     os_close(s);
203     return -1;
204   }
205
206   /* show that we are willing to listen */
207   if (listen(s, 1) == -1) {
208     OLSR_WARN(LOG_COMPORT, "Com-port %d listen for IPv%c failed %s\n", port, ipchar, strerror(errno));
209     os_close(s);
210     return -1;
211   }
212
213   return s;
214 }
215
216 static void
217 olsr_com_parse_request(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
218   struct comport_connection *con;
219   struct sockaddr_storage addr;
220   socklen_t addrlen;
221   int sock;
222 #if !defined REMOVE_LOG_DEBUG
223   struct ipaddr_str buf;
224 #endif
225
226   addrlen = sizeof(addr);
227   sock = accept(fd, (struct sockaddr *) &addr, &addrlen);
228   if (sock < 0) {
229     return;
230   }
231
232   con = olsr_cookie_malloc(connection_cookie);
233   abuf_init(&con->in, 1024);
234   abuf_init(&con->out, 0);
235
236   con->is_http = fd == comsocket_http;
237   con->fd = sock;
238
239   if (olsr_cnf->ip_version == AF_INET6) {
240     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr;
241     con->addr.v6 = addr6->sin6_addr;
242   } else {
243     struct sockaddr_in *addr4 = (struct sockaddr_in *) &addr;
244     con->addr.v4 = addr4->sin_addr;
245   }
246
247   if (con->is_http) {
248     if (connection_http_count < olsr_cnf->comport_http_limit) {
249       connection_http_count++;
250       con->state = HTTP_LOGIN;
251       con->send_as = PLAIN;
252       con->timeout_value = HTTP_TIMEOUT;
253     } else {
254       con->state = SEND_AND_QUIT;
255       con->send_as = HTTP_503_SERVICE_UNAVAILABLE;
256     }
257   } else { /* !con->is_http */
258     if (connection_txt_count < olsr_cnf->comport_txt_limit) {
259       connection_txt_count++;
260       con->state = INTERACTIVE;
261       con->send_as = PLAIN;
262       con->timeout_value = TXT_TIMEOUT;
263     } else {
264       abuf_puts(&con->out, "Too many txt connections, sorry...\n");
265       con->state = SEND_AND_QUIT;
266       con->send_as = PLAIN;
267     }
268   }
269
270   OLSR_DEBUG(LOG_COMPORT, "Got connection through socket %d from %s.\n",
271       sock, olsr_ip_to_string(&buf, &con->addr));
272
273   con->timeout = olsr_start_timer(con->timeout_value, 0, con, connection_timeout);
274
275   add_olsr_socket(sock, &olsr_com_parse_connection, NULL, con, SP_PR_READ
276       | SP_PR_WRITE);
277
278   list_add_after(&olsr_comport_head, &con->node);
279 }
280
281 static void
282 olsr_com_cleanup_session(struct comport_connection *con) {
283   if (con->is_http) {
284     connection_http_count--;
285   } else {
286     connection_txt_count--;
287   }
288
289   list_remove(&con->node);
290
291   if (con->stop_handler) {
292     con->stop_handler(con);
293   }
294   remove_olsr_socket(con->fd, &olsr_com_parse_connection, NULL);
295   os_close(con->fd);
296
297   abuf_free(&con->in);
298   abuf_free(&con->out);
299
300   olsr_cookie_free(connection_cookie, con);
301 }
302
303 static void
304 olsr_com_timeout_handler(void *data) {
305   struct comport_connection *con = data;
306   olsr_com_cleanup_session(con);
307 }
308
309 static void
310 olsr_com_parse_connection(int fd, void *data, unsigned int flags) {
311   struct comport_connection *con = data;
312 #if !defined(REMOVE_LOG_WARN)
313   struct ipaddr_str buf;
314 #endif
315
316   OLSR_DEBUG(LOG_COMPORT, "Parsing connection of socket %d\n", fd);
317   /* read data if necessary */
318   if (flags & SP_PR_READ) {
319     char buffer[1024];
320     int len;
321
322     len = recv(fd, buffer, sizeof(buffer), 0);
323     if (len > 0) {
324       OLSR_DEBUG(LOG_COMPORT, "  recv returned %d\n", len);
325       if (con->state != SEND_AND_QUIT) {
326         abuf_memcpy(&con->in, buffer, len);
327       }
328
329       if (con->in.len > COMPORT_MAX_INPUTBUFFER) {
330         if (con->state == INTERACTIVE) {
331           abuf_puts(&con->out, "Sorry, input buffer overflow...\n");
332         } else if (con->state == HTTP_LOGIN) {
333           con->send_as = HTTP_413_REQUEST_TOO_LARGE;
334         }
335         con->state = SEND_AND_QUIT;
336       }
337     } else if (len < 0) {
338       OLSR_WARN(LOG_COMPORT, "Error while reading from communication stream with %s: %s\n",
339           olsr_ip_to_string(&buf, &con->addr), strerror(errno));
340       con->state = CLEANUP;
341     }
342     else {
343       con->state = SEND_AND_QUIT;
344     }
345   }
346
347   switch (con->state) {
348     case HTTP_LOGIN:
349       olsr_com_parse_http(con, flags);
350       break;
351     case INTERACTIVE:
352       olsr_com_parse_txt(con, flags);
353       break;
354     default:
355       break;
356   }
357
358   /* maybe we have to create an error message */
359   if (con->out.len == 0 && con->state == SEND_AND_QUIT && con->send_as != PLAIN
360       && con->send_as != HTTP_PLAIN && con->send_as != HTTP_200_OK) {
361     olsr_com_create_httperror(con);
362   }
363
364   /* send data if necessary */
365   if (con->out.len > 0) {
366     if (con->state == SEND_AND_QUIT && con->send_as != PLAIN) {
367       /* create header */
368       olsr_com_build_httpheader(con);
369       con->send_as = PLAIN;
370     }
371
372     if (flags & SP_PR_WRITE) {
373       int len;
374
375       len = send(fd, con->out.buf, con->out.len, 0);
376       if (len > 0) {
377         OLSR_DEBUG(LOG_COMPORT, "  send returned %d\n", len);
378         abuf_pull(&con->out, len);
379       } else if (len < 0) {
380         OLSR_WARN(LOG_COMPORT, "Error while writing to communication stream with %s: %s\n",
381             olsr_ip_to_string(&buf, &con->addr), strerror(errno));
382         con->state = CLEANUP;
383       }
384     } else {
385       OLSR_DEBUG(LOG_COMPORT, "  activating output in scheduler\n");
386       enable_olsr_socket(fd, &olsr_com_parse_connection, NULL, SP_PR_WRITE);
387     }
388   }
389   if (con->out.len == 0) {
390     OLSR_DEBUG(LOG_COMPORT, "  deactivating output in scheduler\n");
391     disable_olsr_socket(fd, &olsr_com_parse_connection, NULL, SP_PR_WRITE);
392     if (con->state == SEND_AND_QUIT) {
393       con->state = CLEANUP;
394     }
395   }
396
397   /* end of connection ? */
398   if (con->state == CLEANUP) {
399     OLSR_DEBUG(LOG_COMPORT, "  cleanup\n");
400     /* clean up connection by calling timeout directly */
401     olsr_stop_timer(con->timeout);
402     con->timeout = NULL;
403     olsr_com_cleanup_session(con);
404   }
405   return;
406 }