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