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