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