1 /* EtherApe
2 * Copyright (C) 2001 Juan Toledo, Riccardo Ghetta
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <glib.h>
23 #include <gtk/gtk.h>
24 #include <netdb.h>
25 #include "datastructs.h"
26 #include "appdata.h"
27 #include "stats/util.h"
28
29 #define LINESIZE 1024
30
31
32 /************************************************************************
33 *
34 * services and port_service_t data and functions
35 *
36 ************************************************************************/
37 static GTree *service_names = NULL;
38 static GTree *tcp_services = NULL;
39 static GTree *udp_services = NULL;
40 static void services_fill_preferred(void);
41 static port_service_t *port_service_new(port_type_t port, const gchar *name);
42 static void port_service_free(port_service_t *);
43
44 /************************************************************************
45 *
46 * proto->color hash table support functions
47 *
48 ************************************************************************/
49 static GHashTable *protohash = NULL; /* the hash table containing proto,color pairs*/
50 static GList *cycle_color_list = NULL; /* the list of colors without protocol */
51 static GList *current_cycle = NULL; /* current ptr to free color */
52
53 /* adds or replaces the protoname item */
54 static gboolean protohash_set(gchar *protoname, const GdkRGBA *protocolor);
55
freehash(gpointer data)56 static void freehash(gpointer data)
57 {
58 g_free(data);
59 }
60
protohash_init(void)61 static gboolean protohash_init(void)
62 {
63 if (protohash)
64 return TRUE; /* already ok */
65
66 protohash = g_hash_table_new_full(g_str_hash,
67 g_str_equal,
68 freehash,
69 freehash);
70 return protohash != NULL;
71 }
72
73 /* clears the proto hash */
protohash_clear(void)74 void protohash_clear(void)
75 {
76 if (protohash) {
77 g_hash_table_destroy(protohash);
78 protohash = NULL;
79 }
80
81 while (cycle_color_list) {
82 g_free(cycle_color_list->data);
83 cycle_color_list = g_list_delete_link(cycle_color_list, cycle_color_list);
84 }
85 current_cycle = NULL;
86 }
87
88 /* adds or replaces the protoname item */
protohash_set(gchar * protoname,const GdkRGBA * protocolor)89 static gboolean protohash_set(gchar *protoname, const GdkRGBA *protocolor)
90 {
91 ColorHashItem item;
92
93 g_assert(protocolor);
94 if (!protohash && !protohash_init())
95 return FALSE;
96
97 item.color = *protocolor;
98
99 /* if a protocol is specified, we put the pair (proto,color) in the hash,
100 * marking it as preferred (a color obtained from user mappings) */
101 if (protoname && *protoname) {
102 item.preferred = TRUE;
103 g_hash_table_insert(protohash, g_strdup(protoname),
104 g_memdup(&item, sizeof(ColorHashItem)));
105 }
106
107 /* Without protocols defined we add the color to the cycle list. Cycle colors
108 aren't preferred */
109 if (!protoname || !*protoname) {
110 item.preferred = FALSE;
111 cycle_color_list = g_list_prepend(cycle_color_list,
112 g_memdup(&item, sizeof(ColorHashItem)));
113 current_cycle = cycle_color_list;
114 }
115
116 return TRUE;
117 }
118
119 /* resets the cycle color to start of list */
protohash_reset_cycle(void)120 void protohash_reset_cycle(void)
121 {
122 current_cycle = cycle_color_list;
123 }
124
125 /* returns the colorhash item from the named protocol, creating a new entry if
126 needed. Internal use only */
protohash_itemproto(const gchar * protoname)127 static const ColorHashItem *protohash_itemproto(const gchar *protoname)
128 {
129 const ColorHashItem *item;
130 g_assert(protoname); /* proto must be valid - note: empty IS valid, NULL no*/
131 g_assert(protohash);
132
133 item = (ColorHashItem *)g_hash_table_lookup(protohash, protoname);
134 if (!item) {
135 /* color not found, take from cycle list */
136 item = (ColorHashItem *)current_cycle->data;
137
138 /* add to hash */
139 g_hash_table_insert(protohash, g_strdup(protoname),
140 g_memdup(item, sizeof(ColorHashItem)));
141
142 /* advance cycle */
143 current_cycle = current_cycle->next;
144 if (!current_cycle)
145 current_cycle = cycle_color_list;
146 }
147 /* g_my_debug ("Protocol %s in color 0x%2.2x%2.2x%2.2x",
148 protoname, color->red, color->green, color->blue); */
149 return item;
150 }
151
protohash_color(const gchar * protoname)152 const GdkRGBA *protohash_color(const gchar *protoname)
153 {
154 g_assert(protoname); /* proto must be valid - note: empty IS valid, NULL no*/
155 g_assert(protohash);
156 return &(protohash_itemproto(protoname)->color);
157 }
158
159 /* returns the preferred flag */
protohash_is_preferred(const gchar * protoname)160 gboolean protohash_is_preferred(const gchar *protoname)
161 {
162 const ColorHashItem *item;
163 g_assert(protoname); /* proto must be valid - note: empty IS valid, NULL no*/
164 g_assert(protohash);
165
166 item = (ColorHashItem *)g_hash_table_lookup(protohash, protoname);
167 if (!item)
168 return FALSE;
169
170 return item->preferred;
171 }
172
173 /* fills the hash from a pref vector */
protohash_read_prefvect(gchar ** colors)174 gboolean protohash_read_prefvect(gchar * *colors)
175 {
176 int i;
177 GdkRGBA color;
178
179 protohash_clear();
180
181 /* fills with colors */
182 for (i = 0; colors[i]; ++i) {
183 gchar * *colors_protocols, * *protos;
184 int j;
185
186 colors_protocols = g_strsplit_set(colors[i], "; \t\n", 0);
187 if (!colors_protocols[0])
188 continue;
189
190 /* converting color */
191 gdk_rgba_parse(&color, colors_protocols[0]);
192
193 if (!colors_protocols[1] || !strlen(colors_protocols[1]))
194 protohash_set(colors_protocols[1], &color);
195 else {
196 /* multiple protos, split them */
197 protos = g_strsplit_set(colors_protocols[1], ", \t\n", 0);
198 for (j = 0; protos[j]; ++j)
199 if (protos[j] && *protos[j])
200 protohash_set(protos[j], &color);
201
202
203 g_strfreev(protos);
204 }
205 g_strfreev(colors_protocols);
206 }
207
208 if (!cycle_color_list) {
209 /* the list of color available for unmapped protocols is empty,
210 * so we add a grey */
211 gdk_rgba_parse(&color, "#7f7f7f");
212 protohash_set(NULL, &color);
213 }
214 else
215 cycle_color_list = g_list_reverse(cycle_color_list); /* list was reversed */
216
217 /* update preferred flag on services tree */
218 services_fill_preferred();
219 return TRUE;
220 }
221
222
223
224 /* compacts the array of colors/protocols mappings by collapsing identical
225 * colors - frees the input array */
protohash_compact(gchar ** colors)226 gchar **protohash_compact(gchar * *colors)
227 {
228 int i;
229 gchar * *compacted;
230 GList *work;
231 GList *el;
232
233 /* constructs a list with unique colors. We use a list to maintain the
234 fill order of the dialog. This is less surprising for the user. */
235 work = NULL;
236 for (i = 0; colors[i]; ++i) {
237 gchar * *colors_protocols;
238
239 colors_protocols = g_strsplit_set(colors[i], "; \t\n", 0);
240 if (!colors_protocols[0])
241 continue;
242
243 colors_protocols[1] = remove_spaces(colors_protocols[1]);
244
245 for (el = g_list_first(work); el; el = g_list_next(el)) {
246 gchar * *col = (gchar * *)(el->data);
247 if (!col || !col[0])
248 continue;
249 if (!g_ascii_strcasecmp(col[0], colors_protocols[0])) {
250 /* found same color, append protocol */
251 gchar *old = col[1];
252 if (colors_protocols[1] && *colors_protocols[1]) {
253 if (old)
254 col[1] = g_strjoin(",", old, colors_protocols[1], NULL);
255 else
256 col[1] = g_strdup(colors_protocols[1]);
257 g_free(old);
258 }
259 break;
260 }
261 }
262
263 if (el)
264 g_strfreev(colors_protocols); /* found, free temporary */
265 else {
266 /* color not found, adds to list - no need to free here */
267 work = g_list_prepend(work, colors_protocols);
268 }
269 }
270
271 /* reverse list to match original order (with GList, prepend+reverse is more
272 efficient than append */
273 work = g_list_reverse(work);
274
275 /* now scans the list filling the protostring */
276 compacted = malloc(sizeof(gchar *) * (g_list_length(work) + 1));
277 i = 0;
278 for (el = g_list_first(work); el; el = g_list_next(el)) {
279 gchar * *col = (gchar * *)(el->data);
280 compacted[i++] = g_strjoin(";", col[0], col[1], NULL);
281 g_strfreev(col);
282 }
283 compacted[i] = NULL;
284 g_list_free(work);
285 g_strfreev(colors);
286 return compacted;
287 }
288
289 /*
290 ***********************************************************************
291 *
292 * compacting function
293 *
294 ***********************************************************************
295 */
remove_spaces(gchar * str)296 gchar *remove_spaces(gchar *str)
297 {
298 char *out = str;
299 char *cur = str;
300 if (str) {
301 for (cur = str; *cur; ++cur)
302 if (!g_ascii_isspace((guchar)(*cur)))
303 *out++ = *cur;
304
305
306 *out = '\0';
307 }
308 return str;
309 }
310
311
312 /************************************************************************
313 *
314 * proto name mappers
315 *
316 ************************************************************************/
317
318 /* Comparison function to sort tcp/udp services by port number */
services_port_cmp(gconstpointer a,gconstpointer b,gpointer unused)319 static gint services_port_cmp(gconstpointer a, gconstpointer b, gpointer unused)
320 {
321 port_type_t port_a, port_b;
322
323 port_a = *(port_type_t *)a;
324 port_b = *(port_type_t *)b;
325
326 if (port_a > port_b)
327 return 1;
328 if (port_a < port_b)
329 return -1;
330 return 0;
331 } /* services_port_cmp */
332
333 /* Comparison function to sort service names */
services_name_cmp(gconstpointer a,gconstpointer b,gpointer unused)334 static gint services_name_cmp(gconstpointer a, gconstpointer b, gpointer unused)
335 {
336 return g_ascii_strcasecmp((const gchar *)a, (const gchar *)b);
337 }
338
services_tree_free(gpointer p)339 static void services_tree_free(gpointer p)
340 {
341 port_service_free((port_service_t *)p);
342 }
343
344 /* traverse function to map names to ports */
services_port_trv(gpointer key,gpointer value,gpointer data)345 static gboolean services_port_trv(gpointer key, gpointer value, gpointer data)
346 {
347 const port_service_t *svc = (const port_service_t *)value;
348 GTree *tree = (GTree *)data;
349 port_service_t *new_el;
350
351 new_el = port_service_new(svc->port, svc->name);
352 g_tree_replace(tree, new_el->name, new_el);
353 return FALSE;
354 }
355
356 /* traverse function to fill preferred field */
services_pref_trv(gpointer key,gpointer value,gpointer data)357 static gboolean services_pref_trv(gpointer key, gpointer value, gpointer data)
358 {
359 port_service_t *svc = (port_service_t *)value;
360 svc->preferred = protohash_is_preferred(svc->name);
361 return FALSE;
362 }
services_fill_preferred(void)363 static void services_fill_preferred(void)
364 {
365 if (udp_services)
366 g_tree_foreach(udp_services, services_pref_trv, NULL);
367 if (tcp_services)
368 g_tree_foreach(tcp_services, services_pref_trv, NULL);
369 }
370
services_init(void)371 void services_init(void)
372 {
373 struct servent *ent;
374 port_service_t *port_service;
375
376 g_assert(!service_names && !tcp_services && !udp_services);
377
378 service_names = g_tree_new_full(services_name_cmp, NULL, NULL, services_tree_free);
379 tcp_services = g_tree_new_full(services_port_cmp, NULL, NULL, services_tree_free);
380 udp_services = g_tree_new_full(services_port_cmp, NULL, NULL, services_tree_free);
381
382 while ((ent = getservent())) {
383 if (g_ascii_strcasecmp(ent->s_proto, "tcp") &&
384 g_ascii_strcasecmp(ent->s_proto, "udp"))
385 g_my_info(_("%s protocol not supported"), ent->s_proto);
386 else {
387 port_service = port_service_new(ntohs(ent->s_port), ent->s_name);
388 g_tree_replace(ent->s_proto[0] == 't' ? tcp_services : udp_services,
389 &port_service->port, port_service);
390 }
391 }
392
393 endservent();
394
395 /* now traverse port->name trees to fill the name->port tree */
396 g_tree_foreach(udp_services, services_port_trv, service_names);
397 g_tree_foreach(tcp_services, services_port_trv, service_names);
398
399 /* and finally assign preferred services */
400 services_fill_preferred();
401 }
402
services_clear(void)403 void services_clear(void)
404 {
405 if (service_names)
406 g_tree_destroy(service_names);
407 if (tcp_services)
408 g_tree_destroy(tcp_services);
409 if (udp_services)
410 g_tree_destroy(udp_services);
411 }
412
services_tcp_find(port_type_t port)413 const port_service_t *services_tcp_find(port_type_t port)
414 {
415 if (tcp_services)
416 return (port_service_t *)g_tree_lookup(tcp_services, &port);
417 else
418 return NULL;
419 }
420
services_udp_find(port_type_t port)421 const port_service_t *services_udp_find(port_type_t port)
422 {
423 if (udp_services)
424 return (port_service_t *)g_tree_lookup(udp_services, &port);
425 else
426 return NULL;
427 }
428
429 /************************************************************************
430 *
431 * port_service_t functions
432 *
433 ************************************************************************/
port_service_new(port_type_t port,const gchar * name)434 port_service_t *port_service_new(port_type_t port, const gchar *name)
435 {
436 port_service_t *p;
437 p = g_malloc(sizeof(port_service_t));
438 g_assert(p);
439
440 p->port = port;
441 p->name = g_ascii_strup(name, -1);
442 p->preferred = FALSE;
443 return p;
444 }
445
port_service_free(port_service_t * p)446 void port_service_free(port_service_t *p)
447 {
448 if (p)
449 g_free(p->name);
450 g_free(p);
451 }
452
services_port_find(const gchar * name)453 const port_service_t *services_port_find(const gchar *name)
454 {
455 if (!name || !service_names)
456 return NULL;
457
458 return (const port_service_t *)g_tree_lookup(service_names, name);
459 }
460