1 /* This is pretty messy because it is pretty much copied as is from
2  * ethereal. I should probably clean it up some day */
3 
4 
5 /* util.c
6  * Utility routines
7  *
8  * Original file by Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  * Later changes copyright 2016 Riccardo Ghetta
11  *
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27 
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <glib.h>
34 #include <common.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <math.h>
39 #include <errno.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
42 #endif
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 #ifdef HAVE_SYS_TYPES_H
47 #include <sys/types.h>
48 #endif
49 #ifdef HAVE_SYS_STAT_H
50 #include <sys/stat.h>
51 #endif
52 #ifdef HAVE_INET_NTOP
53 #include <arpa/inet.h>
54 #endif
55 #ifdef HAVE_NETINET_IN_H
56 #include <netinet/in.h>
57 #elif !defined(INET6_ADDRSTRLEN)
58 #define INET6_ADDRSTRLEN  46
59 #endif
60 #include <locale.h>
61 #include <assert.h>
62 
63 #include "appdata.h"
64 #include "util.h"
65 #include "traffic_stats.h"
66 
67 
68 /* safe strncpy */
safe_strncpy(char * dst,const char * src,size_t maxlen)69 char *safe_strncpy(char *dst, const char *src, size_t maxlen)
70 {
71   if (maxlen < 1)
72     return dst;
73   assert(src);
74   assert(dst);
75   strncpy(dst, src, maxlen - 1);       /* no need to copy that last char */
76   dst[maxlen - 1] = '\0';
77   return dst;
78 }
79 
80 /* safe strncat */
safe_strncat(char * dst,const char * src,size_t maxlen)81 char *safe_strncat(char *dst, const char *src, size_t maxlen)
82 {
83   assert(dst);
84   assert(src);
85   size_t lendst = strlen(dst);
86   if (lendst >= maxlen)
87     return dst; /* already full, nothing to do */
88   strncat(dst, src, maxlen - lendst);
89   dst[maxlen - 1] = '\0';
90   return dst;
91 }
92 
93 /* Next three functions copied directly from ethereal packet.c
94  * by Gerald Combs */
95 
96 /* Output has to be copied elsewhere */
ipv4_to_str(const guint8 * ad)97 const gchar *ipv4_to_str(const guint8 *ad)
98 {
99 #ifdef HAVE_INET_NTOP
100   static char buf[INET6_ADDRSTRLEN];
101   if (!inet_ntop(AF_INET, ad, buf, sizeof(buf)))
102     return "<invalid IPv4 address>";
103   return buf;
104 #else
105   static gchar str[3][16];
106   static gchar *cur;
107   gchar *p;
108   int i;
109   guint32 octet;
110   guint32 digit;
111 
112   if (cur == &str[0][0]) {
113     cur = &str[1][0];
114   }
115   else if (cur == &str[1][0]) {
116     cur = &str[2][0];
117   }
118   else {
119     cur = &str[0][0];
120   }
121   p = &cur[16];
122   *--p = '\0';
123   i = 3;
124   for (;;) {
125     octet = ad[i];
126     *--p = (octet % 10) + '0';
127     octet /= 10;
128     digit = octet % 10;
129     octet /= 10;
130     if (digit != 0 || octet != 0)
131       *--p = digit + '0';
132     if (octet != 0)
133       *--p = octet + '0';
134     if (i == 0)
135       break;
136     *--p = '.';
137     i--;
138   }
139   return p;
140 #endif
141 }                               /* ipv4_to_str */
142 
143 /* (toledo) This function I copied from capture.c of ethereal it was
144  * without comments, but I believe it keeps three different
145  * strings conversions in memory so as to try to make sure that
146  * the conversions made will be valid in memory for a longer
147  * period of time */
148 
149 /* Places char punct in the string as the hex-digit separator.
150  * If punct is '\0', no punctuation is applied (and thus
151  * the resulting string is 5 bytes shorter)
152  */
153 
ether_to_str_punct(const guint8 * ad,char punct)154 static const gchar *ether_to_str_punct(const guint8 *ad, char punct)
155 {
156   static gchar str[3][18];
157   static gchar *cur;
158   gchar *p;
159   int i;
160   guint32 octet;
161   static const gchar hex_digits[16] = "0123456789abcdef";
162 
163   if (cur == &str[0][0]) {
164     cur = &str[1][0];
165   }
166   else if (cur == &str[1][0]) {
167     cur = &str[2][0];
168   }
169   else {
170     cur = &str[0][0];
171   }
172   p = &cur[18];
173   *--p = '\0';
174   i = 5;
175   for (;;) {
176     octet = ad[i];
177     *--p = hex_digits[octet & 0xF];
178     octet >>= 4;
179     *--p = hex_digits[octet & 0xF];
180     if (i == 0)
181       break;
182     if (punct)
183       *--p = punct;
184     i--;
185   }
186   return p;
187 }                               /* ether_to_str_punct */
188 
189 /* Wrapper for the most common case of asking
190  * for a string using a colon as the hex-digit separator.
191  */
ether_to_str(const guint8 * ad)192 const gchar *ether_to_str(const guint8 *ad)
193 {
194   return ether_to_str_punct(ad, ':');
195 }                               /* ether_to_str */
196 
197 /*
198  * These functions are for IP/IPv6 handling
199  */
ipv6_to_str(const guint8 * ad)200 const gchar *ipv6_to_str(const guint8 *ad)
201 {
202   if (!ad)
203     return "<null addr>";
204 #ifdef HAVE_INET_NTOP
205   static char buf[INET6_ADDRSTRLEN];
206   if (!inet_ntop(AF_INET6, ad, buf, sizeof(buf)))
207     return "<invalid IPv6 address>";
208   return buf;
209 #else
210   static gchar str[3][40];
211   static gchar *cur;
212   gchar *p;
213   int i;
214   guint32 octet;
215   static const gchar hex_digits[16] = "0123456789abcdef";
216 
217   if (cur == &str[0][0]) {
218     cur = &str[1][0];
219   }
220   else if (cur == &str[1][0]) {
221     cur = &str[2][0];
222   }
223   else {
224     cur = &str[0][0];
225   }
226   p = &cur[40];
227   *--p = '\0';
228   i = 15;
229   for (;;) {
230     octet = ad[i];
231     *--p = hex_digits[octet & 0xF];
232     octet >>= 4;
233     *--p = hex_digits[octet & 0xF];
234     i--;
235     octet = ad[i];
236     *--p = hex_digits[octet & 0xF];
237     octet >>= 4;
238     *--p = hex_digits[octet & 0xF];
239     if (i == 0)
240       break;
241     *--p = ':';
242     i--;
243   }
244   return p;
245 #endif
246 }                               /* ipv6_to_str */
247 
address_to_str(const address_t * ad)248 const gchar *address_to_str(const address_t *ad)
249 {
250   if (!ad)
251     return "<null addr>";
252   switch (ad->type)
253   {
254       case AF_INET:
255         return ipv4_to_str(ad->addr_v4);
256       case AF_INET6:
257         return ipv6_to_str(ad->addr_v6);
258       default:
259         return "<invalid address family>";
260   }
261 }                               /* address_to_str */
262 
type_to_str(const address_t * ad)263 const gchar *type_to_str(const address_t *ad)
264 {
265   if (!ad)
266     return "<null addr>";
267   switch (ad->type)
268   {
269       case AF_INET:
270         return "IP";
271       case AF_INET6:
272         return "IPv6";
273       default:
274         return "<invalid address family>";
275   }
276 }                               /* type_to_str */
277 
strict_strtol(const char * str,int base,long * val)278 int strict_strtol(const char *str, int base, long *val)
279 {
280   char *end;
281   *val = strtol(str, &end, base);
282   return (*str && !*end) ? 0 : EINVAL;
283 }
284 
285 /* Like memcp(3), but bitwise (big-endian at the sub-byte level) */
bitwise_memcmp(const void * a,const void * b,size_t nbits)286 int bitwise_memcmp(const void *a, const void *b, size_t nbits)
287 {
288   int ret;
289   unsigned char a_last, b_last, mask;
290   size_t wholebytes = nbits / CHAR_BIT, rembits = nbits % CHAR_BIT;
291 
292   ret = memcmp(a, b, wholebytes);
293   if (ret)
294     return ret;
295 
296   mask = ~(rembits ? (1 << (CHAR_BIT - rembits)) - 1 : 0xFF);
297   a_last = *((unsigned char *)a + wholebytes) & mask;
298   b_last = *((unsigned char *)b + wholebytes) & mask;
299 
300   return a_last - b_last;
301 }
302 
303 /* returns a newly allocated string with a timeval in human readable form */
timeval_to_str(struct timeval last_heard)304 gchar *timeval_to_str(struct timeval last_heard)
305 {
306   gchar *str;
307   struct timeval diff;
308   struct tm broken_time;
309 
310   diff = subtract_times(appdata.now, last_heard);
311   if (diff.tv_sec <= 60) {
312     /* Meaning "n seconds" ago */
313     return g_strdup_printf(_("%ld\" ago"), (long)diff.tv_sec);
314   }
315 
316   if (diff.tv_sec < 600) {
317     /* Meaning "m minutes, n seconds ago" */
318     return g_strdup_printf(_("%ld'%ld\" ago"),
319                            (long)floor((double)diff.tv_sec / 60),
320                            (long)diff.tv_sec % 60);
321   }
322 
323   if (!localtime_r((time_t *)&(last_heard.tv_sec), &broken_time)) {
324     g_my_critical("Time conversion failed in timeval_to_str");
325     return NULL;
326   }
327 
328   if (diff.tv_sec < 3600 * 24)
329     str = g_strdup_printf("%d:%d", broken_time.tm_hour, broken_time.tm_min);
330   else {
331     /* Watch out! The first is month, the second day of the month */
332     str = g_strdup_printf("%d/%d %d:%d",
333                           broken_time.tm_mon, broken_time.tm_mday,
334                           broken_time.tm_hour, broken_time.tm_min);
335   }
336 
337   return str;
338 }                               /* timeval_to_str */
339 
340 
341 /************************************************
342  *
343  * xml helpers
344  *
345  *************************************************/
xmlescape(const gchar * str)346 gchar *xmlescape(const gchar *str)
347 {
348   gchar *escaped;
349   gchar *cur;
350 
351   size_t maxlen = strlen(str) * 4 + 8;
352   g_assert(maxlen < 16392);     //  just to avoid abnormal strings
353 
354   escaped = g_malloc(maxlen);  // allow worst case space
355   cur = escaped;
356   while (*str != '\0') {
357     switch (*str) {
358       case '&':
359         *cur++ = '&';
360         *cur++ = 'a';
361         *cur++ = 'm';
362         *cur++ = 'p';
363         *cur++ = ';';
364         break;
365       case '<':
366         *cur++ = '&';
367         *cur++ = 'l';
368         *cur++ = 't';
369         *cur++ = ';';
370         break;
371       default:
372         *cur++ = *str;
373         break;
374     }
375     ++str;
376   }
377   *cur = '\0';
378   return escaped;
379 }
380 
381 
382 /* returns a new string containing the named tag */
xmltag(const gchar * name,const gchar * fmt,...)383 gchar *xmltag(const gchar *name, const gchar *fmt, ...)
384 {
385   gchar *msg;
386   gchar *xml;
387   va_list ap;
388   va_start(ap, fmt);
389   msg = g_strdup_vprintf(fmt, ap);
390   va_end(ap);
391   xml = g_strdup_printf("<%s>%s</%s>\n", name, msg, name);
392   g_free(msg);
393   return xml;
394 }
395 
396 /* returns a new string containing the named tag with the string data escaped */
xmltag_escaped(const gchar * name,const gchar * fmt,...)397 gchar *xmltag_escaped(const gchar *name, const gchar *fmt, ...)
398 {
399   gchar *msg;
400   gchar *escaped;
401   gchar *xml;
402   va_list ap;
403   va_start(ap, fmt);
404   msg = g_strdup_vprintf(fmt, ap);
405   va_end(ap);
406   escaped = xmlescape(msg);
407   xml = g_strdup_printf("<%s>%s</%s>\n", name, escaped, name);
408   g_free(escaped);
409   g_free(msg);
410   return xml;
411 }
412 
413