/* GNet - Networking library * Copyright (C) 2000 David Helder * Copyright (C) 2000 Andrew Lanoix * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "gnet-private.h" #include "inetaddr.h" #ifdef HAVE_LIBPTHREAD #include #endif /* **************************************** */ static gboolean gnet_gethostbyname (const char* hostname, struct sockaddr* sa, gchar** nicename); static gchar* gnet_gethostbyaddr(const char* addr, size_t length, int type); #ifdef ENABLE_IPV6 static int use_ipv6; #endif /* Testing stuff */ /* #undef HAVE_GETHOSTBYNAME_R_GLIBC */ /* #define HAVE_GETHOSTBYNAME_R_GLIB_MUTEX */ /* TODO: Move this to an init function */ #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX # ifndef G_THREADS_ENABLED # error Using GLib Mutex but thread are not enabled. # endif G_LOCK_DEFINE (gethostbyname); #endif #ifdef ENABLE_IPV6 static gboolean gnet_have_ipv6 (void) { int s; s = socket (AF_INET6, SOCK_STREAM, 0); if (s != -1) { close (s); return TRUE; } return FALSE; } #endif /* Thread safe gethostbyname. The only valid fields are sin_len, sin_family, and sin_addr. Nice name */ gboolean gnet_gethostbyname(const char* hostname, struct sockaddr* sa, gchar** nicename) { gboolean rv = FALSE; #ifdef ENABLE_IPV6 if (gnet_have_ipv6 ()) { struct addrinfo hints, *res, *result; result = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if (!getaddrinfo (hostname, NULL, &hints, &result)) { /* got the address*/ for (res = result ; res ; res= res->ai_next) if (res->ai_family == AF_INET6 || res->ai_family == AF_INET) break; if (res && sa) memcpy (sa, res->ai_addr, res->ai_addrlen); if (nicename && res && res->ai_canonname) *nicename = g_strdup (res->ai_canonname); freeaddrinfo (result); rv = TRUE; } } #endif #ifdef HAVE_GETHOSTBYNAME_R_GLIBC { struct hostent result_buf, *result; size_t len; char* buf; int herr; int res; len = 1024; buf = g_new(gchar, len); while ((res = gethostbyname_r (hostname, &result_buf, buf, len, &result, &herr)) == ERANGE) { len *= 2; buf = g_renew (gchar, buf, len); } if (res || result == NULL || result->h_addr_list[0] == NULL) goto done; if (sa) { ((struct sockaddr_in *)sa)->sin_family = result->h_addrtype; memcpy(&((struct sockaddr_in *)sa)->sin_addr, result->h_addr_list[0], result->h_length); } if (nicename && result->h_name) *nicename = g_strdup(result->h_name); rv = TRUE; done: g_free(buf); } #else #ifdef HAVE_GET_HOSTBYNAME_R_SOLARIS { struct hostent result; size_t len; char* buf; int herr; int res; len = 1024; buf = g_new(gchar, len); while ((res = gethostbyname_r (hostname, &result, buf, len, &herr)) == ERANGE) { len *= 2; buf = g_renew (gchar, buf, len); } if (res || hp == NULL || hp->h_addr_list[0] == NULL) goto done; if (sa) { ((struct sockaddr_in *)sa)->sin_family = result->h_addrtype; memcpy(&((struct sockaddr_in *)sa)->sin_addr, result->h_addr_list[0], result->h_length); } if (nicename && result->h_name) *nicename = g_strdup(result->h_name); rv = TRUE; done: g_free(buf); } #else #ifdef HAVE_GETHOSTBYNAME_R_HPUX { struct hostent result; struct hostent_data buf; int res; res = gethostbyname_r (hostname, &result, &buf); if (res == 0) { if (sa) { ((struct sockaddr_in *)sa)->sin_family = result.h_addrtype; memcpy(&((struct sockaddr_in *)sa)->sin_addr, result.h_addr_list[0], result.h_length); } if (nicename && result.h_name) *nicename = g_strdup(result.h_name); rv = TRUE; } } #else #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX { struct hostent* he; if (!g_threads_got_initialized) g_thread_init (NULL); G_LOCK (gethostbyname); he = gethostbyname(hostname); if (he != NULL && he->h_addr_list[0] != NULL) { if (sa) { ((struct sockaddr_in *)sa)->sin_family = he->h_addrtype; memcpy(&((struct sockaddr_in *)sa)->sin_addr, he->h_addr_list[0], he->h_length); } if (nicename && he->h_name) *nicename = g_strdup(he->h_name); rv = TRUE; } G_UNLOCK (gethostbyname); } #else #ifdef GNET_WIN32 { struct hostent *result; WaitForSingleObject(gnet_hostent_Mutex, INFINITE); result = gethostbyname(hostname); if (result != NULL) { if (sa) { sa->sin_family = result->h_addrtype; memcpy(&sa->sin_addr, result->h_addr_list[0], result->h_length); } if (nicename && result->h_name) *nicename = g_strdup(result->h_name); ReleaseMutex(gnet_hostent_Mutex); rv = TRUE; } } #else { struct hostent* he; he = gethostbyname(hostname); if (he != NULL && he->h_addr_list[0] != NULL) { if (sa) { ((struct sockaddr_in *)sa)->sin_family = he->h_addrtype; memcpy(&((struct sockaddr_in *)sa)->sin_addr, he->h_addr_list[0], he->h_length); } if (nicename && he->h_name) *nicename = g_strdup(he->h_name); rv = TRUE; } } #endif #endif #endif #endif #endif return rv; } /* Thread safe gethostbyaddr (we assume that gethostbyaddr_r follows the same pattern as gethostbyname_r, so we don't have special checks for it in configure.in. Returns: the hostname, NULL if there was an error. */ gchar* gnet_gethostbyaddr(const char* addr, size_t length, int type) { gchar* rv = NULL; #ifdef ENABLE_IPV6 if (gnet_have_ipv6 ()) { struct addrinfo hints, *result; char *name; result = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if (!getaddrinfo (addr, NULL, &hints, &result)) { if (result->ai_canonname) name = g_strdup (result->ai_canonname); freeaddrinfo (result); } } #endif #ifdef HAVE_GETHOSTBYNAME_R_GLIBC { struct hostent result_buf, *result; size_t len; char* buf; int herr; int res; len = 1024; buf = g_new(gchar, len); while ((res = gethostbyaddr_r (addr, length, type, &result_buf, buf, len, &result, &herr)) == ERANGE) { len *= 2; buf = g_renew (gchar, buf, len); } if (res || result == NULL || result->h_name == NULL) goto done; rv = g_strdup(result->h_name); done: g_free(buf); } #else #ifdef HAVE_GET_HOSTBYNAME_R_SOLARIS { struct hostent result; size_t len; char* buf; int herr; int res; len = 1024; buf = g_new(gchar, len); while ((res = gethostbyaddr_r (addr, lenght, type, &result, buf, len, &herr)) == ERANGE) { len *= 2; buf = g_renew (gchar, buf, len); } if (res || hp == NULL || hp->h_name == NULL) goto done; rv = g_strdup(result->h_name); done: g_free(buf); } #else #ifdef HAVE_GETHOSTBYNAME_R_HPUX { struct hostent result; struct hostent_data buf; int res; res = gethostbyaddr_r (addr, length, type, &result, &buf); if (res == 0) rv = g_strdup (result.h_name); } #else #ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX { struct hostent* he; if (!g_threads_got_initialized) g_thread_init (NULL); G_LOCK (gethostbyname); he = gethostbyaddr(addr, length, type); if (he != NULL && he->h_name != NULL) rv = g_strdup(he->h_name); G_UNLOCK (gethostbyname); } #else #ifdef GNET_WIN32 { struct hostent* he; WaitForSingleObject(gnet_hostent_Mutex, INFINITE); he = gethostbyaddr(addr, length, type); if (he != NULL && he->h_name != NULL) rv = g_strdup(he->h_name); ReleaseMutex(gnet_hostent_Mutex); } #else { struct hostent* he; he = gethostbyaddr(addr, length, type); if (he != NULL && he->h_name != NULL) rv = g_strdup(he->h_name); } #endif #endif #endif #endif #endif return rv; } /* **************************************** */ /* TO IMPLEMENT?: */ /*** * * Create an internet address from raw bytes. * **/ /* InetAddr* inetaddr_bytes_new(const guint8* addr, const gint length); */ /* **************************************** */ /** * gnet_inetaddr_new: * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name * (eg, 141.213.8.59). You can delete the after the function is called. * @port: port number (0 if the port doesn't matter) * * Create an internet address from a name and port. This function * may block. * * Returns: a new #GInetAddr, or NULL if there was a failure. * **/ GInetAddr* gnet_inetaddr_new (const gchar* name, gint port) { struct sockaddr_in* sa_in; struct in_addr inaddr; GInetAddr* ia = NULL; #ifdef ENABLE_IPV6 struct sockaddr_in6 *sa_in6; struct in6_addr in6addr; struct sockaddr_in6 addr6; #endif g_return_val_if_fail(name != NULL, NULL); ia = g_new0(GInetAddr, 1); ia->name = g_strdup (name); ia->ref_count = 1; /* Try to read the name as if were dotted decimal */ #ifdef ENABLE_IPV6 if (gnet_have_ipv6 ()) { memset (&addr6, 0, sizeof (addr6)); if (inet_pton (AF_INET6, name, &in6addr) > 0) { sa_in6 = (struct sockaddr_in6 *) &ia->sa; sa_in6->sin6_family = AF_INET6; sa_in6->sin6_port = g_htons (port); memcpy (&sa_in6->sin6_addr, &addr6.sin6_addr, sizeof (struct in6_addr)); } else if (gnet_gethostbyname (name, (struct sockaddr *) &addr6, NULL)) { /* Try to get the host address */ ia->name = g_strdup (name); if (addr6.sin6_family == AF_INET6) { sa_in6 = (struct sockaddr_in6 *) &ia->sa; sa_in6->sin6_family = AF_INET6; sa_in6->sin6_port = g_htons (port); memcpy (&sa_in6->sin6_addr, &addr6.sin6_addr, sizeof (struct in6_addr)); } else { sa_in = (struct sockaddr_in *) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = g_htons (port); memcpy (&sa_in->sin_addr, &((struct sockaddr_in *)&addr6)->sin_addr, sizeof (struct in_addr)); } } return ia; } else #endif if (inet_pton(AF_INET, name, &inaddr) > 0) { ia = g_new0(GInetAddr, 1); ia->ref_count = 1; sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = g_htons(port); memcpy(&sa_in->sin_addr, (char*) &inaddr, sizeof(struct in_addr)); } else { struct sockaddr_in sa; /* Try to get the host by name (ie, DNS) */ if (gnet_gethostbyname(name, (struct sockaddr *)&sa, NULL)) { ia = g_new0(GInetAddr, 1); ia->name = g_strdup(name); ia->ref_count = 1; sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = g_htons(port); memcpy(&sa_in->sin_addr, &sa.sin_addr, 4); } } return ia; } static void* gethostbyname_async_child (void* arg); /** * gnet_inetaddr_new_async: * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name * (eg, 141.213.8.59). You can delete the after the function is called. * @port: port number (0 if the port doesn't matter) * @func: Callback function. * @data: User data passed when callback function is called. * * Create a GInetAddr from a name and port asynchronously. Once the * structure is created, it will call the callback. It may call the * callback if there is a failure. * * The Unix version creates a pthread thread which does the lookup. * If pthreads aren't available, it forks and does the lookup. * Forking will be slow or even fail when using operating systems * that copy the entire process when forking. * * If you need to lookup hundreds of addresses, we recommend calling * g_main_iteration(FALSE) between calls. This will help prevent an * explosion of threads or processes. * * If you need a more robust library for Unix, look at GNU ADNS. * GNU ADNS is under the GNU GPL. This library does not use threads * or processes. * * The Windows version should work fine. Windows has an asynchronous * DNS lookup function. * * Returns: ID of the lookup which can be used with * gnet_inetaddr_new_async_cancel() to cancel it; NULL on failure. * **/ GInetAddrNewAsyncID gnet_inetaddr_new_async (const gchar* name, gint port, GInetAddrNewAsyncFunc func, gpointer data) { int pipes[2]; GInetAddr* ia; #ifdef ENABLE_IPV6 struct sockaddr_in6* sa_in6; #endif struct sockaddr_in* sa_in; GInetAddrAsyncState* state; g_return_val_if_fail(name != NULL, NULL); g_return_val_if_fail(func != NULL, NULL); /* Open a pipe */ if (pipe(pipes) == -1) { (*func)(NULL, GINETADDR_ASYNC_STATUS_ERROR, data); return NULL; } # ifdef HAVE_LIBPTHREAD /* Pthread */ { pthread_t pthread; void** args; args = g_new (void*, 2); args[0] = (void*) name; args[1] = g_new (int, 1); *(int*) args[1] = pipes[1]; if (pthread_create (&pthread, NULL/*&pthread_attr*/, gethostbyname_async_child, args)) { g_warning ("Pthread_create error\n"); return NULL; } state = g_new0(GInetAddrAsyncState, 1); state->pthread = pthread; } # else /* Fork */ { pid_t pid = -1; /* Fork to do the look up. */ fork_again: errno = 0; if ((pid = fork()) == 0) { void** args; args = g_new (void*, 2); args[0] = (void*) name; args[1] = g_new (int, 1); *(int*) args[1] = pipes[1]; gethostbyname_async_child (args); /* Exit (we don't want atexit called, so do _exit instead) */ _exit(EXIT_SUCCESS); } /* Set up state */ else if (pid > 0) { state = g_new0(GInetAddrAsyncState, 1); state->pid = pid; } /* Try again */ else if (errno == EAGAIN) { sleep(0); /* Yield the processor */ goto fork_again; } /* Else fork failed completely */ else { g_warning ("Fork error: %s (%d)\n", g_strerror(errno), errno); (*func)(NULL, GINETADDR_ASYNC_STATUS_ERROR, data); return NULL; } } #endif /* Create a new InetAddr */ ia = g_new0(GInetAddr, 1); ia->name = g_strdup(name); ia->ref_count = 1; #ifdef ENABLE_IPV6 if (use_ipv6 == 1) { sa_in6 = (struct sockaddr_in6*) &ia->sa; sa_in6->sin6_family = AF_INET6; sa_in6->sin6_port = g_htons(port); } else #endif { sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = g_htons(port); } /* Create a structure for the call back */ state->ia = ia; state->func = func; state->data = data; state->fd = pipes[0]; /* Add a watch */ state->watch = g_io_add_watch(g_io_channel_unix_new(pipes[0]), (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), gnet_inetaddr_new_async_cb, state); return state; } static void* gethostbyname_async_child (void* arg) /* pthread_create friendly */ { void** args = (void**) arg; const char* name = (const char*) args[0]; int outfd = *(int*) args[1]; guchar size; #ifdef ENABLE_IPV6 struct sockaddr_in6 sa6; #else struct sockaddr_in sa; #endif /* Try to get the host by name (ie, DNS) */ #ifdef ENABLE_IPV6 memset (&sa6, 0, sizeof (sa6)); if (gnet_gethostbyname (name, (struct sockaddr *)&sa6, NULL)) { if (sa6.sin6_family == AF_INET6) { size = 16; use_ipv6 = 1; if ( (write (outfd, &size, sizeof (guchar)) == -1) || (write (outfd, &sa6.sin6_addr, size) == -1) ) g_warning ("Problem writing to pipe\n"); } else { size = 4; use_ipv6 = 0; if ( (write (outfd, &size, sizeof (guchar)) == -1) || (write (outfd, &((struct sockaddr_in *)&sa6)->sin_addr, size) == -1) ) g_warning ("Problem writing to pipe\n"); } } #else if (gnet_gethostbyname(name, (struct sockaddr *)&sa, NULL)) { size = 4; /* 16 for IPv6 */ if ( (write(outfd, &size, sizeof(guchar)) == -1) || (write(outfd, &sa.sin_addr, size) == -1) ) g_warning ("Problem writing to pipe\n"); } #endif else { /* Write a zero */ guchar zero = 0; if (write(outfd, &zero, sizeof(zero)) == -1) g_warning ("Problem writing to pipe\n"); } /* Close the socket */ g_free (args[1]); g_free (args); close(outfd); return NULL; } gboolean gnet_inetaddr_new_async_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data) { GInetAddrAsyncState* state = (GInetAddrAsyncState*) data; /* Read from the pipe */ if (condition & G_IO_IN) { int rv; char* buf; int length; buf = (char *)&state->buffer[state->len]; length = sizeof(state->buffer) - state->len; if ((rv = read(state->fd, buf, length)) >= 0) { state->len += rv; /* Return true if there's more to read */ if ((state->len - 1) != state->buffer[0]) return TRUE; /* We're done reading. Copy into the addr if we were successful. */ if (state->len > 1) { #ifdef ENABLE_IPV6 if (state->len == 17) { struct sockaddr_in6 *sa_in6; sa_in6 = (struct sockaddr_in6 *) &state->ia->sa; sa_in6->sin6_family = AF_INET6; memcpy (&sa_in6->sin6_addr, &state->buffer[1], (state->len -1)); } else #endif { struct sockaddr_in* sa_in; sa_in = (struct sockaddr_in*) &state->ia->sa; memcpy(&sa_in->sin_addr, &state->buffer[1], (state->len - 1)); } } /* Otherwise, we got a 0 because there was an error */ else goto error; /* Remove the watch now in case we don't return immediately */ g_source_remove (state->watch); /* Call back */ (*state->func)(state->ia, GINETADDR_ASYNC_STATUS_OK, state->data); close (state->fd); # ifdef HAVE_LIBPTHREAD pthread_join (state->pthread, NULL); # else waitpid (state->pid, NULL, 0); # endif g_free(state); return FALSE; } /* otherwise, there was an error */ } /* otherwise, there was an error */ error: /* Remove the watch now in case we don't return immediately */ g_source_remove (state->watch); (*state->func)(NULL, GINETADDR_ASYNC_STATUS_ERROR, state->data); gnet_inetaddr_new_async_cancel(state); return FALSE; } /** * gnet_inetaddr_new_async_cancel: * @id: ID of the lookup * * Cancel an asynchronous GInetAddr creation that was started with * gnet_inetaddr_new_async(). * */ void gnet_inetaddr_new_async_cancel (GInetAddrNewAsyncID id) { GInetAddrAsyncState* state = (GInetAddrAsyncState*) id; g_return_if_fail(state != NULL); gnet_inetaddr_delete (state->ia); g_source_remove (state->watch); close (state->fd); # ifdef HAVE_LIBPTHREAD pthread_join (state->pthread, NULL); # else kill (state->pid, SIGKILL); waitpid (state->pid, NULL, 0); # endif g_free(state); } /** * gnet_inetaddr_new_nonblock: * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name * (eg, 141.213.8.59). You can delete the after the function is called. * @port: port number (0 if the port doesn't matter) * * Create an internet address from a name and port, but don't block * and fail if success would require blocking. This is, if the name * is a canonical name or "localhost", it returns the address. * Otherwise, it returns NULL. * * Returns: a new #GInetAddr, or NULL if there was a failure. * **/ GInetAddr* gnet_inetaddr_new_nonblock (const gchar* name, gint port) { struct sockaddr_in* sa_in; struct in_addr inaddr; GInetAddr* ia = NULL; #ifdef ENABLE_IPV6 struct sockaddr_in6 *sa_in6; struct in6_addr in6addr; #endif g_return_val_if_fail (name, NULL); /* Try to read the name as if were dotted decimal */ try_again: #ifdef ENABLE_IPV6 if (inet_pton (AF_INET6, name, &in6addr) > 0) { ia = g_new0 (GInetAddr, 0); ia->ref_count = 1; sa_in6 = (struct sockaddr_in6 *) &ia->sa; sa_in6->sin6_family = AF_INET6; sa_in6->sin6_port = g_htons (port); memcpy (&sa_in6->sin6_addr, (char*) &in6addr, sizeof (struct in6_addr)); } else if (!strcmp (name, "localhost")) { name = "::1"; goto try_again; } else #endif if (inet_pton(AF_INET, name, &inaddr) > 0) { ia = g_new0(GInetAddr, 1); ia->ref_count = 1; sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = g_htons(port); memcpy(&sa_in->sin_addr, (char*) &inaddr, sizeof(struct in_addr)); } /* If the name is localhost, change it to 127.0.0.1 and try again */ /* FIX: I don't think this is legal... */ else if (!strcmp (name, "localhost")) { name = "127.0.0.1"; goto try_again; } return ia; } /** * gnet_inetaddr_clone: * @ia: Address to clone * * Create an internet address from another one. * * Returns: a new InetAddr, or NULL if there was a failure. * **/ GInetAddr* gnet_inetaddr_clone(const GInetAddr* ia) { GInetAddr* cia; g_return_val_if_fail (ia != NULL, NULL); cia = g_new0(GInetAddr, 1); cia->ref_count = 1; cia->sa = ia->sa; if (ia->name != NULL) cia->name = g_strdup(ia->name); return cia; } /** * gnet_inetaddr_delete: * @ia: GInetAddr to delete * * Delete a GInetAddr. * **/ void gnet_inetaddr_delete(GInetAddr* ia) { if (ia != NULL) gnet_inetaddr_unref(ia); } /** * gnet_inetaddr_ref * @ia: GInetAddr to reference * * Increment the reference counter of the GInetAddr. * **/ void gnet_inetaddr_ref(GInetAddr* ia) { g_return_if_fail(ia != NULL); ++ia->ref_count; } /** * gnet_inetaddr_unref * @ia: GInetAddr to unreference * * Remove a reference from the GInetAddr. When reference count * reaches 0, the address is deleted. * **/ void gnet_inetaddr_unref(GInetAddr* ia) { g_return_if_fail(ia != NULL); --ia->ref_count; if (ia->ref_count == 0) { if (ia->name != NULL) g_free (ia->name); g_free (ia); } } /** * gnet_inetaddr_get_name_async: * @ia: Address to get the name of. * @func: Callback function. * @data: User data passed when callback function is called. * * Get the nice name of the address (eg, "mofo.eecs.umich.edu"). * This function will use the callback once it knows the nice name. * It may even call the callback before it returns. The callback * will be called if there is an error. * * The Unix version forks and does the reverse lookup. This has * problems. See the notes for gnet_inetaddr_new_async(). The * Windows version should work fine. * * Returns: ID of the lookup which can be used with * gnet_inetaddrr_get_name_async_cancel() to cancel it; NULL on * immediate success or failure. * **/ GInetAddrGetNameAsyncID gnet_inetaddr_get_name_async (GInetAddr* ia, GInetAddrGetNameAsyncFunc func, gpointer data) { g_return_val_if_fail(ia != NULL, NULL); g_return_val_if_fail(func != NULL, NULL); /* If we already know the name, just copy that */ if (ia->name != NULL) { (func)(ia, GINETADDR_ASYNC_STATUS_OK, g_strdup(ia->name), data); } /* Otherwise, fork and look it up */ else { pid_t pid = -1; int pipes[2]; /* Open a pipe */ if (pipe(pipes) == -1) { (func)(ia, GINETADDR_ASYNC_STATUS_ERROR, NULL, data); return NULL; } /* Fork to do the look up. */ fork_again: if ((pid = fork()) == 0) { gchar* name; guchar len; /* Write the name to the pipe. If we didn't get a name, we just write the canonical name. */ #ifdef ENABLE_IPV6 if (GNET_INETADDR_FAMILY (ia) == AF_INET6) name = gnet_gethostbyaddr ((char *)&(GNET_SOCKADDR_IN6 (ia->sa).sin6_addr), sizeof (struct in6_addr), AF_INET6); else #endif name = gnet_gethostbyaddr((char*) &((struct sockaddr_in*)&ia->sa)->sin_addr, sizeof(struct in_addr), AF_INET); if (name != NULL) { guint lenint = strlen(name); if (lenint > 255) { g_warning ("Truncating domain name: %s\n", name); name[256] = '\0'; lenint = 255; } len = lenint; if ((write(pipes[1], &len, sizeof(len)) == -1) || (write(pipes[1], name, len) == -1) ) g_warning ("Problem writing to pipe\n"); g_free(name); } else { #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY (ia) == AF_INET6) { gchar buffer6[INET6_ADDRSTRLEN]; if (inet_ntop (AF_INET6, &(GNET_SOCKADDR_IN6 (ia->sa).sin6_addr), buffer6, INET6_ADDRSTRLEN)) { len = strlen (buffer6); if ((write (pipes[1], &len, sizeof (len)) == -1) || (write (pipes[1], buffer6, len) == -1)) g_warning ("Problem writing to pipe\n"); } } else if (GNET_INETADDR_FAMILY(ia) == AF_INET) #endif { gchar buffer[INET_ADDRSTRLEN]; guchar* p = (guchar*) &(GNET_SOCKADDR_IN (ia->sa).sin_addr); g_snprintf (buffer, sizeof (buffer), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); len = strlen (buffer); if ((write (pipes[1], &len, sizeof (len)) == -1) || (write (pipes[1], buffer, len) == -1)) { g_warning ("Problem writing to pipe\n"); } } } /* Close the socket */ close(pipes[1]); /* Exit (we don't want atexit called, so do _exit instead) */ _exit(EXIT_SUCCESS); } /* Set up an IOChannel to read from the pipe */ else if (pid > 0) { GInetAddrReverseAsyncState* state; /* Create a structure for the call back */ state = g_new0(GInetAddrReverseAsyncState, 1); state->ia = ia; state->func = func; state->data = data; state->pid = pid; state->fd = pipes[0]; /* Add a watch */ state->watch = g_io_add_watch(g_io_channel_unix_new(pipes[0]), (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), gnet_inetaddr_get_name_async_cb, state); return state; } /* Try again */ else if (errno == EAGAIN) { sleep(0); /* Yield the processor */ goto fork_again; } /* Else there was a goofy error */ else { g_warning ("Fork error: %s (%d)\n", g_strerror(errno), errno); (*func)(ia, GINETADDR_ASYNC_STATUS_ERROR, NULL, data); } } return NULL; } gboolean gnet_inetaddr_get_name_async_cb (GIOChannel* iochannel, GIOCondition condition, gpointer data) { GInetAddrReverseAsyncState* state = (GInetAddrReverseAsyncState*) data; gchar* name = NULL; g_return_val_if_fail (state != NULL, FALSE); /* Read from the pipe */ if (condition & G_IO_IN) { int rv; char* buf; int length; buf = (char *)&state->buffer[state->len]; length = sizeof(state->buffer) - state->len; if ((rv = read(state->fd, buf, length)) >= 0) { state->len += rv; /* Return true if there's more to read */ if ((state->len - 1) != state->buffer[0]) return TRUE; /* Copy the name */ name = g_new(gchar, state->buffer[0] + 1); strncpy(name, (char *)&state->buffer[1], state->buffer[0]); name[state->buffer[0]] = '\0'; state->ia->name = g_strdup(name); /* Remove the watch now in case we don't return immediately */ g_source_remove (state->watch); /* Call back */ (*state->func)(state->ia, GINETADDR_ASYNC_STATUS_OK, name, state->data); close (state->fd); waitpid (state->pid, NULL, 0); g_free (state); return FALSE; } /* otherwise, there was a read error */ } /* otherwise, there was some error */ /* Remove the watch now in case we don't return immediately */ g_source_remove (state->watch); /* Call back */ (*state->func)(state->ia, GINETADDR_ASYNC_STATUS_ERROR, NULL, state->data); gnet_inetaddr_get_name_async_cancel(state); return FALSE; } /** * gnet_inetaddr_get_name_async_cancel: * @id: ID of the lookup * * Cancel an asynchronous nice name lookup that was started with * gnet_inetaddr_get_name_async(). * */ void gnet_inetaddr_get_name_async_cancel(GInetAddrGetNameAsyncID id) { GInetAddrReverseAsyncState* state = (GInetAddrReverseAsyncState*) id; g_return_if_fail(state != NULL); gnet_inetaddr_delete (state->ia); g_source_remove (state->watch); close (state->fd); kill (state->pid, SIGKILL); waitpid (state->pid, NULL, 0); g_free(state); } /** * gnet_inetaddr_get_canonical_name: * @ia: Address to get the canonical name of. * * Get the "canonical" name of an address (eg, for IP4 the dotted * decimal name 141.213.8.59). * * Returns: NULL if there was an error. The caller is responsible * for deleting the returned string. * **/ gchar* gnet_inetaddr_get_canonical_name(const GInetAddr* ia) { guchar* p; g_return_val_if_fail (ia != NULL, NULL); #ifdef ENABLE_IPV6 if (GNET_INETADDR_FAMILY (ia) == AF_INET6) { guchar buffer6[INET6_ADDRSTRLEN]; if (inet_ntop (AF_INET6, &((GNET_SOCKADDR_IN6 (ia->sa)).sin6_addr), (char *)buffer6, sizeof (buffer6))) return NULL; return g_strdup ((char *)buffer6); } else #endif { p = (guchar*) &(GNET_SOCKADDR_IN(ia->sa).sin_addr); } return g_strdup_printf ("%d.%d.%d.%d", p[0], p[1], p[2], p[3]); } /** * gnet_inetaddr_get_port: * @ia: Address to get the port number of. * * Get the port number. * Returns: the port number. */ gint gnet_inetaddr_get_port(const GInetAddr* ia) { g_return_val_if_fail(ia != NULL, -1); #ifdef ENABLE_IPV6 if (GNET_INETADDR_FAMILY (ia) == AF_INET6) return (gint) g_ntohs ((GNET_SOCKADDR_IN6 (ia->sa)).sin6_port); #endif return (gint) g_ntohs(((struct sockaddr_in*) &ia->sa)->sin_port); } /** * gnet_inetaddr_set_port: * @ia: Address to set the port number of. * @port: New port number * * Set the port number. * **/ void gnet_inetaddr_set_port(const GInetAddr* ia, guint port) { g_return_if_fail(ia != NULL); #ifdef ENABLE_IPV6 if (GNET_INETADDR_FAMILY (ia) == AF_INET6) (GNET_SOCKADDR_IN6 (ia->sa)).sin6_port = g_htons (port); else #endif ((struct sockaddr_in*) &ia->sa)->sin_port = g_htons(port); } /* **************************************** */ /** * gnet_inetaddr_is_canonical: * @name: Name to check * * Check if the domain name is canonical. For IPv4, a canonical name * is a dotted decimal name (eg, 141.213.8.59). * * Returns: TRUE if @name is canonical; FALSE otherwise. * **/ gboolean gnet_inetaddr_is_canonical (const gchar* name) { struct in_addr inaddr; g_return_val_if_fail (name, FALSE); #ifdef ENABLE_IPV6 { struct in6_addr in6addr; return ((inet_pton (AF_INET6, name, &in6addr) > 0) || (inet_pton (AF_INET, name, &inaddr) > 0)); } #endif return (inet_pton(AF_INET, name, &inaddr) > 0); } /** * gnet_inetaddr_is_internet: * @inetaddr: Address to check * * Check if the address is a sensible internet address. This mean it * is not private, reserved, loopback, multicast, or broadcast. * * Note that private and loopback address are often valid addresses, * so this should only be used to check for general Internet * connectivity. That is, if the address passes, it is reachable on * the Internet. * * Returns: TRUE if the address is an 'Internet' address; FALSE * otherwise. * **/ gboolean gnet_inetaddr_is_internet (const GInetAddr* inetaddr) { g_return_val_if_fail(inetaddr != NULL, FALSE); if (!gnet_inetaddr_is_private(inetaddr) && !gnet_inetaddr_is_reserved(inetaddr) && !gnet_inetaddr_is_loopback(inetaddr) && !gnet_inetaddr_is_multicast(inetaddr) && !gnet_inetaddr_is_broadcast(inetaddr)) { return TRUE; } return FALSE; } /** * gnet_inetaddr_is_private: * @inetaddr: Address to check * * Check if the address is an address reserved for private networks * or something else. This includes: * * 10.0.0.0 - 10.255.255.255 (10/8 prefix) * 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) * * (from RFC 1918. See also draft-manning-dsua-02.txt) * * Returns: TRUE if the address is reserved for private networks; * FALSE otherwise. * **/ gboolean gnet_inetaddr_is_private (const GInetAddr* inetaddr) { guint addr; g_return_val_if_fail (inetaddr != NULL, FALSE); #ifdef ENABLE_IPV6 if (GNET_INETADDR_FAMILY (inetaddr) == AF_INET6) { if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) &(inetaddr->sa))->sin6_addr) || IN6_IS_ADDR_SITELOCAL (&((struct sockaddr_in6 *) &(inetaddr->sa))->sin6_addr)) return TRUE; return FALSE; } #endif addr = GNET_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; addr = g_ntohl(addr); if ((addr & 0xFF000000) == (10 << 24)) return TRUE; if ((addr & 0xFFF00000) == 0xAC100000) return TRUE; if ((addr & 0xFFFF0000) == 0xC0A80000) return TRUE; return FALSE; } /** * gnet_inetaddr_is_reserved: * @inetaddr: Address to check * * Check if the address is reserved for 'something'. This excludes * address reserved for private networks. * * We check for: * 0.0.0.0/16 (top 16 bits are 0's) * Class E (top 5 bits are 11110) * * Returns: TRUE if the address is reserved for something; FALSE * otherwise. * **/ gboolean gnet_inetaddr_is_reserved (const GInetAddr* inetaddr) { guint addr; g_return_val_if_fail (inetaddr != NULL, FALSE); #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY(inetaddr) == AF_INET6) { struct sockaddr_in6 sin6 ; char buffer6[INET6_ADDRSTRLEN]; /* In IPv6 reserved address is 0:0:0:0:0:0:0:0 i.e. (::) */ sin6 = GNET_SOCKADDR_IN6 (inetaddr->sa); if (inet_ntop (AF_INET6, &(sin6.sin6_addr), buffer6, sizeof (buffer6))) { if (!strcmp (buffer6, "::")) return TRUE; } return FALSE; } #endif addr = GNET_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; addr = g_ntohl(addr); if ((addr & 0xFFFF0000) == 0) return TRUE; if ((addr & 0xF8000000) == 0xF0000000) return TRUE; return FALSE; } /** * gnet_inetaddr_is_loopback: * @inetaddr: Address to check * * Check if the address is a loopback address. Loopback addresses * have the prefix 127.0.0.1/16. * * Returns: TRUE if the address is a loopback address; FALSE * otherwise. * **/ gboolean gnet_inetaddr_is_loopback (const GInetAddr* inetaddr) { guint addr; g_return_val_if_fail (inetaddr != NULL, FALSE); #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY (inetaddr) == AF_INET6) { if (IN6_IS_ADDR_LOOPBACK (&GNET_SOCKADDR_IN6 (inetaddr->sa).sin6_addr)) return TRUE; return FALSE; } #endif addr = GNET_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; addr = g_ntohl(addr); if ((addr & 0xFF000000) == (127 << 24)) return TRUE; return FALSE; } /** * gnet_inetaddr_is_multicast: * @inetaddr: Address to check * * Check if the address is a multicast address. Multicast address * are in the range 224.0.0.1 - 239.255.255.255 (ie, the top four * bits are 1110). * * Returns: TRUE if the address is a multicast address; FALSE * otherwise. * **/ gboolean gnet_inetaddr_is_multicast (const GInetAddr* inetaddr) { guint addr; g_return_val_if_fail (inetaddr != NULL, FALSE); #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY (inetaddr) == AF_INET6) { if (IN6_IS_ADDR_MULTICAST (&GNET_SOCKADDR_IN6 (inetaddr->sa).sin6_addr)) return TRUE; return FALSE; } #endif addr = GNET_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; addr = g_htonl(addr); if ((addr & 0xF0000000) == 0xE0000000) return TRUE; return FALSE; } /** * gnet_inetaddr_is_broadcast: * @inetaddr: Address to check * * Check if the address is a broadcast address. The broadcast * address is 255.255.255.255. (Network broadcast address are * network dependent.) * * Returns: TRUE if the address is a broadcast address; FALSE * otherwise. * **/ gboolean gnet_inetaddr_is_broadcast (const GInetAddr* inetaddr) { guint addr; g_return_val_if_fail (inetaddr != NULL, FALSE); addr = GNET_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; if (addr == 0xFFFFFFFF) return TRUE; return FALSE; } /* **************************************** */ /** * gnet_inetaddr_hash: * @p: Pointer to an #GInetAddr. * * Hash the address. This is useful for glib containers. * * Returns: hash value. * **/ guint gnet_inetaddr_hash (gconstpointer p) { const GInetAddr* ia; guint32 port; guint32 addr; g_assert(p != NULL); ia = (const GInetAddr*) p; /* We do pay attention to network byte order just in case the hash result is saved or sent to a different host. */ #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY(ia) == AF_INET6) { port = GNET_SOCKADDR_IN6(ia->sa).sin6_port; addr = g_ntohl (GNET_INETADDR_ADDR32 (ia, 0)) ^ g_ntohl (GNET_INETADDR_ADDR32 (ia, 1)) ^ g_ntohl (GNET_INETADDR_ADDR32 (ia, 2)) ^ g_ntohl (GNET_INETADDR_ADDR32 (ia, 3)); return (port ^ addr); } #endif port = (guint32) g_ntohs(((struct sockaddr_in*) &ia->sa)->sin_port); addr = g_ntohl(((struct sockaddr_in*) &ia->sa)->sin_addr.s_addr); return (port ^ addr); } /** * gnet_inetaddr_equal: * @p1: Pointer to first #GInetAddr. * @p2: Pointer to second #GInetAddr. * * Compare two #GInetAddr's. * * Returns: 1 if they are the same; 0 otherwise. * **/ gint gnet_inetaddr_equal(gconstpointer p1, gconstpointer p2) { const GInetAddr* ia1 = (const GInetAddr*) p1; const GInetAddr* ia2 = (const GInetAddr*) p2; g_assert(p1 != NULL && p2 != NULL); /* Note network byte order doesn't matter */ #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY (ia1) == AF_INET6) return ((GNET_SOCKADDR_IN6 (ia1->sa).sin6_addr.s6_addr == GNET_SOCKADDR_IN6 (ia2->sa).sin6_addr.s6_addr) && (GNET_SOCKADDR_IN6 (ia1->sa).sin6_port == GNET_SOCKADDR_IN6 (ia2->sa).sin6_port)); #endif return ((GNET_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == GNET_SOCKADDR_IN(ia2->sa).sin_addr.s_addr) && (GNET_SOCKADDR_IN(ia1->sa).sin_port == GNET_SOCKADDR_IN(ia2->sa).sin_port)); } /** * gnet_inetaddr_noport_equal: * @p1: Pointer to first GInetAddr. * @p2: Pointer to second GInetAddr. * * Compare two #GInetAddr's, but does not compare the port numbers. * * Returns: 1 if they are the same; 0 otherwise. * **/ gint gnet_inetaddr_noport_equal(gconstpointer p1, gconstpointer p2) { const GInetAddr* ia1 = (const GInetAddr*) p1; const GInetAddr* ia2 = (const GInetAddr*) p2; g_assert(p1 != NULL && p2 != NULL); /* Note network byte order doesn't matter */ #if defined (ENABLE_IPV6) if (GNET_INETADDR_FAMILY (ia1) == AF_INET6) return (GNET_SOCKADDR_IN6 (ia1->sa).sin6_addr.s6_addr == GNET_SOCKADDR_IN6 (ia2->sa).sin6_addr.s6_addr); #endif return (GNET_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == GNET_SOCKADDR_IN(ia2->sa).sin_addr.s_addr); } /* **************************************** */ /** * gnet_inetaddr_gethostname: * * Get the primary host's name. * * Returns: the name of the host; NULL if there was an error. The * caller is responsible for deleting the returned string. * **/ gchar* gnet_inetaddr_gethostname(void) { gchar* name = NULL; struct utsname myname; if (uname(&myname) < 0) return NULL; if (!gnet_gethostbyname(myname.nodename, NULL, &name)) return NULL; return name; } /** * gnet_inetaddr_gethostaddr: * * Get the primary host's #GInetAddr. * * Returns: the #GInetAddr of the host; NULL if there was an error. * The caller is responsible for deleting the returned #GInetAddr. * **/ GInetAddr* gnet_inetaddr_gethostaddr(void) { gchar* name; GInetAddr* ia = NULL; name = gnet_inetaddr_gethostname(); if (name != NULL) { ia = gnet_inetaddr_new(name, 0); g_free(name); } return ia; } /* **************************************** */ /** * gnet_inetaddr_new_any: * * Create a #GInetAddr with the address INADDR_ANY and port 0. This * is useful for creating default addresses for binding. The * address's name will be "". * * Returns: INADDR_ANY #GInetAddr. * **/ GInetAddr* gnet_inetaddr_new_any (void) { GInetAddr* ia; struct sockaddr_in* sa_in; ia = g_new0 (GInetAddr, 1); #if defined (ENABLE_IPV6) if (gnet_have_ipv6 ()) { struct sockaddr_in6 *sa_in6; sa_in6 = (struct sockaddr_in6*) &ia->sa; sa_in6->sin6_addr = in6addr_any; sa_in6->sin6_port = 0; ia->name = g_strdup (""); return ia; } #endif sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_addr.s_addr = g_htonl(INADDR_ANY); sa_in->sin_port = 0; ia->name = g_strdup (""); return ia; } /** * gnet_inetaddr_autodetect_internet_interface: * * Find an Internet interface. Usually, this interface routes * packets to and from the Internet. It can be used to automatically * configure simple servers that must advertise their address. This * sometimes doesn't work correctly when the user is behind a NAT. * * Returns: Address of an Internet interface; NULL if it couldn't * find one or there was an error. * **/ GInetAddr* gnet_inetaddr_autodetect_internet_interface (void) { GInetAddr* jm_addr; GInetAddr* iface; /* First try to get the interface with a route to junglemonkey.net (141.213.11.1). This uses the connected UDP socket method described by Stevens. It does not work on all systems. (see Stevens UNPv1 pp 231-3) */ jm_addr = gnet_inetaddr_new_nonblock ("141.213.11.1", 0); g_assert (jm_addr); iface = gnet_inetaddr_get_interface_to (jm_addr); gnet_inetaddr_delete (jm_addr); /* We want an internet interface */ if (iface && gnet_inetaddr_is_internet(iface)) return iface; gnet_inetaddr_delete (iface); /* Try getting an internet interface from the list via SIOCGIFCONF. (see Stevens UNPv1 pp 428-) */ iface = gnet_inetaddr_get_internet_interface (); return iface; } /** * gnet_inetaddr_get_interface_to: * @addr: address * * Figure out which local interface would be used to send a packet to * @addr. This works on some systems, but not others. We recommend * using gnet_inetaddr_autodetect_internet_interface() to find an * Internet interface since it's more likely to work. * * Returns: Address of an interface used to route packets to @addr; * NULL if there is no such interface or the system does not support * this check. * **/ GInetAddr* gnet_inetaddr_get_interface_to (const GInetAddr* addr) { int sockfd; #ifdef ENABLE_IPV6 struct sockaddr_in6 myaddr6; #endif struct sockaddr_in myaddr; socklen_t len; GInetAddr* iface; g_return_val_if_fail (addr, NULL); iface = g_new0 (GInetAddr, 1); iface->ref_count = 1; #ifdef ENABLE_IPV6 if ((GNET_SOCKADDR_IN6 (addr->sa)).sin6_family == AF_INET6) { sockfd = socket (AF_INET6, SOCK_STREAM, 0); if (sockfd == -1) { g_free (iface); return NULL; } if (connect (sockfd, (struct sockaddr *)&(addr->sa), sizeof (addr->sa)) == -1) { GNET_CLOSE_SOCKET (sockfd); g_free (iface); return NULL; } len = sizeof (myaddr6); if (getsockname (sockfd, (struct sockaddr *)&(addr->sa), &len) != 0) { GNET_CLOSE_SOCKET (sockfd); g_free (iface); return NULL; } memcpy (&iface->sa, (char*) &myaddr6, sizeof (struct sockaddr_in6)); } else #endif { sockfd = socket (AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { g_free (iface); return NULL; } if (connect (sockfd, (struct sockaddr *)&addr->sa, sizeof(addr->sa)) == -1) { GNET_CLOSE_SOCKET(sockfd); g_free (iface); return NULL; } len = sizeof (myaddr); if (getsockname (sockfd, (struct sockaddr*) &myaddr, &len) != 0) { GNET_CLOSE_SOCKET(sockfd); g_free (iface); return NULL; } memcpy (&iface->sa, (char*) &myaddr, sizeof (struct sockaddr_in)); } return iface; } /** * gnet_inetaddr_get_internet_interface: * * Find an Internet interface. This just calls * gnet_inetaddr_list_interfaces() and returns the first one that * passes gnet_inetaddr_is_internet(). This works well on some * systems, but not so well on others. We recommend using * gnet_inetaddr_autodetect_internet_interface() to find an Internet * interface since it's more likely to work. * * Returns: Address of an Internet interface; NULL if there is no * such interface or the system does not support this check. * **/ GInetAddr* gnet_inetaddr_get_internet_interface (void) { GInetAddr* iface = NULL; GList* interfaces; GList* i; /* Get a list of interfaces */ interfaces = gnet_inetaddr_list_interfaces (); if (interfaces == NULL) return NULL; /* Find the first interface that's an internet interface */ for (i = interfaces; i != NULL; i = i->next) { GInetAddr* ia; ia = (GInetAddr*) i->data; if (gnet_inetaddr_is_internet (ia)) { iface = gnet_inetaddr_clone (ia); break; } } /* If we didn't find one, return the first interface. */ if (iface == NULL) iface = gnet_inetaddr_clone ((GInetAddr*) interfaces->data); /* Delete the interface list */ for (i = interfaces; i != NULL; i = i->next) gnet_inetaddr_delete ((GInetAddr*) i->data); g_list_free (interfaces); return iface; } /** * gnet_inetaddr_list_interfaces: * * Get a list of #GInetAddr interfaces's on this host. This list * includes all "up" Internet interfaces and the loopback interface, * if it exists. * * Note that the Windows version supports a maximum of 10 interfaces. * In Windows NT, Service Pack 4 (or higher) is required. * * Returns: A list of #GInetAddr's representing available interfaces. * The caller should delete the list and the addresses. * **/ GList* gnet_inetaddr_list_interfaces (void) { GList* list = NULL; gint len, lastlen; gchar* buf; gchar* ptr; gint sockfd; struct ifconf ifc; /* Create a dummy socket */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) return NULL; len = 8 * sizeof(struct ifreq); lastlen = 0; /* Get the list of interfaces. We might have to try multiple times if there are a lot of interfaces. */ while(1) { buf = g_new0(gchar, len); ifc.ifc_len = len; ifc.ifc_buf = buf; if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { /* Might have failed because our buffer was too small */ if (errno != EINVAL || lastlen != 0) { g_free(buf); return NULL; } } else { /* Break if we got all the interfaces */ if (ifc.ifc_len == lastlen) break; lastlen = ifc.ifc_len; } /* Did not allocate big enough buffer - try again */ len += 8 * sizeof(struct ifreq); g_free(buf); } /* Create the list. Stevens has a much more complex way of doing this, but his is probably much more correct portable. */ for (ptr = buf; ptr < (buf + ifc.ifc_len); ) { struct ifreq* ifr = (struct ifreq*) ptr; struct sockaddr addr; GInetAddr* ia; #ifdef HAVE_SOCKADDR_SA_LEN ptr += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; #else ptr += sizeof(struct ifreq); #endif /* Ignore non-AF_INET */ if (ifr->ifr_addr.sa_family != AF_INET) continue; /* FIX: Skip colons in name? Can happen if aliases, maybe. */ /* Save the address - the next call will clobber it */ memcpy(&addr, &ifr->ifr_addr, sizeof(addr)); /* Get the flags */ ioctl(sockfd, SIOCGIFFLAGS, ifr); /* Ignore entries that aren't up or loopback. Someday we'll write an interface structure and include this stuff. */ if (!(ifr->ifr_flags & IFF_UP) || (ifr->ifr_flags & IFF_LOOPBACK)) continue; /* Create an InetAddr for this one and add it to our list */ ia = g_new0 (GInetAddr, 1); ia->ref_count = 1; memcpy(&ia->sa, &addr, sizeof(addr)); list = g_list_prepend (list, ia); } list = g_list_reverse (list); return list; }