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