/* * $Id: inetaddr.c 399 2004-11-18 21:42:46Z massie $ * This code was originally written by the authors below but * I rewrote it and took out all the glib dependencies. * * 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 #include #include #include #include #include "llist.h" #include "net.h" pthread_mutex_t gethostbyname_mutex = PTHREAD_MUTEX_INITIALIZER; /* Need to free "nicename" later */ int g_gethostbyname(const char* hostname, struct sockaddr_in* sa, char** nicename) { int rv = 0; struct in_addr inaddr; struct hostent* he; if (inet_aton(hostname, &inaddr) != 0) { sa->sin_family = AF_INET; memcpy(&sa->sin_addr, (char*) &inaddr, sizeof(struct in_addr)); if (nicename) *nicename = (char *)strdup (hostname); return 1; } pthread_mutex_lock (&gethostbyname_mutex); he = (struct hostent*)gethostbyname(hostname); if (he && he->h_addrtype==AF_INET && he->h_addr_list[0]) { if (sa) { sa->sin_family = he->h_addrtype; memcpy(&sa->sin_addr, he->h_addr_list[0], he->h_length); } if (nicename && he->h_name) *nicename = (char *)strdup(he->h_name); rv = 1; } pthread_mutex_unlock(&gethostbyname_mutex); return rv; } /* Need to free return value later */ char* g_gethostbyaddr(const char* addr, size_t length, int type) { char* rv = NULL; struct hostent* he; pthread_mutex_lock (&gethostbyname_mutex); he = gethostbyaddr(addr, length, type); if (he != NULL && he->h_name != NULL) rv = (char *)strdup(he->h_name); pthread_mutex_unlock(&gethostbyname_mutex); return rv; } g_inet_addr* g_inetaddr_new (const char* name, int port) { struct sockaddr_in* sa_in; struct in_addr inaddr; g_inet_addr* ia = NULL; if(name==NULL) return NULL; ia = (g_inet_addr *)malloc(sizeof(g_inet_addr)); if(ia==NULL) return NULL; memset(ia, 0, sizeof(g_inet_addr)); ia->name = (char *)strdup(name); ia->ref_count = 1; /* Try to read the name as if were dotted decimal */ if (inet_aton(name, &inaddr) != 0) { sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = 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 (g_gethostbyname(name, &sa, NULL)) { sa_in = (struct sockaddr_in*) &ia->sa; sa_in->sin_family = AF_INET; sa_in->sin_port = htons(port); memcpy(&sa_in->sin_addr, &sa.sin_addr, 4); } } return ia; } void g_inetaddr_ref (g_inet_addr* ia) { if(ia==NULL) return; ++ia->ref_count; } void g_inetaddr_unref (g_inet_addr* ia) { if(ia==NULL) return; --ia->ref_count; if (ia->ref_count == 0) { if (ia->name != NULL) free (ia->name); free (ia); } } void g_inetaddr_delete (g_inet_addr* ia) { if (ia != NULL) g_inetaddr_unref(ia); } int g_inetaddr_get_port(const g_inet_addr* ia) { if(ia==NULL) return -1; return (int) ntohs(G_SOCKADDR_IN(ia->sa).sin_port); } void g_inetaddr_set_port(const g_inet_addr* ia, unsigned int port) { if(ia == NULL) return; G_SOCKADDR_IN(ia->sa).sin_port = htons(port); } int g_inetaddr_is_multicast (const g_inet_addr* inetaddr) { unsigned int addr; if( inetaddr == NULL ) return 0; addr = G_SOCKADDR_IN(inetaddr->sa).sin_addr.s_addr; addr = htonl(addr); if ((addr & 0xF0000000) == 0xE0000000) return 1; return 0; } g_inet_addr* g_inetaddr_get_interface_to (const g_inet_addr* addr) { int sockfd; struct sockaddr_in myaddr; socklen_t len; g_inet_addr* iface; if(!addr) return NULL; sockfd = socket (AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) return NULL; if (connect (sockfd, &addr->sa, sizeof(addr->sa)) == -1) { close(sockfd); return NULL; } len = sizeof (myaddr); if (getsockname (sockfd, (struct sockaddr*) &myaddr, &len) != 0) { close(sockfd); return NULL; } iface = (g_inet_addr *)malloc(sizeof(g_inet_addr)); if(!iface) { close(sockfd); return NULL; } iface->ref_count = 1; memcpy (&iface->sa, (char*) &myaddr, sizeof (struct sockaddr_in)); return iface; } llist_entry * g_inetaddr_list_interfaces (void) { llist_entry *list = NULL; llist_entry *e; int len, lastlen; char* buf; char* ptr; int 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 = (char *) malloc (len); memset( buf, 0, 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) { 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); 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; g_inet_addr* 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 = malloc( sizeof(g_inet_addr) ); memset( ia, 0, sizeof(g_inet_addr) ); ia->ref_count = 1; memcpy(&ia->sa, &addr, sizeof(addr)); e = malloc(sizeof(llist_entry)); if(!e) { free(ia); free(buf); return NULL; } e->val = ia; llist_add( &list, e); } free (buf); return list; }