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