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