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