~ubuntu-branches/ubuntu/oneiric/gdm3/oneiric

« back to all changes in this revision

Viewing changes to common/gdm-address.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2010-03-25 20:02:20 UTC
  • Revision ID: james.westby@ubuntu.com-20100325200220-12cap62s6p304nuh
Tags: upstream-2.29.92
ImportĀ upstreamĀ versionĀ 2.29.92

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
18
 *
 
19
 */
 
20
 
 
21
 
 
22
#include "config.h"
 
23
 
 
24
#include <stdlib.h>
 
25
#include <stdio.h>
 
26
#include <fcntl.h>
 
27
#include <unistd.h>
 
28
#ifdef HAVE_STROPTS_H
 
29
#include <stropts.h>
 
30
#endif
 
31
#include <string.h>
 
32
#ifdef HAVE_SYS_SOCKET_H
 
33
#include <sys/socket.h>
 
34
#endif
 
35
#ifdef HAVE_SYS_SOCKIO_H
 
36
#include <sys/sockio.h>
 
37
#endif
 
38
#include <netdb.h>
 
39
#include <sys/ioctl.h>
 
40
#include <net/if.h>
 
41
 
 
42
#ifndef G_OS_WIN32
 
43
#include <sys/select.h>
 
44
#include <netinet/in.h>
 
45
#include <arpa/inet.h>
 
46
#else
 
47
#include <winsock2.h>
 
48
#include <ws2tcpip.h>
 
49
#endif
 
50
 
 
51
#include <glib-object.h>
 
52
 
 
53
#include "gdm-address.h"
 
54
 
 
55
struct _GdmAddress
 
56
{
 
57
        struct sockaddr_storage *ss;
 
58
};
 
59
 
 
60
/* Register GdmAddress in the glib type system */
 
61
GType
 
62
gdm_address_get_type (void)
 
63
{
 
64
        static GType addr_type = 0;
 
65
 
 
66
        if (addr_type == 0) {
 
67
                addr_type = g_boxed_type_register_static ("GdmAddress",
 
68
                                                          (GBoxedCopyFunc) gdm_address_copy,
 
69
                                                          (GBoxedFreeFunc) gdm_address_free);
 
70
        }
 
71
 
 
72
        return addr_type;
 
73
}
 
74
 
 
75
/**
 
76
 * gdm_address_get_family_type:
 
77
 * @address: A pointer to a #GdmAddress
 
78
 *
 
79
 * Use this function to retrive the address family of @address.
 
80
 *
 
81
 * Return value: The address family of @address.
 
82
 **/
 
83
int
 
84
gdm_address_get_family_type (GdmAddress *address)
 
85
{
 
86
        g_return_val_if_fail (address != NULL, -1);
 
87
 
 
88
        return address->ss->ss_family;
 
89
}
 
90
 
 
91
 
 
92
/**
 
93
 * gdm_address_new_from_sockaddr:
 
94
 * @sa: A pointer to a sockaddr.
 
95
 * @size: size of sockaddr in bytes.
 
96
 *
 
97
 * Creates a new #GdmAddress from @sa.
 
98
 *
 
99
 * Return value: The new #GdmAddress
 
100
 * or %NULL if @sa was invalid or the address family isn't supported.
 
101
 **/
 
102
GdmAddress *
 
103
gdm_address_new_from_sockaddr (struct sockaddr *sa,
 
104
                               size_t           size)
 
105
{
 
106
        GdmAddress *addr;
 
107
 
 
108
        g_return_val_if_fail (sa != NULL, NULL);
 
109
        g_return_val_if_fail (size >= sizeof (struct sockaddr), NULL);
 
110
        g_return_val_if_fail (size <= sizeof (struct sockaddr_storage), NULL);
 
111
 
 
112
        addr = g_new0 (GdmAddress, 1);
 
113
        addr->ss = g_new0 (struct sockaddr_storage, 1);
 
114
        memcpy (addr->ss, sa, size);
 
115
 
 
116
        return addr;
 
117
}
 
