1 /*
2 * jnettop, network online traffic visualiser
3 * Copyright (C) 2002-2005 Jakub Skopal
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Header: /cvsroot/jnettop/jnettop/jresolver.c,v 1.5 2006/04/12 07:47:01 merunka Exp $
20 *
21 */
22
23 #include "jbase.h"
24 #include "jutil.h"
25 #include "jresolver.h"
26
27 gboolean jresolver_IsEnabled;
28
29 GHashTable *resolverCache;
30 GMutex *resolverCacheMutex;
31 GThreadPool *resolverThreadPool;
32
33 GMutex *gethostbyaddrMutex;
34
35 GPtrArray *resolverTypes;
36
37 ResolvedNotifyFunc jresolver_ResolvedNotifyFunc;
38
jresolver_Lookup(int af,const jbase_mutableaddress * address)39 jbase_resolv_entry *jresolver_Lookup(int af, const jbase_mutableaddress *address) {
40 jbase_resolv_entry key;
41 jbase_resolv_entry *rentry;
42
43 memcpy(&key.addr, address, sizeof(jbase_mutableaddress));
44 key.name = NULL;
45 key.af = af;
46 g_mutex_lock(resolverCacheMutex);
47 rentry = g_hash_table_lookup(resolverCache, &key);
48 if (rentry == NULL) {
49 rentry = g_new0(jbase_resolv_entry, 1);
50 memcpy(rentry, &key, sizeof(key));
51 g_hash_table_insert(resolverCache, rentry, rentry);
52 g_mutex_unlock(resolverCacheMutex);
53 if (jresolver_IsEnabled)
54 g_thread_pool_push(resolverThreadPool, rentry, NULL);
55 } else {
56 g_mutex_unlock(resolverCacheMutex);
57 }
58
59 return rentry;
60 }
61
resolveNormal(jbase_resolv_entry * entry)62 static gboolean resolveNormal(jbase_resolv_entry *entry) {
63 gchar buffer[4096];
64 struct hostent shentry, *hentry;
65 int e, ret, size;
66 gchar *name;
67 gboolean found = FALSE;
68
69 #if !HAVE_GETHOSTBYADDR_R_8 && !HAVE_GETHOSTBYADDR_7
70 g_mutex_lock(gethostbyaddrMutex);
71 #endif
72 ret = 0; e=0;
73 size = entry->af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr);
74 #if HAVE_GETHOSTBYADDR_R_8
75 ret = gethostbyaddr_r(&entry->addr, size, entry->af, &shentry, buffer, 4096, &hentry, &e);
76 #elif HAVE_GETHOSTBYADDR_R_7
77 hentry = gethostbyaddr_r(&entry->addr, size, entry->af, &shentry, buffer, 4096, &e);
78 #else
79 hentry = gethostbyaddr(&entry->addr, size, entry->af);
80 #endif
81 if (!hentry || ret || e) {
82 goto resolverfailed;
83 }
84 name = g_strdup(hentry->h_name);
85 entry->name = name;
86 found = TRUE;
87 resolverfailed:
88 #if !HAVE_GETHOSTBYADDR_R_8 && !HAVE_GETHOSTBYADDR_7
89 g_mutex_unlock(gethostbyaddrMutex);
90 #else
91 name = name; // dummy to avoid deprecated warning on linux
92 #endif
93 return found;
94 }
95
resolveExternal(char * lookupScriptPath,jbase_resolv_entry * entry)96 static gboolean resolveExternal(char *lookupScriptPath, jbase_resolv_entry *entry) {
97 gchar *outname;
98 gint outstatus;
99 gchar **argv = (gchar **) g_new0(gchar *, 2);
100 gchar *buffer = (gchar *) g_new0(gchar, 256);
101 jutil_Address2String(entry->af, &entry->addr, buffer, 256);
102 argv[0] = lookupScriptPath;
103 argv[1] = buffer;
104 if (g_spawn_sync(NULL, argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, &outname, NULL, &outstatus, NULL)) {
105 if (!WEXITSTATUS(outstatus)) {
106 g_strdelimit(outname, "\r\n\t", '\0');
107 if (strlen(outname) > 0) {
108 entry->name = outname;
109 return TRUE;
110 }
111 // strip name
112 }
113 }
114 g_free(buffer);
115 return FALSE;
116 }
117
118 #ifdef SUPPORT_DB4
119
120 DB *cacheDb = NULL;
121
initializeCache()122 static gboolean initializeCache() {
123 int err;
124 if ((err = db_create(&cacheDb, NULL, 0))) {
125 debug(LOG_NOTICE, "db_create: Cannot create database environment (%s)", db_strerror(err));
126 goto initialize_cleanup;
127 }
128 if ((err = cacheDb->set_alloc(cacheDb, (void*(*)(size_t))g_malloc, (void*(*)(void*,size_t))g_realloc, (void(*)(void*))g_free))) {
129 debug(LOG_NOTICE, "db->set_alloc: Cannot set allocation functions to DB4 (%s)", db_strerror(err));
130 goto initialize_cleanup;
131 }
132 if ((err = cacheDb->open(cacheDb, NULL, "/var/cache/jnettop/dns.cache", NULL, DB_HASH, DB_AUTO_COMMIT || DB_CREATE || DB_THREAD, 0))) {
133 debug(LOG_NOTICE, "db->open: Cannot open/create database (%s)", db_strerror(err));
134 goto initialize_cleanup;
135 }
136
137 return TRUE;
138
139 initialize_cleanup:
140 if (cacheDb != NULL) {
141 cacheDb->close(cacheDb, 0);
142 cacheDb = NULL;
143 }
144 return FALSE;
145 }
146
resolveFromCache(jbase_resolv_entry * entry)147 static gboolean resolveFromCache(jbase_resolv_entry *entry) {
148 int err;
149 DBT keyThang, valueThang;
150
151 if (cacheDb == NULL)
152 return FALSE;
153
154 memset(&keyThang, '\0', sizeof(DBT));
155 memset(&valueThang, '\0', sizeof(DBT));
156
157 keyThang.data = &entry->addr;
158 keyThang.size = JBASE_AF_SIZE(entry->af);
159 keyThang.ulen = keyThang.size;
160
161 valueThang.data = NULL;
162 valueThang.flags = DB_DBT_MALLOC;
163
164 err = cacheDb->get(cacheDb, NULL, &keyThang, &valueThang, 0);
165
166 if (err == DB_NOTFOUND) {
167 return FALSE;
168 }
169
170 if (err) {
171 debug(LOG_NOTICE, "db->get: Cannot get record from database (%s)", db_strerror(err));
172 return FALSE;
173 }
174
175 entry->name = (gchar *)valueThang.data;
176 debug(LOG_DEBUG, "resolved from cache: %s", entry->name);
177 return TRUE;
178 }
179
storeToCache(jbase_resolv_entry * entry)180 static void storeToCache(jbase_resolv_entry *entry) {
181 int err;
182 DBT keyThang, valueThang;
183
184 if (cacheDb == NULL)
185 return;
186
187 memset(&keyThang, '\0', sizeof(DBT));
188 memset(&valueThang, '\0', sizeof(DBT));
189
190 keyThang.data = &entry->addr;
191 keyThang.size = JBASE_AF_SIZE(entry->af);
192 keyThang.ulen = keyThang.size;
193
194 valueThang.data = g_strdup(entry->name);
195 valueThang.size = strlen(entry->name) + 1;
196 valueThang.ulen = valueThang.size;
197
198 if ((err = cacheDb->put(cacheDb, NULL, &keyThang, &valueThang, 0))) {
199 debug(LOG_NOTICE, "db->put: Cannot put record to database (%s)", db_strerror(err));
200 }
201
202 g_free(valueThang.data);
203 }
204
shutdownCache()205 static void shutdownCache() {
206 int err;
207
208 if (cacheDb == NULL)
209 return;
210
211 if ((err = cacheDb->close(cacheDb, 0))) {
212 debug(LOG_NOTICE, "db->close: Cannot close database (%s)", db_strerror(err));
213 }
214
215 cacheDb = NULL;
216 }
217
218 #else
219
initializeCache()220 static gboolean initializeCache() {
221 return FALSE;
222 }
223
resolveFromCache(jbase_resolv_entry * entry)224 static gboolean resolveFromCache(jbase_resolv_entry *entry) {
225 return FALSE;
226 }
227
storeToCache(jbase_resolv_entry * entry)228 static void storeToCache(jbase_resolv_entry *entry) {
229 }
230
shutdownCache()231 static void shutdownCache() {
232 }
233
234 #endif
235
resolverThreadFunc(gpointer task,gpointer user_data)236 void resolverThreadFunc(gpointer task, gpointer user_data) {
237 int i;
238 jbase_resolv_entry *entry = (jbase_resolv_entry *)task;
239
240 if (resolveFromCache(entry)) {
241 goto resolver_done_nocache;
242 }
243
244 for (i=0; i<resolverTypes->len; i++) {
245 jresolver_resolvertype *type = (jresolver_resolvertype *)g_ptr_array_index(resolverTypes, i);
246
247 if (jutil_IsInNetwork(&entry->addr, entry->af, &type->value, &type->mask, type->af)) {
248 switch (type->lookupType) {
249 case LOOKUPTYPE_EXTERNAL:
250 if (resolveExternal(type->externalLookupScript, entry))
251 goto resolver_done;
252 break;
253 case LOOKUPTYPE_NORMAL:
254 if (resolveNormal(entry))
255 goto resolver_done;
256 break;
257 }
258 }
259 }
260
261 resolveNormal(entry);
262
263 resolver_done:
264 if (entry->name != NULL) {
265 storeToCache(entry);
266 }
267
268 resolver_done_nocache:
269 if (entry->name != NULL && jresolver_ResolvedNotifyFunc != NULL) {
270 jresolver_ResolvedNotifyFunc(entry);
271 }
272 }
273
addZeroResolves()274 static void addZeroResolves() {
275 jbase_resolv_entry *entry;
276 entry = g_new0(jbase_resolv_entry, 1);
277 entry->name = "UNKNOWNv4";
278 entry->af = AF_INET;
279 entry->addr.addr4.s_addr = 0x0;
280 g_hash_table_insert(resolverCache, entry, entry);
281 entry = g_new0(jbase_resolv_entry, 1);
282 entry->name = "UNKNOWNv6";
283 entry->af = AF_INET6;
284 entry->addr.addr6.ntop_s6_addr32[0] = 0x0;
285 entry->addr.addr6.ntop_s6_addr32[1] = 0x0;
286 entry->addr.addr6.ntop_s6_addr32[2] = 0x0;
287 entry->addr.addr6.ntop_s6_addr32[3] = 0x0;
288 g_hash_table_insert(resolverCache, entry, entry);
289 entry = g_new0(jbase_resolv_entry, 1);
290 entry->name = "AGGREGATEDv4";
291 entry->af = AF_INET;
292 entry->addr.addr4.s_addr = htonl(0x01000000);
293 g_hash_table_insert(resolverCache, entry, entry);
294 entry = g_new0(jbase_resolv_entry, 1);
295 entry->name = "AGGREGATEDv6";
296 entry->af = AF_INET6;
297 entry->addr.addr6.ntop_s6_addr32[0] = 0x0;
298 entry->addr.addr6.ntop_s6_addr32[1] = 0x0;
299 entry->addr.addr6.ntop_s6_addr32[2] = 0x0;
300 entry->addr.addr6.ntop_s6_addr32[3] = htonl(0x01000000);
301 g_hash_table_insert(resolverCache, entry, entry);
302 }
303
hashResolvEntry(gconstpointer key)304 static guint hashResolvEntry(gconstpointer key) {
305 const jbase_resolv_entry *resolv = key;
306 guint hash = 0;
307 hash = resolv->addr.addr6.ntop_s6_addr32[0];
308 hash ^= resolv->addr.addr6.ntop_s6_addr32[1];
309 hash ^= resolv->addr.addr6.ntop_s6_addr32[2];
310 hash ^= resolv->addr.addr6.ntop_s6_addr32[3];
311 return hash;
312 }
313
compareResolvEntry(gconstpointer a,gconstpointer b)314 static gboolean compareResolvEntry(gconstpointer a, gconstpointer b) {
315 jbase_resolv_entry *aa, *bb;
316 if(a == b) return 1;
317 aa = (jbase_resolv_entry *) a;
318 bb = (jbase_resolv_entry *) b;
319 if(aa->af != bb->af)
320 return 0;
321 return !memcmp(&aa->addr, &bb->addr, sizeof(aa->addr));
322 }
323
jresolver_AddExternalLookupScript(int af,const jbase_mutableaddress * mask,const jbase_mutableaddress * value,char * lookupScriptName)324 void jresolver_AddExternalLookupScript(int af, const jbase_mutableaddress *mask, const jbase_mutableaddress *value, char *lookupScriptName) {
325 jresolver_resolvertype *type = g_new0(jresolver_resolvertype, 1);
326 type->af = af;
327 memcpy(&type->mask, mask, sizeof(jbase_mutableaddress));
328 memcpy(&type->value, value, sizeof(jbase_mutableaddress));
329 type->lookupType = LOOKUPTYPE_EXTERNAL;
330 type->externalLookupScript = lookupScriptName;
331 g_ptr_array_add(resolverTypes, type);
332 }
333
jresolver_AddNormalLookup(int af,const jbase_mutableaddress * mask,const jbase_mutableaddress * value)334 void jresolver_AddNormalLookup(int af, const jbase_mutableaddress *mask, const jbase_mutableaddress *value) {
335 jresolver_resolvertype *type = g_new0(jresolver_resolvertype, 1);
336 type->af = af;
337 memcpy(&type->mask, mask, sizeof(jbase_mutableaddress));
338 memcpy(&type->value, value, sizeof(jbase_mutableaddress));
339 type->lookupType = LOOKUPTYPE_NORMAL;
340 g_ptr_array_add(resolverTypes, type);
341 }
342
jresolver_SetEnabled(gboolean isEnabled)343 void jresolver_SetEnabled(gboolean isEnabled) {
344 jresolver_IsEnabled = isEnabled;
345 }
346
jresolver_Setup()347 gboolean jresolver_Setup() {
348 resolverCache = g_hash_table_new((GHashFunc)hashResolvEntry, (GEqualFunc)compareResolvEntry);
349 resolverCacheMutex = g_mutex_new();
350 resolverThreadPool = g_thread_pool_new((GFunc)resolverThreadFunc, NULL, 5, FALSE, NULL);
351 resolverTypes = g_ptr_array_new();
352 gethostbyaddrMutex = g_mutex_new();
353
354 jresolver_IsEnabled = TRUE;
355
356 addZeroResolves();
357 return TRUE;
358 }
359
jresolver_SetResolvedNotifyFunc(ResolvedNotifyFunc resolvedNotifyFunction)360 void jresolver_SetResolvedNotifyFunc(ResolvedNotifyFunc resolvedNotifyFunction) {
361 jresolver_ResolvedNotifyFunc = resolvedNotifyFunction;
362 }
363
jresolver_Initialize()364 void jresolver_Initialize() {
365 initializeCache();
366 }
367
jresolver_Shutdown()368 void jresolver_Shutdown() {
369 shutdownCache();
370 }
371