e79b5cc9ad454b5acfd2a090688e10c71ca5d15e
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /*
43  * Dynamic linked library for the olsr.org olsr daemon
44  */
45
46 #include <arpa/inet.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "ipcalc.h"
51 #include "olsr.h"
52 #include "scheduler.h"
53 #include "../../info/http_headers.h"
54 #include "../../info/info_types.h"
55 #include "txtinfo_printers.h"
56
57 #include "olsrd_txtinfo.h"
58
59 #ifdef _WIN32
60 #define close(x) closesocket(x)
61 #endif /* _WIN32 */
62
63 /* defines to make txtinfo and jsoninfo look alike */
64 #define PLUGIN_NAME "TXTINFO"
65 #define info_accept_ip txtinfo_accept_ip
66 #define info_listen_ip txtinfo_listen_ip
67 #define info_ipv6_only txtinfo_ipv6_only
68 #ifdef TXTINFO_ALLOW_LOCALHOST
69 #define INFO_ALLOW_LOCALHOST TXTINFO_ALLOW_LOCALHOST
70 #endif
71
72 static int ipc_socket;
73
74 /* IPC initialization function */
75 static int plugin_ipc_init(void);
76
77 static void send_info(unsigned int /*send_what*/, int /*socket*/);
78
79 static void ipc_action(int, void *, unsigned int);
80
81 #define TXT_IPC_BUFSIZE 256
82
83 /* these provide all of the runtime status info */
84 #define SIW_NEIGHBORS 0x0001
85 #define SIW_LINKS 0x0002
86 #define SIW_ROUTES 0x0004
87 #define SIW_HNA 0x0008
88 #define SIW_MID 0x0010
89 #define SIW_TOPOLOGY 0x0020
90 #define SIW_GATEWAYS 0x0040
91 #define SIW_INTERFACES 0x0080
92 #define SIW_2HOP 0x0100
93 #define SIW_SGW 0x0200
94 #define SIW_RUNTIME_ALL (SIW_NEIGHBORS | SIW_LINKS | SIW_ROUTES | SIW_HNA | SIW_MID | SIW_TOPOLOGY | SIW_GATEWAYS | SIW_INTERFACES | SIW_2HOP | SIW_SGW)
95
96 /* these only change at olsrd startup */
97 #define SIW_VERSION 0x0400
98 #define SIW_CONFIG 0x0800
99 #define SIW_PLUGINS 0x1000
100 #define SIW_STARTUP_ALL (SIW_VERSION | SIW_CONFIG | SIW_PLUGINS)
101
102 /* this is everything in normal format */
103 #define SIW_ALL (SIW_RUNTIME_ALL | SIW_STARTUP_ALL)
104
105 /* this data is not normal format but olsrd.conf format */
106 #define SIW_OLSRD_CONF 0x2000
107
108 #define MAX_CLIENTS 3
109
110 static char *outbuffer[MAX_CLIENTS];
111 static size_t outbuffer_size[MAX_CLIENTS];
112 static size_t outbuffer_written[MAX_CLIENTS];
113 static int outbuffer_socket[MAX_CLIENTS];
114 static int outbuffer_count = 0;
115
116 static struct timer_entry *writetimer_entry;
117
118 static printer_functions_t printer_functions = { //
119     //
120         .neighbors = &ipc_print_neighbors, //
121         .links = &ipc_print_links, //
122         .routes = &ipc_print_routes, //
123         .topology = &ipc_print_topology, //
124         .hna = &ipc_print_hna, //
125         .mid = &ipc_print_mid, //
126         .gateways = &ipc_print_gateways, //
127         .sgw = &ipc_print_sgw, //
128         .version = &ipc_print_version, //
129         .olsrd_conf = &ipc_print_olsrd_conf, //
130         .interfaces = &ipc_print_interfaces, //
131         .config = NULL, //
132         .plugins = NULL //
133     };
134
135 static void determine_action(unsigned int *send_what, char *requ) {
136   if (strstr(requ, "/con"))
137     *send_what |= SIW_OLSRD_CONF;
138   else if (strstr(requ, "/all"))
139     *send_what = SIW_ALL;
140   else {
141     // these are the two overarching categories
142     if (strstr(requ, "/runtime"))
143       *send_what |= SIW_RUNTIME_ALL;
144     if (strstr(requ, "/startup"))
145       *send_what |= SIW_STARTUP_ALL;
146
147     // these are the individual sections
148     if (strstr(requ, "/nei"))
149       *send_what |= SIW_NEIGHBORS;
150     if (strstr(requ, "/lin"))
151       *send_what |= SIW_LINKS;
152     if (strstr(requ, "/rou"))
153       *send_what |= SIW_ROUTES;
154     if (strstr(requ, "/hna"))
155       *send_what |= SIW_HNA;
156     if (strstr(requ, "/mid"))
157       *send_what |= SIW_MID;
158     if (strstr(requ, "/top"))
159       *send_what |= SIW_TOPOLOGY;
160     if (strstr(requ, "/gat"))
161       *send_what |= SIW_GATEWAYS;
162     if (strstr(requ, "/int"))
163       *send_what |= SIW_INTERFACES;
164     if (strstr(requ, "/2ho"))
165       *send_what |= SIW_2HOP;
166     if (strstr(requ, "/sgw"))
167       *send_what |= SIW_SGW;
168
169     // specials
170     if (strstr(requ, "/ver"))
171       *send_what |= SIW_VERSION;
172     if (strstr(requ, "/config"))
173       *send_what |= SIW_CONFIG;
174     if (strstr(requ, "/plugins"))
175       *send_what |= SIW_PLUGINS;
176
177     /* To print out neighbours only on the Freifunk Status
178      * page the normal output is somewhat lengthy. The
179      * header parsing is sufficient for standard wget.
180      */
181     if (strstr(requ, "/neighbours"))
182       *send_what = SIW_NEIGHBORS | SIW_LINKS;
183   }
184 }
185
186 static void plugin_init(void) {
187   /* nothing to do */
188 }
189
190 /**
191  *Do initialization here
192  *
193  *This function is called by the my_init
194  *function in uolsrd_plugin.c
195  */
196 int olsrd_plugin_init(void) {
197   /* Initial IPC value */
198   ipc_socket = -1;
199
200   plugin_init();
201
202   plugin_ipc_init();
203   return 1;
204 }
205
206 /**
207  * destructor - called at unload
208  */
209 void olsr_plugin_exit(void) {
210   if (ipc_socket != -1)
211     close(ipc_socket);
212 }
213
214 static int plugin_ipc_init(void) {
215   union olsr_sockaddr sst;
216   uint32_t yes = 1;
217   socklen_t addrlen;
218
219   /* Init ipc socket */
220   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
221 #ifndef NODEBUG
222     olsr_printf(1, "("PLUGIN_NAME") socket()=%s\n", strerror(errno));
223 #endif /* NODEBUG */
224     return 0;
225   } else {
226     if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
227 #ifndef NODEBUG
228       olsr_printf(1, "("PLUGIN_NAME") setsockopt()=%s\n", strerror(errno));
229 #endif /* NODEBUG */
230       return 0;
231     }
232 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
233     if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
234       perror("SO_REUSEADDR failed");
235       return 0;
236     }
237 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
238 #if defined linux && defined IPV6_V6ONLY
239     if (info_ipv6_only && olsr_cnf->ip_version == AF_INET6) {
240       if (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0) {
241         perror("IPV6_V6ONLY failed");
242         return 0;
243       }
244     }
245 #endif /* defined linux && defined IPV6_V6ONLY */
246     /* Bind the socket */
247
248     /* complete the socket structure */
249     memset(&sst, 0, sizeof(sst));
250     if (olsr_cnf->ip_version == AF_INET) {
251       sst.in4.sin_family = AF_INET;
252       addrlen = sizeof(struct sockaddr_in);
253 #ifdef SIN6_LEN
254       sst.in4.sin_len = addrlen;
255 #endif /* SIN6_LEN */
256       sst.in4.sin_addr.s_addr = info_listen_ip.v4.s_addr;
257       sst.in4.sin_port = htons(ipc_port);
258     } else {
259       sst.in6.sin6_family = AF_INET6;
260       addrlen = sizeof(struct sockaddr_in6);
261 #ifdef SIN6_LEN
262       sst.in6.sin6_len = addrlen;
263 #endif /* SIN6_LEN */
264       sst.in6.sin6_addr = info_listen_ip.v6;
265       sst.in6.sin6_port = htons(ipc_port);
266     }
267
268     /* bind the socket to the port number */
269     if (bind(ipc_socket, &sst.in, addrlen) == -1) {
270 #ifndef NODEBUG
271       olsr_printf(1, "("PLUGIN_NAME") bind()=%s\n", strerror(errno));
272 #endif /* NODEBUG */
273       return 0;
274     }
275
276     /* show that we are willing to listen */
277     if (listen(ipc_socket, 1) == -1) {
278 #ifndef NODEBUG
279       olsr_printf(1, "("PLUGIN_NAME") listen()=%s\n", strerror(errno));
280 #endif /* NODEBUG */
281       return 0;
282     }
283
284     /* Register with olsrd */
285     add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
286
287 #ifndef NODEBUG
288     olsr_printf(2, "("PLUGIN_NAME") listening on port %d\n", ipc_port);
289 #endif /* NODEBUG */
290   }
291   return 1;
292 }
293
294 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
295   union olsr_sockaddr pin;
296
297   char addr[INET6_ADDRSTRLEN];
298   fd_set rfds;
299   struct timeval tv;
300   unsigned int send_what = 0;
301   int ipc_connection;
302
303   socklen_t addrlen = sizeof(pin);
304
305   if (outbuffer_count >= MAX_CLIENTS) {
306     return;
307   }
308
309   if ((ipc_connection = accept(fd, &pin.in, &addrlen)) == -1) {
310 #ifndef NODEBUG
311     olsr_printf(1, "("PLUGIN_NAME") accept()=%s\n", strerror(errno));
312 #endif /* NODEBUG */
313     return;
314   }
315
316   tv.tv_sec = tv.tv_usec = 0;
317   if (olsr_cnf->ip_version == AF_INET) {
318     if (inet_ntop(olsr_cnf->ip_version, &pin.in4.sin_addr, addr, INET6_ADDRSTRLEN) == NULL)
319       addr[0] = '\0';
320     if (!ip4equal(&pin.in4.sin_addr, &info_accept_ip.v4) && info_accept_ip.v4.s_addr != INADDR_ANY) {
321 #ifdef INFO_ALLOW_LOCALHOST
322       if (ntohl(pin.in4.sin_addr.s_addr) != INADDR_LOOPBACK) {
323 #endif /* INFO_ALLOW_LOCALHOST */
324         olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
325         close(ipc_connection);
326         return;
327 #ifdef INFO_ALLOW_LOCALHOST
328       }
329 #endif /* INFO_ALLOW_LOCALHOST */
330     }
331   } else {
332     if (inet_ntop(olsr_cnf->ip_version, &pin.in6.sin6_addr, addr, INET6_ADDRSTRLEN) == NULL)
333       addr[0] = '\0';
334     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
335     if (!ip6equal(&in6addr_any, &info_accept_ip.v6) && !ip6equal(&pin.in6.sin6_addr, &info_accept_ip.v6)) {
336       olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
337       close(ipc_connection);
338       return;
339     }
340   }
341
342 #ifndef NODEBUG
343   olsr_printf(2, "("PLUGIN_NAME") Connect from %s\n", addr);
344 #endif /* NODEBUG */
345
346   /* purge read buffer to prevent blocking on linux */
347   FD_ZERO(&rfds);
348   FD_SET((unsigned int )ipc_connection, &rfds); /* Win32 needs the cast here */
349   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
350     char requ[1024];
351     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
352
353     if (s == sizeof(requ) - 1) {
354       /* input was much too long, just skip the rest */
355       char dummy[1024];
356
357       while (recv(ipc_connection, (void *) &dummy, sizeof(dummy), 0) == sizeof(dummy))
358         ;
359     }
360
361     if (0 < s) {
362       requ[s] = 0;
363       determine_action(&send_what, requ);
364     }
365
366     if (!send_what)
367       send_what = SIW_ALL;
368   }
369
370   send_info(send_what, ipc_connection);
371 }
372
373 static void info_write_data(void *foo __attribute__ ((unused))) {
374   fd_set set;
375   int result, i, j, max;
376   struct timeval tv;
377
378   FD_ZERO(&set);
379   max = 0;
380   for (i = 0; i < outbuffer_count; i++) {
381     /* And we cast here since we get a warning on Win32 */
382     FD_SET((unsigned int )(outbuffer_socket[i]), &set);
383
384     if (outbuffer_socket[i] > max) {
385       max = outbuffer_socket[i];
386     }
387   }
388
389   tv.tv_sec = 0;
390   tv.tv_usec = 0;
391
392   result = select(max + 1, NULL, &set, NULL, &tv);
393   if (result <= 0) {
394     return;
395   }
396
397   for (i = 0; i < outbuffer_count; i++) {
398     if (FD_ISSET(outbuffer_socket[i], &set)) {
399       result = send(outbuffer_socket[i], outbuffer[i] + outbuffer_written[i], outbuffer_size[i] - outbuffer_written[i], 0);
400       if (result > 0) {
401         outbuffer_written[i] += result;
402       }
403
404       if (result <= 0 || outbuffer_written[i] == outbuffer_size[i]) {
405         /* close this socket and cleanup*/
406         close(outbuffer_socket[i]);
407         free(outbuffer[i]);
408
409         for (j = i + 1; j < outbuffer_count; j++) {
410           outbuffer[j - 1] = outbuffer[j];
411           outbuffer_size[j - 1] = outbuffer_size[j];
412           outbuffer_socket[j - 1] = outbuffer_socket[j];
413           outbuffer_written[j - 1] = outbuffer_written[j];
414         }
415         outbuffer_count--;
416       }
417     }
418   }
419   if (!outbuffer_count) {
420     olsr_stop_timer(writetimer_entry);
421   }
422 }
423
424 static void send_info(unsigned int send_what, int the_socket) {
425   struct autobuf abuf;
426
427   const char *content_type = "text/plain; charset=utf-8";
428   int contentLengthPlaceholderStart = 0;
429   int headerLength = 0;
430
431   abuf_init(&abuf, 2 * 4096);
432
433   if (http_headers) {
434     build_http_header(PLUGIN_NAME, HTTP_200, content_type, &abuf, &contentLengthPlaceholderStart);
435     headerLength = abuf.len;
436   }
437
438   // only add if normal format
439   if (send_what & SIW_ALL) {
440     if ((send_what & SIW_LINKS) && printer_functions.links)
441       (*printer_functions.links)(&abuf);
442     if ((send_what & SIW_NEIGHBORS) && printer_functions.neighbors)
443       (*printer_functions.neighbors)(&abuf, false);
444     if ((send_what & SIW_TOPOLOGY) && printer_functions.topology)
445       (*printer_functions.topology)(&abuf);
446     if ((send_what & SIW_HNA) && printer_functions.hna)
447       (*printer_functions.hna)(&abuf);
448     if ((send_what & SIW_SGW) && printer_functions.sgw)
449       (*printer_functions.sgw)(&abuf);
450     if ((send_what & SIW_MID) && printer_functions.mid)
451       (*printer_functions.mid)(&abuf);
452     if ((send_what & SIW_ROUTES) && printer_functions.routes)
453       (*printer_functions.routes)(&abuf);
454     if ((send_what & SIW_GATEWAYS) && printer_functions.gateways)
455       (*printer_functions.gateways)(&abuf);
456     if ((send_what & SIW_CONFIG) && printer_functions.config)
457       (*printer_functions.config)(&abuf);
458     if ((send_what & SIW_INTERFACES) && printer_functions.interfaces)
459       (*printer_functions.interfaces)(&abuf);
460     if ((send_what & SIW_2HOP) && printer_functions.neighbors)
461       (*printer_functions.neighbors)(&abuf, true);
462     if ((send_what & SIW_VERSION) && printer_functions.version)
463       (*printer_functions.version)(&abuf);
464     if ((send_what & SIW_PLUGINS) && printer_functions.plugins)
465       (*printer_functions.plugins)(&abuf);
466   } else if ((send_what & SIW_OLSRD_CONF) && printer_functions.olsrd_conf) {
467     /* this outputs the olsrd.conf text directly, not normal format */
468     (*printer_functions.olsrd_conf)(&abuf);
469   }
470
471   if (http_headers) {
472     http_header_adjust_content_length(&abuf, contentLengthPlaceholderStart, abuf.len - headerLength);
473   }
474
475   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
476   outbuffer[outbuffer_count] = abuf.buf;
477   outbuffer_size[outbuffer_count] = abuf.len;
478   outbuffer_written[outbuffer_count] = 0;
479   outbuffer_socket[outbuffer_count] = the_socket;
480   abuf.buf = NULL;
481   abuf.len = 0;
482   abuf.size = 0;
483
484   outbuffer_count++;
485
486   if (outbuffer_count == 1) {
487     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &info_write_data, NULL, 0);
488   }
489
490   abuf_free(&abuf);
491 }
492
493 /*
494  * Local Variables:
495  * mode: c
496  * style: linux
497  * c-basic-offset: 2
498  * indent-tabs-mode: nil
499  * End:
500  */