118
 
 
119
/**
 
120
 * gdm_address_get_sockaddr_storage:
 
121
 * @address: A #GdmAddress
 
122
 *
 
123
 * This function tanslates @address into a equivalent
 
124
 * sockaddr_storage
 
125
 *
 
126
 * Return value: A newly allocated sockaddr_storage structure the caller must free
 
127
 * or %NULL if @address did not point to a valid #GdmAddress.
 
128
 **/
 
129
struct sockaddr_storage *
 
130
gdm_address_get_sockaddr_storage (GdmAddress *address)
 
131
{
 
132
        struct sockaddr_storage *ss;
 
133
 
 
134
        g_return_val_if_fail (address != NULL, NULL);
 
135
        g_return_val_if_fail (address->ss != NULL, NULL);
 
136
 
 
137
        ss = g_memdup (address->ss, sizeof (struct sockaddr_storage));
 
138
 
 
139
        return ss;
 
140
}
 
141
 
 
142
struct sockaddr_storage *
 
143
gdm_address_peek_sockaddr_storage (GdmAddress *address)
 
144
{
 
145
        g_return_val_if_fail (address != NULL, NULL);
 
146
 
 
147
        return address->ss;
 
148
}
 
149
 
 
150
static gboolean
 
151
v4_v4_equal (const struct sockaddr_in *a,
 
152
             const struct sockaddr_in *b)
 
153
{
 
154
        return a->sin_addr.s_addr == b->sin_addr.s_addr;
 
155
}
 
156
 
 
157
#ifdef ENABLE_IPV6
 
158
static gboolean
 
159
v6_v6_equal (struct sockaddr_in6 *a,
 
160
             struct sockaddr_in6 *b)
 
161
{
 
162
        return IN6_ARE_ADDR_EQUAL (&a->sin6_addr, &b->sin6_addr);
 
163
}
 
164
#endif
 
165
 
 
166
#define SA(__s)    ((struct sockaddr *) __s)
 
167
#define SIN(__s)   ((struct sockaddr_in *) __s)
 
168
#define SIN6(__s)  ((struct sockaddr_in6 *) __s)
 
169
 
 
170
gboolean
 
171
gdm_address_equal (GdmAddress *a,
 
172
                   GdmAddress *b)
 
173
{
 
174
        guint8 fam_a;
 
175
        guint8 fam_b;
 
176
 
 
177
        g_return_val_if_fail (a != NULL, FALSE);
 
178
        g_return_val_if_fail (a->ss != NULL, FALSE);
 
179
        g_return_val_if_fail (b != NULL, FALSE);
 
180
        g_return_val_if_fail (b->ss != NULL, FALSE);
 
181
 
 
182
        fam_a = a->ss->ss_family;
 
183
        fam_b = b->ss->ss_family;
 
184
 
 
185
        if (fam_a == AF_INET && fam_b == AF_INET) {
 
186
                return v4_v4_equal (SIN (a->ss), SIN (b->ss));
 
187
        }
 
188
#ifdef ENABLE_IPV6
 
189
        else if (fam_a == AF_INET6 && fam_b == AF_INET6) {
 
190
                return v6_v6_equal (SIN6 (a->ss), SIN6 (b->ss));
 
191
        }
 
192
#endif
 
193
        return FALSE;
 
194
}
 
195
 
 
196
/* for debugging */
 
197
static const char *
 
198
address_family_str (GdmAddress *address)
 
199
{
 
200
        const char *str;
 
201
        switch (address->ss->ss_family) {
 
202
        case AF_INET:
 
203
                str = "inet";
 
204
                break;
 
205
        case AF_INET6:
 
206
                str = "inet6";
 
207
                break;
 
208
        case AF_UNIX:
 
209
                str = "unix";
 
210
                break;
 
211
        case AF_UNSPEC:
 
212
                str = "unspecified";
 
213
                break;
 
214
        default:
 
215
                str = "unknown";
 
216
                break;
 
217
        }
 
218
        return str;
 
219
}
 
220
 
 
221
static void
 
222
_gdm_address_debug (GdmAddress *address, char *hostname, char *host, char *port)
 
