]> git.meshlink.io Git - catta/blob - avahi-common/util.c
* add proper error codes and patch everything to make use of it
[catta] / avahi-common / util.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34
35 #include "util.h"
36
37 gchar *avahi_get_host_name(void) {
38 #ifdef HOST_NAME_MAX
39     char t[HOST_NAME_MAX];
40 #else
41     char t[256];
42 #endif
43     gethostname(t, sizeof(t));
44     t[sizeof(t)-1] = 0;
45     return avahi_normalize_name(t);
46 }
47
48 static gchar *unescape_uneeded(const gchar *src, gchar *ret_dest, size_t size) {
49     gboolean escaped = FALSE;
50     
51     g_assert(src);
52     g_assert(ret_dest);
53     g_assert(size > 0);
54     
55     for (; *src; src++) {
56
57         if (!escaped && *src == '\\')
58             escaped = TRUE;
59         else if (escaped && (*src == '.' || *src == '\\')) {
60
61             if ((size -= 2) <= 1) break;
62             
63             *(ret_dest++) = '\\';
64             *(ret_dest++) = *src;
65             escaped = FALSE;
66         } else {
67             if (--size <= 1) break;
68
69             *(ret_dest++) = *src;
70             escaped = FALSE;
71         }
72
73     }
74
75     *ret_dest = 0;
76     
77     return ret_dest;
78 }
79
80 gchar *avahi_normalize_name(const gchar *s) {
81     gchar tmp[256];
82     guint l;
83     g_assert(s);
84
85     unescape_uneeded(s, tmp, sizeof(tmp));
86
87     l = strlen(tmp);
88
89     while (l > 0 && tmp[l-1] == '.')
90         tmp[--l] = 0;
91
92     return g_strdup(tmp);
93 }
94
95 gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) {
96     g_assert(a);
97     g_assert(b);
98
99     if (a->tv_sec < b->tv_sec)
100         return -1;
101
102     if (a->tv_sec > b->tv_sec)
103         return 1;
104
105     if (a->tv_usec < b->tv_usec)
106         return -1;
107
108     if (a->tv_usec > b->tv_usec)
109         return 1;
110
111     return 0;
112 }
113
114 AvahiUsec avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) {
115     g_assert(a);
116     g_assert(b);
117
118     if (avahi_timeval_compare(a, b) < 0)
119         return - avahi_timeval_diff(b, a);
120
121     return ((AvahiUsec) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec;
122 }
123
124 GTimeVal* avahi_timeval_add(GTimeVal *a, AvahiUsec usec) {
125     AvahiUsec u;
126     g_assert(a);
127
128     u = usec + a->tv_usec;
129
130     if (u < 0) {
131         a->tv_usec = (glong) (1000000 + (u % 1000000));
132         a->tv_sec += (glong) (-1 + (u / 1000000));
133     } else {
134         a->tv_usec = (glong) (u % 1000000);
135         a->tv_sec += (glong) (u / 1000000);
136     }
137
138     return a;
139 }
140
141 AvahiUsec avahi_age(const GTimeVal *a) {
142     GTimeVal now;
143     
144     g_assert(a);
145
146     g_get_current_time(&now);
147
148     return avahi_timeval_diff(&now, a);
149 }
150
151 GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) {
152     g_assert(tv);
153
154     g_get_current_time(tv);
155
156     if (msec)
157         avahi_timeval_add(tv, (AvahiUsec) msec*1000);
158
159     if (jitter)
160         avahi_timeval_add(tv, (AvahiUsec) g_random_int_range(0, jitter) * 1000);
161         
162     return tv;
163 }
164
165 gint avahi_set_cloexec(gint fd) {
166     gint n;
167
168     g_assert(fd >= 0);
169     
170     if ((n = fcntl(fd, F_GETFD)) < 0)
171         return -1;
172
173     if (n & FD_CLOEXEC)
174         return 0;
175
176     return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
177 }
178
179 gint avahi_set_nonblock(gint fd) {
180     gint n;
181
182     g_assert(fd >= 0);
183
184     if ((n = fcntl(fd, F_GETFL)) < 0)
185         return -1;
186
187     if (n & O_NONBLOCK)
188         return 0;
189
190     return fcntl(fd, F_SETFL, n|O_NONBLOCK);
191 }
192
193 gint avahi_wait_for_write(gint fd) {
194     fd_set fds;
195     gint r;
196     
197     FD_ZERO(&fds);
198     FD_SET(fd, &fds);
199     
200     if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) {
201         g_message("select() failed: %s", strerror(errno));
202
203         return -1;
204     }
205     
206     g_assert(r > 0);
207
208     return 0;
209 }
210
211 /* Read the first label from string *name, unescape "\" and write it to dest */
212 gchar *avahi_unescape_label(const gchar **name, gchar *dest, guint size) {
213     guint i = 0;
214     gchar *d;
215     
216     g_assert(dest);
217     g_assert(size > 0);
218     g_assert(name);
219
220     if (!**name)
221         return NULL;
222
223     d = dest;
224     
225     for (;;) {
226         if (i >= size)
227             return NULL;
228
229         if (**name == '.') {
230             (*name)++;
231             break;
232         }
233         
234         if (**name == 0)
235             break;
236         
237         if (**name == '\\') {
238             (*name) ++;
239             
240             if (**name == 0)
241                 break;
242         }
243         
244         *(d++) = *((*name) ++);
245         i++;
246     }
247
248     g_assert(i < size);
249
250     *d = 0;
251
252     return dest;
253 }
254
255 /* Escape "\" and ".", append \0 */
256 gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, guint *ret_size) {
257     gchar *r;
258
259     g_assert(src);
260     g_assert(ret_name);
261     g_assert(*ret_name);
262     g_assert(ret_size);
263     g_assert(*ret_size > 0);
264
265     r = *ret_name;
266
267     while (src_length > 0) {
268         if (*src == '.' || *src == '\\') {
269             if (*ret_size < 3)
270                 return NULL;
271             
272             *((*ret_name) ++) = '\\';
273             (*ret_size) --;
274         }
275
276         if (*ret_size < 2)
277             return NULL;
278         
279         *((*ret_name)++) = *src;
280         (*ret_size) --;
281
282         src_length --;
283         src++;
284     }
285
286     **ret_name = 0;
287
288     return r;
289 }
290
291 gboolean avahi_domain_equal(const gchar *a, const gchar *b) {
292     g_assert(a);
293     g_assert(b);
294
295     if (a == b)
296         return TRUE;
297     
298     for (;;) {
299         gchar ca[65], cb[65], *pa, *pb;
300
301         pa = avahi_unescape_label(&a, ca, sizeof(ca));
302         pb = avahi_unescape_label(&b, cb, sizeof(cb));
303
304         if (!pa && !pb)
305             return TRUE;
306         else if ((pa && !pb) || (!pa && pb))
307             return FALSE;
308
309         if (g_ascii_strcasecmp(pa, pb))
310             return FALSE;
311     }
312
313     return TRUE;
314 }
315
316 gint avahi_binary_domain_cmp(const gchar *a, const gchar *b) {
317     g_assert(a);
318     g_assert(b);
319
320     if (a == b)
321         return 0;
322
323     for (;;) {
324         gchar ca[65], cb[65], *pa, *pb;
325         gint r;
326
327         pa = avahi_unescape_label(&a, ca, sizeof(ca));
328         pb = avahi_unescape_label(&b, cb, sizeof(cb));
329
330         if (!pa && !pb)
331             return 0;
332         else if (pa && !pb)
333             return 1;
334         else if (!pa && pb)
335             return -1;
336         
337         if ((r = strcmp(pa, pb)))
338             return r;
339     }
340 }
341
342 void avahi_hexdump(gconstpointer p, guint size) {
343     const guint8 *c = p;
344     g_assert(p);
345
346     printf("Dumping %u bytes from %p:\n", size, p);
347     
348     while (size > 0) {
349         guint i;
350
351         for (i = 0; i < 16; i++) { 
352             if (i < size)
353                 printf("%02x ", c[i]);
354             else
355                 printf("   ");
356         }
357
358         for (i = 0; i < 16; i++) {
359             if (i < size)
360                 printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.');
361             else
362                 printf(" ");
363         }
364         
365         printf("\n");
366
367         c += 16;
368
369         if (size <= 16)
370             break;
371         
372         size -= 16;
373     }
374 }
375
376 guint avahi_domain_hash(const gchar *s) {
377     guint hash = 0;
378     
379     for (;;) {
380         gchar c[65], *m;
381
382         if (!avahi_unescape_label(&s, c, sizeof(c)))
383             return hash;
384
385         if (!c[0])
386             continue;
387         
388         m = g_ascii_strdown(c, -1);
389         hash += g_str_hash(m);
390         g_free(m);
391     }
392 }
393
394 gchar *avahi_format_mac_address(const guint8* mac, guint size) {
395     gchar *r, *t;
396     guint i;
397     static const gchar hex[] = "0123456789abcdef";
398
399     t = r = g_new(gchar, size > 0 ? size*3 : 1);
400
401     if (size <= 0) {
402         *r = 0;
403         return r;
404     }
405     
406     for (i = 0; i < size; i++) {
407         *(t++) = hex[*mac >> 4];
408         *(t++) = hex[*mac & 0xF];
409         *(t++) = ':';
410
411         mac++;
412     }
413
414     *(--t) = 0;
415     return r;
416 }
417
418 gboolean avahi_valid_service_type(const gchar *t) {
419     const gchar *p;
420     g_assert(t);
421
422     if (strlen(t) < 5)
423         return FALSE;
424     
425     if (*t != '_')
426         return FALSE;
427
428     if (!(p = strchr(t, '.')))
429         return FALSE;
430
431     if (p - t > 63 || p - t < 2)
432         return FALSE;
433
434     if (*(++p) != '_')
435         return FALSE;
436
437     if (strchr(p, '.'))
438         return FALSE;
439
440     if (strlen(p) > 63 || strlen(p) < 2)
441         return FALSE;
442     
443     return TRUE;
444 }
445
446 gboolean avahi_valid_domain_name(const gchar *t) {
447     const gchar *p, *dp;
448     gboolean dot = FALSE;
449         
450     g_assert(t);
451
452     if (*t == 0)
453         return FALSE;
454
455     /* Domains may not start with a dot */
456     if (*t == '.')
457         return FALSE;
458
459     dp = t; 
460
461     for (p = t; *p; p++) {
462
463         if (*p == '.') {
464             if (dot) /* Two subsequent dots */
465                 return FALSE;
466
467             if (p - dp > 63)
468                 return FALSE;
469
470             dot = TRUE;
471             dp = p + 1;
472         } else
473             dot = FALSE;
474
475     }
476
477     if (p - dp > 63)
478         return FALSE;
479
480     /* A trailing dot IS allowed */
481     
482     return TRUE;
483 }
484
485 gboolean avahi_valid_service_name(const gchar *t) {
486     g_assert(t);
487
488     if (*t == 0)
489         return FALSE;
490
491     if (strlen(t) > 63)
492         return FALSE;
493
494     return TRUE;
495 }
496
497 gboolean avahi_valid_host_name(const gchar *t) {
498     g_assert(t);
499
500     if (*t == 0)
501         return FALSE;
502
503     if (strlen(t) > 63)
504         return FALSE;
505
506     if (strchr(t, '.'))
507         return FALSE;
508
509     return TRUE;
510 }