summaryrefslogtreecommitdiffstats
path: root/common/gdm-address.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/gdm-address.c')
-rw-r--r--common/gdm-address.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/common/gdm-address.c b/common/gdm-address.c
new file mode 100644
index 0000000..a8b73e2
--- /dev/null
+++ b/common/gdm-address.c
@@ -0,0 +1,551 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <string.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#ifndef G_OS_WIN32
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+#include <glib-object.h>
+
+#include "gdm-address.h"
+
+struct _GdmAddress
+{
+ struct sockaddr_storage *ss;
+};
+
+/* Register GdmAddress in the glib type system */
+GType
+gdm_address_get_type (void)
+{
+ static GType addr_type = 0;
+
+ if (addr_type == 0) {
+ addr_type = g_boxed_type_register_static ("GdmAddress",
+ (GBoxedCopyFunc) gdm_address_copy,
+ (GBoxedFreeFunc) gdm_address_free);
+ }
+
+ return addr_type;
+}
+
+/**
+ * gdm_address_get_family_type:
+ * @address: A pointer to a #GdmAddress
+ *
+ * Use this function to retrive the address family of @address.
+ *
+ * Return value: The address family of @address.
+ **/
+int
+gdm_address_get_family_type (GdmAddress *address)
+{
+ g_return_val_if_fail (address != NULL, -1);
+
+ return address->ss->ss_family;
+}
+
+
+/**
+ * gdm_address_new_from_sockaddr:
+ * @sa: A pointer to a sockaddr.
+ * @size: size of sockaddr in bytes.
+ *
+ * Creates a new #GdmAddress from @sa.
+ *
+ * Return value: The new #GdmAddress
+ * or %NULL if @sa was invalid or the address family isn't supported.
+ **/
+GdmAddress *
+gdm_address_new_from_sockaddr (struct sockaddr *sa,
+ size_t size)
+{
+ GdmAddress *addr;
+
+ g_return_val_if_fail (sa != NULL, NULL);
+ g_return_val_if_fail (size >= sizeof (struct sockaddr), NULL);
+ g_return_val_if_fail (size <= sizeof (struct sockaddr_storage), NULL);
+
+ addr = g_new0 (GdmAddress, 1);
+ addr->ss = g_new0 (struct sockaddr_storage, 1);
+ memcpy (addr->ss, sa, size);
+
+ return addr;
+}
+
+/**
+ * gdm_address_get_sockaddr_storage:
+ * @address: A #GdmAddress
+ *
+ * This function tanslates @address into a equivalent
+ * sockaddr_storage
+ *
+ * Return value: A newly allocated sockaddr_storage structure the caller must free
+ * or %NULL if @address did not point to a valid #GdmAddress.
+ **/
+struct sockaddr_storage *
+gdm_address_get_sockaddr_storage (GdmAddress *address)
+{
+ struct sockaddr_storage *ss;
+
+ g_return_val_if_fail (address != NULL, NULL);
+ g_return_val_if_fail (address->ss != NULL, NULL);
+
+ ss = g_memdup (address->ss, sizeof (struct sockaddr_storage));
+
+ return ss;
+}
+
+struct sockaddr_storage *
+gdm_address_peek_sockaddr_storage (GdmAddress *address)
+{
+ g_return_val_if_fail (address != NULL, NULL);
+
+ return address->ss;
+}
+
+static gboolean
+v4_v4_equal (const struct sockaddr_in *a,
+ const struct sockaddr_in *b)
+{
+ return a->sin_addr.s_addr == b->sin_addr.s_addr;
+}
+
+#ifdef ENABLE_IPV6
+static gboolean
+v6_v6_equal (struct sockaddr_in6 *a,
+ struct sockaddr_in6 *b)
+{
+ return IN6_ARE_ADDR_EQUAL (&a->sin6_addr, &b->sin6_addr);
+}
+#endif
+
+#define SA(__s) ((struct sockaddr *) __s)
+#define SIN(__s) ((struct sockaddr_in *) __s)
+#define SIN6(__s) ((struct sockaddr_in6 *) __s)
+
+gboolean
+gdm_address_equal (GdmAddress *a,
+ GdmAddress *b)
+{
+ guint8 fam_a;
+ guint8 fam_b;
+
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (a->ss != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+ g_return_val_if_fail (b->ss != NULL, FALSE);
+
+ fam_a = a->ss->ss_family;
+ fam_b = b->ss->ss_family;
+
+ if (fam_a == AF_INET && fam_b == AF_INET) {
+ return v4_v4_equal (SIN (a->ss), SIN (b->ss));
+ }
+#ifdef ENABLE_IPV6
+ else if (fam_a == AF_INET6 && fam_b == AF_INET6) {
+ return v6_v6_equal (SIN6 (a->ss), SIN6 (b->ss));
+ }
+#endif
+ return FALSE;
+}
+
+/* for debugging */
+static const char *
+address_family_str (GdmAddress *address)
+{
+ const char *str;
+ switch (address->ss->ss_family) {
+ case AF_INET:
+ str = "inet";
+ break;
+ case AF_INET6:
+ str = "inet6";
+ break;
+ case AF_UNIX:
+ str = "unix";
+ break;
+ case AF_UNSPEC:
+ str = "unspecified";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+ return str;
+}
+
+static void
+_gdm_address_debug (GdmAddress *address,
+ const char *host,
+ const char *port)
+{
+ g_debug ("Address family:%d (%s) host:%s port:%s local:%d loopback:%d",
+
+ address->ss->ss_family,
+ address_family_str (address) ? address_family_str (address) : "(null)",
+ host ? host : "(null)",
+ port ? port : "(null)",
+ gdm_address_is_local (address),
+ gdm_address_is_loopback (address));
+}
+
+void
+gdm_address_debug (GdmAddress *address)
+{
+ char *hostname = NULL;
+ char *host = NULL;
+ char *port = NULL;
+
+ gdm_address_get_numeric_info (address, &host, &port);
+
+ _gdm_address_debug (address, host, port);
+
+ g_free (hostname);
+ g_free (host);
+ g_free (port);
+}
+
+gboolean
+gdm_address_get_hostname (GdmAddress *address,
+ char **hostnamep)
+{
+ char host [NI_MAXHOST];
+ int res;
+ gboolean ret;
+
+ g_return_val_if_fail (address != NULL, FALSE);
+ g_return_val_if_fail (address->ss != NULL, FALSE);
+
+ ret = FALSE;
+
+ host [0] = '\0';
+ res = getnameinfo ((const struct sockaddr *)address->ss,
+ (int) gdm_sockaddr_len (address->ss),
+ host, sizeof (host),
+ NULL, 0,
+ 0);
+ if (res == 0) {
+ ret = TRUE;
+ goto done;
+ } else {
+ const char *err_msg;
+
+ err_msg = gai_strerror (res);
+ g_warning ("Unable to lookup hostname: %s",
+ err_msg ? err_msg : "(null)");
+ _gdm_address_debug (address, NULL, NULL);
+
+ }
+
+ /* try numeric? */
+
+ done:
+ if (hostnamep != NULL) {
+ *hostnamep = g_strdup (host);
+ }
+
+ return ret;
+}
+
+gboolean
+gdm_address_get_numeric_info (GdmAddress *address,
+ char **hostp,
+ char **servp)
+{
+ char host [NI_MAXHOST];
+ char serv [NI_MAXSERV];
+ int res;
+ gboolean ret;
+
+ g_return_val_if_fail (address != NULL, FALSE);
+ g_return_val_if_fail (address->ss != NULL, FALSE);
+
+ ret = FALSE;
+
+ host [0] = '\0';
+ serv [0] = '\0';
+ res = getnameinfo ((const struct sockaddr *)address->ss,
+ (int) gdm_sockaddr_len (address->ss),
+ host, sizeof (host),
+ serv, sizeof (serv),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (res != 0) {
+ const char *err_msg;
+
+ err_msg = gai_strerror (res);
+ g_warning ("Unable to lookup numeric info: %s",
+ err_msg ? err_msg : "(null)");
+ _gdm_address_debug (address, NULL, NULL);
+ } else {
+ ret = TRUE;
+ }
+
+ if (servp != NULL) {
+ if (g_str_has_prefix (serv, "::ffff:")) {
+ *servp = g_strdup (serv + 7);
+ } else {
+ *servp = g_strdup (serv);
+ }
+ }
+ if (hostp != NULL) {
+ if (g_str_has_prefix (host, "::ffff:")) {
+ *hostp = g_strdup (host + 7);
+ } else {
+ *hostp = g_strdup (host);
+ }
+ }
+
+ return ret;
+}
+
+gboolean
+gdm_address_is_loopback (GdmAddress *address)
+{
+ g_return_val_if_fail (address != NULL, FALSE);
+ g_return_val_if_fail (address->ss != NULL, FALSE);
+
+ switch (address->ss->ss_family){
+#ifdef AF_INET6
+ case AF_INET6:
+ return IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *)address->ss)->sin6_addr);
+ break;
+#endif
+ case AF_INET:
+ return (INADDR_LOOPBACK == htonl (((struct sockaddr_in *)address->ss)->sin_addr.s_addr));
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+add_local_siocgifconf (GList **list)
+{
+ struct ifconf ifc;
+ struct ifreq ifreq;
+ struct ifreq *ifr;
+ struct ifreq *the_end;
+ int sock;
+ char buf[BUFSIZ];
+
+ if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror ("socket");
+ return;
+ }
+
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+ perror ("SIOCGIFCONF");
+ close (sock);
+ return;
+ }
+
+ /* Get IP address of each active IP network interface. */
+ the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+
+ for (ifr = ifc.ifc_req; ifr < the_end; ifr++) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ /* IP net interface */
+ ifreq = *ifr;
+
+ if (ioctl (sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+ perror("SIOCGIFFLAGS");
+ } else if (ifreq.ifr_flags & IFF_UP) { /* active interface */
+ if (ioctl (sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
+ perror("SIOCGIFADDR");
+ } else {
+ GdmAddress *address;
+ address = gdm_address_new_from_sockaddr ((struct sockaddr *)&ifreq.ifr_addr,
+ sizeof (struct sockaddr));
+
+ *list = g_list_append (*list, address);
+ }
+ }
+ }
+
+ /* Support for variable-length addresses. */
+#ifdef HAS_SA_LEN
+ ifr = (struct ifreq *) ((caddr_t) ifr
+ + ifr->ifr_addr.sa_len - sizeof(struct sockaddr));
+#endif
+ }
+
+ close (sock);
+}
+
+static void
+add_local_addrinfo (GList **list)
+{
+ char hostbuf[BUFSIZ];
+ struct addrinfo *result;
+ struct addrinfo *res;
+ struct addrinfo hints;
+
+ hostbuf[BUFSIZ-1] = '\0';
+ if (gethostname (hostbuf, BUFSIZ-1) != 0) {
+ g_debug ("%s: Could not get server hostname, using localhost", "gdm_peek_local_address_list");
+ snprintf (hostbuf, BUFSIZ-1, "localhost");
+ }
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST;
+
+
+ g_debug ("GdmAddress: looking up hostname: %s", hostbuf);
+ result = NULL;
+ if (getaddrinfo (hostbuf, NULL, &hints, &result) != 0) {
+ g_debug ("%s: Could not get address from hostname!", "gdm_peek_local_address_list");
+
+ return;
+ }
+
+ for (res = result; res != NULL; res = res->ai_next) {
+ GdmAddress *address;
+
+ g_debug ("family=%d sock_type=%d protocol=%d flags=0x%x canonname=%s\n",
+ res->ai_family,
+ res->ai_socktype,
+ res->ai_protocol,
+ res->ai_flags,
+ res->ai_canonname ? res->ai_canonname : "(null)");
+ address = gdm_address_new_from_sockaddr (res->ai_addr, res->ai_addrlen);
+ *list = g_list_append (*list, address);
+ }
+
+ if (result != NULL) {
+ freeaddrinfo (result);
+ result = NULL;
+ }
+}
+
+const GList *
+gdm_address_peek_local_list (void)
+{
+ static GList *list = NULL;
+ static time_t last_time = 0;
+
+ /* Don't check more then every 5 seconds */
+ if (last_time + 5 > time (NULL)) {
+ return list;
+ }
+
+ g_list_foreach (list, (GFunc)gdm_address_free, NULL);
+ g_list_free (list);
+ list = NULL;
+
+ last_time = time (NULL);
+
+ add_local_siocgifconf (&list);
+ add_local_addrinfo (&list);
+
+ return list;
+}
+
+gboolean
+gdm_address_is_local (GdmAddress *address)
+{
+ const GList *list;
+
+ if (gdm_address_is_loopback (address)) {
+ return TRUE;
+ }
+
+ list = gdm_address_peek_local_list ();
+
+ while (list != NULL) {
+ GdmAddress *addr = list->data;
+
+ if (gdm_address_equal (address, addr)) {
+ return TRUE;
+ }
+
+ list = list->next;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gdm_address_copy:
+ * @address: A #GdmAddress.
+ *
+ * Duplicates @address.
+ *
+ * Return value: Duplicated @address or %NULL if @address was not valid.
+ **/
+GdmAddress *
+gdm_address_copy (GdmAddress *address)
+{
+ GdmAddress *addr;
+
+ g_return_val_if_fail (address != NULL, NULL);
+
+ addr = g_new0 (GdmAddress, 1);
+ addr->ss = g_memdup (address->ss, sizeof (struct sockaddr_storage));
+
+ return addr;
+}
+
+/**
+ * gdm_address_free:
+ * @address: A #GdmAddress.
+ *
+ * Frees the memory allocated for @address.
+ **/
+void
+gdm_address_free (GdmAddress *address)
+{
+ g_return_if_fail (address != NULL);
+
+ g_free (address->ss);
+ g_free (address);
+}
+