223
{
 
224
        g_return_if_fail (address != NULL);
 
225
 
 
226
        hostname = NULL;
 
227
        host = NULL;
 
228
        port = NULL;
 
229
 
 
230
 
 
231
        g_debug ("Address family:%d (%s) hostname:%s host:%s port:%s local:%d loopback:%d",
 
232
                 address->ss->ss_family,
 
233
                 address_family_str (address) ? address_family_str (address) : "(null)",
 
234
                 hostname ? hostname : "(null)",
 
235
                 host ? host : "(null)",
 
236
                 port ? port : "(null)",
 
237
                 gdm_address_is_local (address),
 
238
                 gdm_address_is_loopback (address));
 
239
 
 
240
        g_free (hostname);
 
241
        g_free (host);
 
242
        g_free (port);
 
243
}
 
244
 
 
245
void
 
246
gdm_address_debug (GdmAddress *address)
 
247
{
 
248
        char *hostname;
 
249
        char *host;
 
250
        char *port;
 
251
 
 
252
        gdm_address_get_hostname (address, &hostname);
 
253
        gdm_address_get_numeric_info (address, &host, &port);
 
254
 
 
255
        _gdm_address_debug (address, hostname, host, port);
 
256
}
 
257
 
 
258
gboolean
 
259
gdm_address_get_hostname (GdmAddress *address,
 
260
                          char      **hostnamep)
 
261
{
 
262
        char     host [NI_MAXHOST];
 
263
        int      res;
 
264
        gboolean ret;
 
265
 
 
266
        g_return_val_if_fail (address != NULL, FALSE);
 
267
        g_return_val_if_fail (address->ss != NULL, FALSE);
 
268
 
 
269
        ret = FALSE;
 
270
 
 
271
        host [0] = '\0';
 
272
        res = getnameinfo ((const struct sockaddr *)address->ss,
 
273
                           (int) gdm_sockaddr_len (address->ss),
 
274
                           host, sizeof (host),
 
275
                           NULL, 0,
 
276
                           0);
 
277
        if (res == 0) {
 
278
                ret = TRUE;
 
279
                goto done;
 
280
        } else {
 
281
                const char *err_msg;
 
282
 
 
283
                err_msg = gai_strerror (res);
 
284
                g_warning ("Unable to lookup hostname: %s",
 
285
                        err_msg ? err_msg : "(null)");
 
286
                _gdm_address_debug (address, NULL, NULL, NULL);
 
287
        }
 
288
 
 
289
        /* try numeric? */
 
290
 
 
291
 done:
 
292
        if (hostnamep != NULL) {
 
293
                *hostnamep = g_strdup (host);
 
294
        }
 
295
 
 
296
        return ret;
 
297
}
 
298
 
 
299
gboolean
 
300
gdm_address_get_numeric_info (GdmAddress *address,
 
301
                              char      **hostp,
 
302
                              char      **servp)
 
303
{
 
304
        char     host [NI_MAXHOST];
 
305
        char     serv [NI_MAXSERV];
 
306
        int      res;
 
307
        gboolean ret;
 
308
 
 
309
        g_return_val_if_fail (address != NULL, FALSE);
 
310
        g_return_val_if_fail (address->ss != NULL, FALSE);
 
311
 
 
312
        ret = FALSE;
 
313
 
 
314
        host [0] = '\0';
 
315
        serv [0] = '\0';
 
316
        res = getnameinfo ((const struct sockaddr *)address->ss,
 
317
                           (int) gdm_sockaddr_len (address->ss),
 
318
                           host, sizeof (host),
 
319
                           serv, sizeof (serv),
 
320
                           NI_NUMERICHOST | NI_NUMERICSERV);
 
321
        if (res != 0) {
 
322
                const char *err_msg;
 
323
 
 
324
                err_msg = gai_strerror (res);
 
325
                g_warning ("Unable to lookup numeric info: %s",
 
326
                        err_msg ? err_msg : "(null)");
 
327
                _gdm_address_debug (address, NULL, NULL, NULL);
 
328
        } else {
 
329
                ret = TRUE;
 
330
        }
 
331
 
 
332
        if (servp != NULL) {
 
333
                *servp = g_strdup (serv);
 
334
        }
 
335
        if (hostp != NULL) {
 
336
                *hostp = g_strdup (host);
 
337
        }
 
338
 
 
339
        return ret;
 
340
}
 
