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