6091d3331d63f96d2591940b75df9989be51f618
[olsrd.git] / lib / jsoninfo / src / olsrd_jsoninfo.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 #include <stddef.h>
50
51 #include "ipcalc.h"
52 #include "olsr.h"
53 #include "scheduler.h"
54 #include "../../info/http_headers.h"
55 #include "../../info/info_types.h"
56 #include "jsoninfo_printers.h"
57 #include "olsrd_jsoninfo_helpers.h"
58 #include "olsrd_jsoninfo.h"
59
60 #ifdef _WIN32
61 #define close(x) closesocket(x)
62 #endif /* _WIN32 */
63
64 /* defines to make txtinfo and jsoninfo look alike */
65 #define PLUGIN_NAME "JSONINFO"
66 #define info_accept_ip jsoninfo_accept_ip
67 #define info_listen_ip jsoninfo_listen_ip
68 #define info_ipv6_only jsoninfo_ipv6_only
69 #ifdef JSONINFO_ALLOW_LOCALHOST
70 #define INFO_ALLOW_LOCALHOST JSONINFO_ALLOW_LOCALHOST
71 #endif
72
73 static int ipc_socket;
74
75 /* IPC initialization function */
76 static int plugin_ipc_init(void);
77
78 static void send_info(unsigned int /*send_what*/, int /*socket*/);
79
80 static void ipc_action(int, void *, unsigned int);
81
82 #define TXT_IPC_BUFSIZE 256
83
84 #define MAX_CLIENTS 3
85
86 static char *outbuffer[MAX_CLIENTS];
87 static size_t outbuffer_size[MAX_CLIENTS];
88 static size_t outbuffer_written[MAX_CLIENTS];
89 static int outbuffer_socket[MAX_CLIENTS];
90 static int outbuffer_count = 0;
91
92 static struct timer_entry *writetimer_entry;
93
94 static printer_functions_t printer_functions = { //
95     //
96         .neighbors = &ipc_print_neighbors, //
97         .links = &ipc_print_links, //
98         .routes = &ipc_print_routes, //
99         .topology = &ipc_print_topology, //
100         .hna = &ipc_print_hna, //
101         .mid = &ipc_print_mid, //
102         .gateways = &ipc_print_gateways, //
103         .sgw = &ipc_print_sgw, //
104         .version = &ipc_print_version, //
105         .olsrd_conf = &ipc_print_olsrd_conf, //
106         .interfaces = &ipc_print_interfaces, //
107         .config = &ipc_print_config, //
108         .plugins = &ipc_print_plugins //
109     };
110
111 static void determine_action(unsigned int *send_what, char *requ) {
112   if (strstr(requ, "/olsrd.conf"))
113     *send_what |= SIW_OLSRD_CONF;
114   else if (strstr(requ, "/all"))
115     *send_what = SIW_ALL;
116   else {
117     // these are the two overarching categories
118     if (strstr(requ, "/runtime"))
119       *send_what |= SIW_RUNTIME_ALL;
120     if (strstr(requ, "/startup"))
121       *send_what |= SIW_STARTUP_ALL;
122
123     // these are the individual sections
124     if (strstr(requ, "/neighbors"))
125       *send_what |= SIW_NEIGHBORS;
126     if (strstr(requ, "/links"))
127       *send_what |= SIW_LINKS;
128     if (strstr(requ, "/routes"))
129       *send_what |= SIW_ROUTES;
130     if (strstr(requ, "/hna"))
131       *send_what |= SIW_HNA;
132     if (strstr(requ, "/mid"))
133       *send_what |= SIW_MID;
134     if (strstr(requ, "/topology"))
135       *send_what |= SIW_TOPOLOGY;
136     if (strstr(requ, "/gateways"))
137       *send_what |= SIW_GATEWAYS;
138     if (strstr(requ, "/interfaces"))
139       *send_what |= SIW_INTERFACES;
140     if (strstr(requ, "/2hop"))
141       *send_what |= SIW_2HOP;
142     if (strstr(requ, "/sgw"))
143       *send_what |= SIW_SGW;
144
145     // specials
146     if (strstr(requ, "/version"))
147       *send_what |= SIW_VERSION;
148     if (strstr(requ, "/config"))
149       *send_what |= SIW_CONFIG;
150     if (strstr(requ, "/plugins"))
151       *send_what |= SIW_PLUGINS;
152
153     /* To print out neighbours only on the Freifunk Status
154      * page the normal output is somewhat lengthy. The
155      * header parsing is sufficient for standard wget.
156      */
157     if (strstr(requ, "/neighbours"))
158       *send_what = SIW_NEIGHBORS | SIW_LINKS;
159   }
160 }
161
162 /**
163  *Do initialization here
164  *
165  *This function is called by the my_init
166  *function in uolsrd_plugin.c
167  */
168 int olsrd_plugin_init(void) {
169   /* Initial IPC value */
170   ipc_socket = -1;
171
172   plugin_init(PLUGIN_NAME);
173
174   plugin_ipc_init();
175   return 1;
176 }
177
178 /**
179  * destructor - called at unload
180  */
181 void olsr_plugin_exit(void) {
182   if (ipc_socket != -1)
183     close(ipc_socket);
184 }
185
186 static int plugin_ipc_init(void) {
187   union olsr_sockaddr sst;
188   uint32_t yes = 1;
189   socklen_t addrlen;
190
191   /* Init ipc socket */
192   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
193 #ifndef NODEBUG
194     olsr_printf(1, "("PLUGIN_NAME") socket()=%s\n", strerror(errno));
195 #endif /* NODEBUG */
196     return 0;
197   } else {
198     if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
199 #ifndef NODEBUG
200       olsr_printf(1, "("PLUGIN_NAME") setsockopt()=%s\n", strerror(errno));
201 #endif /* NODEBUG */
202       return 0;
203     }
204 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
205     if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
206       perror("SO_REUSEADDR failed");
207       return 0;
208     }
209 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
210 #if defined linux && defined IPV6_V6ONLY
211     if (info_ipv6_only && olsr_cnf->ip_version == AF_INET6) {
212       if (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0) {
213         perror("IPV6_V6ONLY failed");
214         return 0;
215       }
216     }
217 #endif /* defined linux && defined IPV6_V6ONLY */
218     /* Bind the socket */
219
220     /* complete the socket structure */
221     memset(&sst, 0, sizeof(sst));
222     if (olsr_cnf->ip_version == AF_INET) {
223       sst.in4.sin_family = AF_INET;
224       addrlen = sizeof(struct sockaddr_in);
225 #ifdef SIN6_LEN
226       sst.in4.sin_len = addrlen;
227 #endif /* SIN6_LEN */
228       sst.in4.sin_addr.s_addr = info_listen_ip.v4.s_addr;
229       sst.in4.sin_port = htons(ipc_port);
230     } else {
231       sst.in6.sin6_family = AF_INET6;
232       addrlen = sizeof(struct sockaddr_in6);
233 #ifdef SIN6_LEN
234       sst.in6.sin6_len = addrlen;
235 #endif /* SIN6_LEN */
236       sst.in6.sin6_addr = info_listen_ip.v6;
237       sst.in6.sin6_port = htons(ipc_port);
238     }
239
240     /* bind the socket to the port number */
241     if (bind(ipc_socket, &sst.in, addrlen) == -1) {
242 #ifndef NODEBUG
243       olsr_printf(1, "("PLUGIN_NAME") bind()=%s\n", strerror(errno));
244 #endif /* NODEBUG */
245       return 0;
246     }
247
248     /* show that we are willing to listen */
249     if (listen(ipc_socket, 1) == -1) {
250 #ifndef NODEBUG
251       olsr_printf(1, "("PLUGIN_NAME") listen()=%s\n", strerror(errno));
252 #endif /* NODEBUG */
253       return 0;
254     }
255
256     /* Register with olsrd */
257     add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
258
259 #ifndef NODEBUG
260     olsr_printf(2, "("PLUGIN_NAME") listening on port %d\n", ipc_port);
261 #endif /* NODEBUG */
262   }
263   return 1;
264 }
265
266 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
267   union olsr_sockaddr pin;
268
269   char addr[INET6_ADDRSTRLEN];
270   fd_set rfds;
271   struct timeval tv;
272   unsigned int send_what = 0;
273   int ipc_connection;
274
275   socklen_t addrlen = sizeof(pin);
276
277   if (outbuffer_count >= MAX_CLIENTS) {
278     return;
279   }
280
281   if ((ipc_connection = accept(fd, &pin.in, &addrlen)) == -1) {
282 #ifndef NODEBUG
283     olsr_printf(1, "("PLUGIN_NAME") accept()=%s\n", strerror(errno));
284 #endif /* NODEBUG */
285     return;
286   }
287
288   tv.tv_sec = tv.tv_usec = 0;
289   if (olsr_cnf->ip_version == AF_INET) {
290     if (inet_ntop(olsr_cnf->ip_version, &pin.in4.sin_addr, addr, INET6_ADDRSTRLEN) == NULL)
291       addr[0] = '\0';
292     if (!ip4equal(&pin.in4.sin_addr, &info_accept_ip.v4) && info_accept_ip.v4.s_addr != INADDR_ANY) {
293 #ifdef INFO_ALLOW_LOCALHOST
294       if (ntohl(pin.in4.sin_addr.s_addr) != INADDR_LOOPBACK) {
295 #endif /* INFO_ALLOW_LOCALHOST */
296         olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
297         close(ipc_connection);
298         return;
299 #ifdef INFO_ALLOW_LOCALHOST
300       }
301 #endif /* INFO_ALLOW_LOCALHOST */
302     }
303   } else {
304     if (inet_ntop(olsr_cnf->ip_version, &pin.in6.sin6_addr, addr, INET6_ADDRSTRLEN) == NULL)
305       addr[0] = '\0';
306     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
307     if (!ip6equal(&in6addr_any, &info_accept_ip.v6) && !ip6equal(&pin.in6.sin6_addr, &info_accept_ip.v6)) {
308       olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
309       close(ipc_connection);
310       return;
311     }
312   }
313
314 #ifndef NODEBUG
315   olsr_printf(2, "("PLUGIN_NAME") Connect from %s\n", addr);
316 #endif /* NODEBUG */
317
318   /* purge read buffer to prevent blocking on linux */
319   FD_ZERO(&rfds);
320   FD_SET((unsigned int )ipc_connection, &rfds); /* Win32 needs the cast here */
321   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
322     char requ[1024];
323     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
324
325     if (s == sizeof(requ) - 1) {
326       /* input was much too long, just skip the rest */
327       char dummy[1024];
328
329       while (recv(ipc_connection, (void *) &dummy, sizeof(dummy), 0) == sizeof(dummy))
330         ;
331     }
332
333     if (0 < s) {
334       requ[s] = 0;
335       determine_action(&send_what, requ);
336     }
337
338     if (!send_what)
339       send_what = SIW_ALL;
340   }
341
342   send_info(send_what, ipc_connection);
343 }
344
345 static void info_write_data(void *foo __attribute__ ((unused))) {
346   fd_set set;
347   int result, i, j, max;
348   struct timeval tv;
349
350   FD_ZERO(&set);
351   max = 0;
352   for (i = 0; i < outbuffer_count; i++) {
353     /* And we cast here since we get a warning on Win32 */
354     FD_SET((unsigned int )(outbuffer_socket[i]), &set);
355
356     if (outbuffer_socket[i] > max) {
357       max = outbuffer_socket[i];
358     }
359   }
360
361   tv.tv_sec = 0;
362   tv.tv_usec = 0;
363
364   result = select(max + 1, NULL, &set, NULL, &tv);
365   if (result <= 0) {
366     return;
367   }
368
369   for (i = 0; i < outbuffer_count; i++) {
370     if (FD_ISSET(outbuffer_socket[i], &set)) {
371       result = send(outbuffer_socket[i], outbuffer[i] + outbuffer_written[i], outbuffer_size[i] - outbuffer_written[i], 0);
372       if (result > 0) {
373         outbuffer_written[i] += result;
374       }
375
376       if (result <= 0 || outbuffer_written[i] == outbuffer_size[i]) {
377         /* close this socket and cleanup*/
378         close(outbuffer_socket[i]);
379         free(outbuffer[i]);
380
381         for (j = i + 1; j < outbuffer_count; j++) {
382           outbuffer[j - 1] = outbuffer[j];
383           outbuffer_size[j - 1] = outbuffer_size[j];
384           outbuffer_socket[j - 1] = outbuffer_socket[j];
385           outbuffer_written[j - 1] = outbuffer_written[j];
386         }
387         outbuffer_count--;
388       }
389     }
390   }
391   if (!outbuffer_count) {
392     olsr_stop_timer(writetimer_entry);
393   }
394 }
395
396 static void send_info(unsigned int send_what, int the_socket) {
397   struct autobuf abuf;
398
399   const char *content_type = (send_what & SIW_ALL) ? "application/json; charset=utf-8" : "text/plain; charset=utf-8";
400   int contentLengthPlaceholderStart = 0;
401   int headerLength = 0;
402
403   /* global variables for tracking when to put a comma in for JSON */
404   abuf_json_reset_entry_number_and_depth();
405
406   abuf_init(&abuf, 2 * 4096);
407
408   if (http_headers) {
409     build_http_header(PLUGIN_NAME, HTTP_200, content_type, &abuf, &contentLengthPlaceholderStart);
410     headerLength = abuf.len;
411   }
412
413   // only add if normal format
414   if (send_what & SIW_ALL) {
415     abuf_json_mark_output(true, &abuf);
416
417     abuf_json_int(&abuf, "systemTime", time(NULL));
418     abuf_json_int(&abuf, "timeSinceStartup", now_times);
419     if (*uuid)
420       abuf_json_string(&abuf, "uuid", uuid);
421
422     if ((send_what & SIW_LINKS) && printer_functions.links)
423       (*printer_functions.links)(&abuf);
424     if ((send_what & SIW_NEIGHBORS) && printer_functions.neighbors)
425       (*printer_functions.neighbors)(&abuf, false);
426     if ((send_what & SIW_TOPOLOGY) && printer_functions.topology)
427       (*printer_functions.topology)(&abuf);
428     if ((send_what & SIW_HNA) && printer_functions.hna)
429       (*printer_functions.hna)(&abuf);
430     if ((send_what & SIW_SGW) && printer_functions.sgw)
431       (*printer_functions.sgw)(&abuf);
432     if ((send_what & SIW_MID) && printer_functions.mid)
433       (*printer_functions.mid)(&abuf);
434     if ((send_what & SIW_ROUTES) && printer_functions.routes)
435       (*printer_functions.routes)(&abuf);
436     if ((send_what & SIW_GATEWAYS) && printer_functions.gateways)
437       (*printer_functions.gateways)(&abuf);
438     if ((send_what & SIW_CONFIG) && printer_functions.config)
439       (*printer_functions.config)(&abuf);
440     if ((send_what & SIW_INTERFACES) && printer_functions.interfaces)
441       (*printer_functions.interfaces)(&abuf);
442     if ((send_what & SIW_2HOP) && printer_functions.neighbors)
443       (*printer_functions.neighbors)(&abuf, true);
444     if ((send_what & SIW_VERSION) && printer_functions.version)
445       (*printer_functions.version)(&abuf);
446     if ((send_what & SIW_PLUGINS) && printer_functions.plugins)
447       (*printer_functions.plugins)(&abuf);
448
449     abuf_json_mark_output(false, &abuf);
450     abuf_puts(&abuf, "\n");
451   } else if ((send_what & SIW_OLSRD_CONF) && printer_functions.olsrd_conf) {
452     /* this outputs the olsrd.conf text directly, not normal format */
453     (*printer_functions.olsrd_conf)(&abuf);
454   }
455
456   if (http_headers) {
457     http_header_adjust_content_length(&abuf, contentLengthPlaceholderStart, abuf.len - headerLength);
458   }
459
460   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
461   outbuffer[outbuffer_count] = abuf.buf;
462   outbuffer_size[outbuffer_count] = abuf.len;
463   outbuffer_written[outbuffer_count] = 0;
464   outbuffer_socket[outbuffer_count] = the_socket;
465   abuf.buf = NULL;
466   abuf.len = 0;
467   abuf.size = 0;
468
469   outbuffer_count++;
470
471   if (outbuffer_count == 1) {
472     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &info_write_data, NULL, 0);
473   }
474
475   abuf_free(&abuf);
476 }
477
478 /*
479  * Local Variables:
480  * mode: c
481  * style: linux
482  * c-basic-offset: 2
483  * indent-tabs-mode: nil
484  * End:
485  */