341
 
 
342
gboolean
 
343
gdm_address_is_loopback (GdmAddress *address)
 
344
{
 
345
        g_return_val_if_fail (address != NULL, FALSE);
 
346
        g_return_val_if_fail (address->ss != NULL, FALSE);
 
347
 
 
348
        switch (address->ss->ss_family){
 
349
#ifdef  AF_INET6
 
350
        case AF_INET6:
 
351
                return IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *)address->ss)->sin6_addr);
 
352
                break;
 
353
#endif
 
354
        case AF_INET:
 
355
                return (INADDR_LOOPBACK == htonl (((struct sockaddr_in *)address->ss)->sin_addr.s_addr));
 
356
                break;
 
357
        default:
 
358
                break;
 
359
        }
 
360
 
 
361
        return FALSE;
 
362
}
 
363
 
 
364
static void
 
365
add_local_siocgifconf (GList **list)
 
366
{
 
367
        struct ifconf ifc;
 
368
        struct ifreq  ifreq;
 
369
        struct ifreq *ifr;
 
370
        struct ifreq *the_end;
 
371
        int           sock;
 
372
        char          buf[BUFSIZ];
 
373
 
 
374
        if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) < 0) {
 
375
                perror ("socket");
 
376
                return;
 
377
        }
 
378
 
 
379
        ifc.ifc_len = sizeof (buf);
 
380
        ifc.ifc_buf = buf;
 
381
        if (ioctl (sock, SIOCGIFCONF, (char *) &ifc) < 0) {
 
382
                perror ("SIOCGIFCONF");
 
383
                close (sock);
 
384
                return;
 
385
        }
 
386
 
 
387
        /* Get IP address of each active IP network interface. */
 
388
        the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
 
