Fix rahunas netsnmp module crash from invalid records
[rahunas] / netsnmp-module / rhsnmp.c
1 /* RahuNAS SNMP Agent
2  *
3  * License: BSD
4  * Copyright (C) 2011 Neutron Soutmun <neutron@rahunas.org>
5  */
6
7 #include <sys/inotify.h>
8 #include <sys/types.h>
9 #include <fcntl.h>
10 #include <time.h>
11 #include <net-snmp/net-snmp-config.h>
12 #include <net-snmp/net-snmp-includes.h>
13 #include <net-snmp/agent/net-snmp-agent-includes.h>
14 #include <pthread.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <sqlite3.h>
19 #include "rhsnmp.h"
20
21 pthread_mutex_t rh_mtx = PTHREAD_MUTEX_INITIALIZER;
22 netsnmp_table_data_set *table_set = NULL;
23 oid rh_reg_oid[]       = { 1, 3, 6, 1, 4, 1, 38668, 1, 1 };
24 int stop_module;
25
26 /* Override table_data */
27 static int                   rh_netsnmp_register_table_data (
28                                netsnmp_handler_registration *reginfo,
29                                netsnmp_table_data *table,
30                                netsnmp_table_registration_info *table_info);
31
32 static netsnmp_mib_handler * rh_netsnmp_get_table_data_handler (
33                                netsnmp_table_data *table);
34
35 static int                   rh_netsnmp_table_data_helper_handler (
36                                netsnmp_mib_handler *handler,
37                                netsnmp_handler_registration *reginfo,
38                                netsnmp_agent_request_info *reqinfo,
39                                netsnmp_request_info *requests);
40
41 /* Override table_dataset */
42 static int                   rh_netsnmp_register_table_data_set (
43                                netsnmp_handler_registration *reginfo,
44                                netsnmp_table_data_set *data_set,
45                                netsnmp_table_registration_info *table_info);
46
47
48 /* RahuNAS Data Update*/
49 static void * rh_update_data (void *ptr);
50 static void   rh_remove_old_data_set (netsnmp_table_data_set *table_set);
51 static void   rh_add_new_data_set (netsnmp_table_data_set *table_set);
52
53 /* Override table_data implementation */
54 static int
55 rh_netsnmp_register_table_data (
56   netsnmp_handler_registration *reginfo,
57   netsnmp_table_data *table,
58   netsnmp_table_registration_info *table_info)
59 {
60   netsnmp_inject_handler (reginfo, rh_netsnmp_get_table_data_handler (table));
61   return netsnmp_register_table (reginfo, table_info);
62 }
63
64 static netsnmp_mib_handler *
65 rh_netsnmp_get_table_data_handler (netsnmp_table_data *table)
66 {
67   netsnmp_mib_handler *ret = NULL;
68
69   if (!table) {
70       snmp_log(LOG_INFO,
71                "netsnmp_get_table_data_handler(NULL) called\n");
72       return NULL;
73   }
74
75   ret =
76       netsnmp_create_handler(TABLE_DATA_NAME,
77                              rh_netsnmp_table_data_helper_handler);
78   if (ret) {
79       ret->flags |= MIB_HANDLER_AUTO_NEXT;
80       ret->myvoid = (void *) table;
81   }
82
83   return ret;
84 }
85
86 static int
87 rh_netsnmp_table_data_helper_handler (
88   netsnmp_mib_handler *handler,
89   netsnmp_handler_registration *reginfo,
90   netsnmp_agent_request_info *reqinfo,
91   netsnmp_request_info *requests)
92 {
93   int ret = 0;
94
95   pthread_mutex_lock (&rh_mtx);
96   ret = netsnmp_table_data_helper_handler (handler, reginfo, reqinfo, requests);
97   pthread_mutex_unlock (&rh_mtx);
98
99   return ret;
100 }
101
102
103 /* Override table_dataset implementation */
104 static int
105 rh_netsnmp_register_table_data_set (
106   netsnmp_handler_registration *reginfo,
107   netsnmp_table_data_set *data_set,
108   netsnmp_table_registration_info *table_info)
109 {
110     if (NULL == table_info) {
111         /* allocate the table if one wasn't allocated */
112         table_info = SNMP_MALLOC_TYPEDEF (netsnmp_table_registration_info);
113     }
114
115     if (NULL == table_info->indexes && data_set->table->indexes_template) {
116         /* copy the indexes in */
117         table_info->indexes =
118             snmp_clone_varbind (data_set->table->indexes_template);
119     }
120
121     if ((!table_info->min_column || !table_info->max_column) &&
122         (data_set->default_row)) {
123         /* determine min/max columns */
124         unsigned int    mincol = 0xffffffff, maxcol = 0;
125         netsnmp_table_data_set_storage *row;
126
127         for (row = data_set->default_row; row; row = row->next) {
128             mincol = SNMP_MIN (mincol, row->column);
129             maxcol = SNMP_MAX (maxcol, row->column);
130         }
131         if (!table_info->min_column)
132             table_info->min_column = mincol;
133         if (!table_info->max_column)
134             table_info->max_column = maxcol;
135     }
136
137     netsnmp_inject_handler (reginfo,
138                             netsnmp_get_table_data_set_handler (data_set));
139     return rh_netsnmp_register_table_data (reginfo, data_set->table,
140                                            table_info);
141 }
142
143 /* Agent Initializing */
144 void
145 init_rahunas (void)
146 {
147   pthread_t upd_thread;
148
149   stop_module = 0;
150
151   DEBUGMSGTL (("RahuNAS SNMP Agent", "Initializing.\n"));
152
153   table_set = netsnmp_create_table_data_set ("rahunasAuthenLoginTable");
154   table_set->allow_creation = 1;
155
156   netsnmp_table_dataset_add_index (table_set, ASN_OCTET_STR);
157   netsnmp_table_set_multi_add_default_row (table_set,
158                                            2, ASN_OCTET_STR, 0, NULL, 0,
159                                            3, ASN_OCTET_STR, 0, NULL, 0,
160                                            4, ASN_INTEGER,   0, NULL, 0,
161                                            0);
162
163   rh_netsnmp_register_table_data_set(netsnmp_create_handler_registration
164                                   ("rahunasAuthenLoginTable", NULL,
165                                    rh_reg_oid,
166                                    OID_LENGTH(rh_reg_oid),
167                                    HANDLER_CAN_RWRITE), table_set, NULL);
168
169   if (pthread_create (&upd_thread, NULL, rh_update_data,
170         (void *) table_set) != 0)
171     {
172       DEBUGMSGTL(("RahuNAS SNMP Agent", "Start update data thread fail!"));
173       exit (EXIT_FAILURE);
174     }
175   else
176     {
177       DEBUGMSGTL(("RahuNAS SNMP Agent", "Start update data thread success!"));
178       pthread_detach (upd_thread);
179     }
180
181   DEBUGMSGTL (("RahuNAS SNMP Agent", "Done initializing.\n"));
182 }
183
184 void
185 deinit_rahunas (void)
186 {
187   stop_module = 1;
188   unregister_mib (rh_reg_oid, OID_LENGTH(rh_reg_oid));
189 }
190
191 /* Update data thread worker */
192 static void *
193 rh_update_data (void *ptr)
194 {
195   netsnmp_table_data_set *table_set = (netsnmp_table_data_set *) ptr;
196   int  inotify_fd     = -1;
197   int  inotify_wfd    = -1;
198   char inotify_buffer[32 * (sizeof (struct inotify_event) + 16)];
199   int  inotify_enable = 0;
200   fd_set rfds;
201
202   inotify_fd = inotify_init ();
203   if (inotify_fd >= 0)
204     {
205       if (fcntl (inotify_fd, F_SETFL, O_NONBLOCK) == 0)
206         {
207           inotify_wfd = inotify_add_watch (inotify_fd, RAHUNAS_DB,
208                                            IN_CLOSE_WRITE);
209           if (inotify_wfd >= 0)
210             {
211               inotify_enable = 1;
212               FD_ZERO (&rfds);
213               FD_SET  (inotify_fd, &rfds);
214             }
215         }
216     }
217
218   while (!stop_module)
219     {
220       struct timeval timeout;
221       int  retval;
222       ssize_t r;
223
224       pthread_mutex_lock (&rh_mtx);
225       rh_remove_old_data_set (table_set);
226       rh_add_new_data_set (table_set);
227       pthread_mutex_unlock (&rh_mtx);
228
229       if (inotify_enable)
230         {
231           timeout.tv_sec  = 30;
232           timeout.tv_usec = 0;
233
234           retval = select (1, &rfds, NULL, NULL, &timeout);
235
236           if (retval == -1)
237             {
238               /* Fallback to sleep */
239               sleep (30);
240             }
241           else if (retval)
242             {
243               while ((r = read (inotify_fd, inotify_buffer,
244                      sizeof (inotify_buffer))))
245                 {
246                   /* Read until no data, r == -1 expected: errno == EAGAIN */
247                   if (r == -1)
248                     break;
249                 }
250             }
251           else
252             {
253               /* Timeout, fall-through */
254             }
255
256         }
257       else
258         {
259           sleep (30);
260         }
261     }
262
263   if (inotify_enable)
264     {
265       inotify_rm_watch (inotify_fd, inotify_wfd);
266       close (inotify_fd);
267     }
268
269   pthread_exit (NULL);
270 }
271
272 static void
273 rh_remove_old_data_set (netsnmp_table_data_set *table_set)
274 {
275   netsnmp_table_row *row = NULL;
276
277   while ((row = netsnmp_table_data_set_get_first_row (table_set)))
278     {
279       netsnmp_table_dataset_remove_and_delete_row (table_set, row);
280     }
281 }
282
283
284 static void
285 rh_add_new_data_set (netsnmp_table_data_set *table_set)
286 {
287   sqlite3 *db     = NULL;
288   char *zErrMsg   = NULL;
289   char **azResult = NULL;
290   char *colname   = NULL;
291   char *value     = NULL;
292   int nRow;
293   int nColumn;
294   int row_offset = 1;
295   int rc;
296   char sql[] = "SELECT * FROM dbset";
297
298   rc = sqlite3_open (RAHUNAS_DB, &db);
299   if (rc)
300     {
301       sqlite3_close (db);
302       DEBUGMSGTL (("RahuNAS SNMP Agent", "Could not open database.\n"));
303       exit (EXIT_FAILURE);
304     }
305
306   rc = sqlite3_get_table (db, sql, &azResult, &nRow, &nColumn, &zErrMsg);
307   if (rc != SQLITE_OK)
308     {
309       DEBUGMSGTL (("RahuNAS SNMP Agent", zErrMsg));
310       goto out;
311     }
312
313   while (row_offset < (nRow + 1))
314     {
315       int i = 0;
316       netsnmp_table_row *row = NULL;
317
318       row = netsnmp_create_table_data_row ();
319
320       for (i = 0; i < nColumn; i++)
321         {
322           colname = azResult[i];
323           value   = azResult[(row_offset * nColumn) + i];
324
325           if (!value)
326             {
327                netsnmp_table_data_delete_row (row);
328                goto skip;
329             }
330
331           if (strcmp (colname, "session_id") == 0)
332             {
333               netsnmp_table_row_add_index (row, ASN_OCTET_STR, value,
334                                            strlen (value));
335             }
336           else if (strcmp (colname, "username") == 0)
337             {
338               netsnmp_set_row_column      (row, 2, ASN_OCTET_STR, value,
339                                            strlen (value));
340             }
341           else if (strcmp (colname, "ip") == 0)
342             {
343               netsnmp_set_row_column      (row, 3, ASN_OCTET_STR, value,
344                                            strlen (value));
345             }
346           else if (strcmp (colname, "session_start") == 0)
347             {
348               u_long tmpul;
349               char *endptr = NULL;
350
351               endptr = strchr (value, '.');
352               if (endptr)
353                 *endptr = '\0';
354
355               endptr = NULL;
356               tmpul = strtol (value, &endptr, 10);
357
358               if (*endptr != '\0')
359                 tmpul = 0;
360
361               netsnmp_set_row_column      (row, 4, ASN_INTEGER, (char *) &tmpul,
362                                            sizeof (tmpul));
363             }
364         }
365
366       netsnmp_table_dataset_add_row (table_set, row);
367
368 skip:
369       row_offset++;
370     }
371
372   sqlite3_free_table (azResult);
373
374 out:
375   sqlite3_close (db);
376 }