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