389
 
 
390
        for (ifr = ifc.ifc_req; ifr < the_end; ifr++) {
 
391
                if (ifr->ifr_addr.sa_family == AF_INET) {
 
392
                        /* IP net interface */
 
393
                        ifreq = *ifr;
 
394
 
 
395
                        if (ioctl (sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
 
396
                                perror("SIOCGIFFLAGS");
 
397
                        } else if (ifreq.ifr_flags & IFF_UP) {  /* active interface */
 
398
                                if (ioctl (sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
 
399
                                        perror("SIOCGIFADDR");
 
400
                                } else {
 
401
                                        GdmAddress *address;
 
402
                                        address = gdm_address_new_from_sockaddr ((struct sockaddr *)&ifreq.ifr_addr,
 
403
                                                                                 sizeof (struct sockaddr));
 
404
 
 
405
                                        gdm_address_debug (address);
 
406
 
 
407
                                        *list = g_list_append (*list, address);
 
408
                                }
 
409
                        }
 
410
                }
 
411
 
 
412
                /* Support for variable-length addresses. */
 
413
#ifdef HAS_SA_LEN
 
414
                ifr = (struct ifreq *) ((caddr_t) ifr
 
415
                                        + ifr->ifr_addr.sa_len - sizeof(struct sockaddr));
 
416
#endif
 
417
        }
 
418
 
 
419
        close (sock);
 
420
}
 
421
 
 
422
static void
 
423
add_local_addrinfo (GList **list)
 
424
{
 
425
        char             hostbuf[BUFSIZ];
 
426
        struct addrinfo *result;
 
427
        struct addrinfo *res;
 
428
        struct addrinfo  hints;
 
429
 
 
430
        hostbuf[BUFSIZ-1] = '\0';
 
431
        if (gethostname (hostbuf, BUFSIZ-1) != 0) {
 
432
                g_debug ("%s: Could not get server hostname, using localhost", "gdm_peek_local_address_list");
 
433
                snprintf (hostbuf, BUFSIZ-1, "localhost");
 
434
        }
 
435
 
 
436
        memset (&hints, 0, sizeof (hints));
 
437
        hints.ai_family = AF_UNSPEC;
 
438
        hints.ai_flags = AI_CANONNAME;
 
439
 
 
440
        g_debug ("GdmAddress: looking up hostname: %s", hostbuf);
 
441
        result = NULL;
 
442
        if (getaddrinfo (hostbuf, NULL, &hints, &result) != 0) {
 
443
                g_debug ("%s: Could not get address from hostname!", "gdm_peek_local_address_list");
 
444
 
 
445
                return;
 
446
        }
 
447
 
 
448
        for (res = result; res != NULL; res = res->ai_next) {
 
449
                GdmAddress *address;
 
450
 
 
451
                g_debug ("family=%d sock_type=%d protocol=%d flags=0x%x canonname=%s\n",
 
452
                         res->ai_family,
 
453
                         res->ai_socktype,
 
454
                         res->ai_protocol,
 
455
                         res->ai_flags,
 
456
                         res->ai_canonname ? res->ai_canonname : "(null)");
 
457
                address = gdm_address_new_from_sockaddr (res->ai_addr, res->ai_addrlen);
 
458
                *list = g_list_append (*list, address);
 
459
        }
 
460
 
 
461
        if (result != NULL) {
 
462
                freeaddrinfo (result);
 
463
                result = NULL;
 
464
        }
 
465
}
 
466
 
 
467
const GList *
 
468
gdm_address_peek_local_list (void)
 
469
{
 
470
        static GList  *list = NULL;
 
471
        static time_t  last_time = 0;
 
472
 
 
473
        /* Don't check more then every 5 seconds */
 
474
        if (last_time + 5 > time (NULL)) {
 
475
                return list;
 
476
        }
 
477
 
 
478
        g_list_foreach (list, (GFunc)gdm_address_free, NULL);
 
479
        g_list_free (list);
 
480
        list = NULL;
 
481
 
 
482
        last_time = time (NULL);
 
483
 
 
484
        add_local_siocgifconf (&list);
 
485
        add_local_addrinfo (&list);
 
486
 
 
487
        return list;
 
488
}
 
489
 
 
490
gboolean
 
491
gdm_address_is_local (GdmAddress *address)
 
492
{
 
493
        const GList *list;
 
494
 
 
495
        if (gdm_address_is_loopback (address)) {
 
496
                return TRUE;
 
497
        }
 
498
 
 
499
        list = gdm_address_peek_local_list ();
 
500
 
 
501
        while (list != NULL) {
 
502
                GdmAddress *addr = list->data;
 
503
 
 
504
                if (gdm_address_equal (address, addr)) {
 
505
                        return TRUE;
 
506
                }
 
507
 
 
508
                list = list->next;
 
509
        }
 
510
 
 
511
        return FALSE;
 
512
}
 
513
 
 
514
/**
 
515
 * gdm_address_copy:
 
516
 * @address: A #GdmAddress.
 
517
 *
 
518
 * Duplicates @address.
 
519
 *
 
520
 * Return value: Duplicated @address or %NULL if @address was not valid.
 
521
 **/
 
522
GdmAddress *
 
523
gdm_address_copy (GdmAddress *address)
 
524
{
 
525
        GdmAddress *addr;
 
526
 
 
527
        g_return_val_if_fail (address != NULL, NULL);
 
528
 
 
529
        addr = g_new0 (GdmAddress, 1);
 
530
        addr->ss = g_memdup (address->ss, sizeof (struct sockaddr_storage));
 
531
 
 
532
        return addr;
 
533
}
 
534
 
 
535
/**
 
536
 * gdm_address_free:
 
537
 * @address: A #GdmAddress.
 
538
 *
 
539
 * Frees the memory allocated for @address.
 
540
 **/
 
541
void
 
542
gdm_address_free (GdmAddress *address)
 
543
{
 
544
        g_return_if_fail (address != NULL);
 
545
 
 
546
        g_free (address->ss);
 
547
        g_free (address);
 
548
}
 
549