📄 su_localinfo.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@ingroup su_socket * @CFILE su_localinfo.c * * Obtain list of local addresses. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Oct 4 14:09:29 EET 2000 ppessi */#include "config.h"#include "sofia-sip/su.h"#include "sofia-sip/su_localinfo.h"#include "su_module_debug.h"#if HAVE_SYS_SOCKIO_H#include <sys/sockio.h>#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <stddef.h>#include <sys/types.h>#if HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#if HAVE_NETINET_IN_H#include <netinet/in.h>#endif#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#if HAVE_NET_IF_H#include <net/if.h>#endif#if HAVE_NET_IF_TYPES_H#include <net/if_types.h>#endif#if HAVE_GETIFADDRS#define USE_LOCALINFO0 1#define localinfo0 bsd_localinfostatic int bsd_localinfo(su_localinfo_t const *, su_localinfo_t **);#elif HAVE_IPHLPAPI_H#include <iphlpapi.h>#define USE_LOCALINFO0 1#define localinfo0 win_localinfostatic int win_localinfo(su_localinfo_t const *, su_localinfo_t **);#else#undef USE_LOCALINFO0static int localinfo4(su_localinfo_t const *, su_localinfo_t **);# if SU_HAVE_IN6static int localinfo6(su_localinfo_t const *, su_localinfo_t **);# endif#endifstatic int li_name(su_localinfo_t const*, int, su_sockaddr_t const*, char **);static void li_sort(su_localinfo_t *i, su_localinfo_t **rresult);static int li_scope4(uint32_t ip4);/** @brief Request local address information. * * The function su_getlocalinfo() gathers the network interfaces and the * addresses corresponding to them, checks if they match to the search * criteria specifed by @a hints and returns a list of matching local * address information in the @a res. The local address information may * include IPv4/IPv6 addresses, interface name, interface index, address * scope, and domain names corresponding to the local addresses. * * @param hints specifies selection criteria * @param return_localinfo return list of local addresses * * @par Selection criteria - hints * * The selection criteria @a hints is used to select which addresses are * returned and what kind of information is included in the @a res list. * * @par Selection by flags - hints->li_flags * * The @a hints->li_flags contain flags, which can be combined with bit-wise * or. The currently defined flags are as follows: * * - @c LI_V4MAPPED: when returning IPv4 addresses, map them as IPv6 * addresses. If this flag is specified, IPv4 addresses are returned even * if @a hints->li_family is set to @c AF_INET6. * - @c LI_CANONNAME: return the domain name (DNS PTR) corresponding to the * local address in @a li_canonname. * - @c LI_NAMEREQD: Do not return addresses not in DNS. * - @c LI_NUMERIC: instead of domain name, return the text presentation of * the addresss in @a li_canonname. * - @c LI_IFNAME: return the interface name in @a li_ifname. * * @par Selection by address family - hints->li_family * * The address family can have three values: 0, AF_INET and AF_INET6. If * address family @a hints->li_family, both IPv4 and IPv6 addresses are * returned. * * @par Selection by interface index - hints->li_index * * If the field @a hints->li_index is non-zero, only the addresses assigned * to the interface with given index are returned. The list of interface * indices and names can be obtained by the function @c su_if_names(). * * @par Selection by interface name - hints->li_ifname * * If the field @a hints->li_ifname is not NULL, only the addresses assigned * to the named interface are returned. The list of interface names can be * obtained by the function @c su_if_names(). * * @par Selection by address scope - hints->li_scope * * If the field @a hints->li_scope is nonzero, only the addresses with * matching scope are returned. The address scopes can be combined with * bitwise or. For instance, setting @a hints->li_scope to @c * LI_SCOPE_GLOBAL | @c LI_SCOPE_SITE, both the @e global and @e site-local * addresses are returned. * * @par * For IPv4, the loopback addresses (addresses in the net 127) are currently * considered @e host-local, other addresses are @e global. * * @par Selection by domain name - hints->li_canonname * * If this field is non-null, the domain name (DNS PTR) corresponding to * local IP addresses should match to the name given in this field. * * @return * The function su_getlocalinfo() returns zero when successful, or * negative error code when failed. * * @par diagnostics * The function su_gli_strerror() returns a string describing the error code * returned by su_getlocalinfo(). * */int su_getlocalinfo(su_localinfo_t const *hints, su_localinfo_t **return_localinfo){ int error = 0, ip4 = 0, ip6 = 0; su_localinfo_t *result = NULL, **rr = &result; su_localinfo_t hh[1] = {{ 0 }}; assert(return_localinfo); *return_localinfo = NULL; if (hints) { /* Copy hints so that it can be modified */ *hh = *hints; if (hh->li_canonname) hh->li_flags |= LI_CANONNAME; if ((hh->li_flags & LI_IFNAME) && hh->li_ifname == NULL) return ELI_BADHINTS; } switch (hh->li_family) {#if SU_HAVE_IN6 case AF_INET6: if (hh->li_flags & LI_V4MAPPED) ip6 = ip4 = 1, hh->li_family = 0; else ip6 = 1; break;#endif case AF_INET: ip4 = 1; break; case 0: ip6 = ip4 = 1; break; default: return -1; }#if USE_LOCALINFO0 error = localinfo0(hh, rr);#else# if SU_HAVE_IN6 if (ip6) { error = localinfo6(hh, rr); if (error == ELI_NOADDRESS && ip4) error = 0; if (!error) /* Search end of list */ for (; *rr; rr = &(*rr)->li_next) ; }# endif if (ip4 && !error) { /* Append IPv4 addresses */ error = localinfo4(hh, rr); }#endif if (!result) error = ELI_NOADDRESS; if (!error) li_sort(result, return_localinfo); else su_freelocalinfo(result); return error;}/** Free local address information. */void su_freelocalinfo(su_localinfo_t *tbf){ su_localinfo_t *li; for (li = tbf; li; li = tbf) { tbf = li->li_next; if (li->li_canonname) free(li->li_canonname); free(li); }}/** Describe su_localinfo errors. * * The function su_gli_strerror() returns a string describing the error * condition indicated by the code that was returned by the function * su_getlocalinfo(). * * @param error error code returned by su_getlocalinfo() * * @return * A pointer to string describing the error condition. */char const *su_gli_strerror(int error){ switch (error) { case ELI_NOERROR: return "No error"; case ELI_NOADDRESS: return "No matching address"; case ELI_MEMORY: return "Memory allocation error"; case ELI_FAMILY: return "Unknown address family"; case ELI_RESOLVER: return "Error when resolving address"; case ELI_SYSTEM: return "System error"; case ELI_BADHINTS: return "Invalid value for hints"; default: return "Unknown error"; }}/** Duplicate su_localinfo structure. */su_localinfo_t *su_copylocalinfo(su_localinfo_t const *li0){ int n; su_localinfo_t *li, *retval = NULL, **lli = &retval;# define SLEN(s) ((s) ? strlen(s) + 1 : 0) for (; li0 ; li0 = li0->li_next) { n = sizeof(*li0) + li0->li_addrlen + SLEN(li0->li_ifname); if (!(li = calloc(1, n))) { su_freelocalinfo(retval); return NULL; } *lli = li; lli = &li->li_next; li->li_flags = li0->li_flags; li->li_family = li0->li_family; li->li_index = li0->li_index; li->li_scope = li0->li_scope; li->li_addrlen = li0->li_addrlen; li->li_addr = memcpy(li + 1, li0->li_addr, li0->li_addrlen); if (li0->li_canonname) { if (!(li->li_canonname = malloc(SLEN(li0->li_canonname)))) { su_freelocalinfo(retval); return NULL; } strcpy(li->li_canonname, li0->li_canonname); } if (li0->li_ifname) li->li_ifname = strcpy(li->li_addrlen + (char *)li->li_addr, li0->li_ifname); } return retval;}/** Return IPv4 address scope */static int li_scope4(uint32_t ip4){ ip4 = ntohl(ip4); if (0x7f000000 == (ip4 & 0xff000000)) return LI_SCOPE_HOST; /* draft-ietf-zeroconf-ipv4-linklocal-02.txt - 169.254/16. */ else if (0xa9fe0000 == (ip4 & 0xffff0000)) return LI_SCOPE_LINK; /* RFC1918 - 10/8, 172.16/12, 192.168/16. */ else if (0x0a000000 == (ip4 & 0xff000000) || 0xac100000 == (ip4 & 0xfff00000) || 0xc0a80000 == (ip4 & 0xffff0000)) return LI_SCOPE_SITE; else return LI_SCOPE_GLOBAL;}#if USE_LOCALINFO0/** Return IPv6 address scope */static int li_scope6(struct in6_addr const *ip6){ if (IN6_IS_ADDR_V4MAPPED(ip6) || IN6_IS_ADDR_V4COMPAT(ip6)) { uint32_t ip4 = *(uint32_t *)(ip6->s6_addr + 12); return li_scope4(ip4); } else if (IN6_IS_ADDR_LOOPBACK(ip6)) return LI_SCOPE_HOST; else if (IN6_IS_ADDR_LINKLOCAL(ip6)) return LI_SCOPE_LINK; else if (IN6_IS_ADDR_SITELOCAL(ip6)) return LI_SCOPE_SITE; else return LI_SCOPE_GLOBAL;}#elif HAVE_IFCONF/** Build a list of local IPv4 addresses and append it to *rresult. */staticint localinfo4(su_localinfo_t const *hints, su_localinfo_t **rresult){ su_localinfo_t *tbf = NULL, **lli = &tbf; su_localinfo_t *li = NULL, *li_first = NULL; su_sockaddr_t *su; int error = ELI_NOADDRESS; char *canonname = NULL; su_socket_t s;#if SU_HAVE_IN6 int su_xtra = (hints->li_flags & LI_V4MAPPED) ? sizeof(*su) : 0;#else int const su_xtra = 0;#endif struct ifconf ifc; int numifs; char *buffer; struct ifreq *ifr, *ifr_next; s = su_socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) { SU_DEBUG_1(("su_localinfo: su_socket failed: %s\n", su_strerror(su_errno()))); return ELI_SYSTEM; }#if defined(__APPLE_CC__) { su_sockaddr_t *sa; unsigned int salen = sizeof(*sa); int scope = 0, gni_flags = 0; li = calloc(1, sizeof(su_localinfo_t)); sa = calloc(1, sizeof(su_sockaddr_t)); error = getsockname(s, (struct sockaddr *) sa, &salen); if (error < 0 && errno == SOCKET_ERROR) { SU_DEBUG_1(("%s: getsockname() failed: %s\n", __func__, su_strerror(su_errno()))); } error = bind(s, (struct sockaddr *) sa, salen); if (error < 0) { SU_DEBUG_1(("%s: bind() failed: %s\n", __func__, su_strerror(su_errno()))); goto err; } su_close(s); scope = li_scope4(sa->su_sin.sin_addr.s_addr); if (scope == LI_SCOPE_HOST || scope == LI_SCOPE_LINK) gni_flags = NI_NUMERICHOST; if (su_xtra) { /* Map IPv4 address to IPv6 address */ memset(sa, 0, sizeof(*sa)); sa->su_family = AF_INET6; ((int32_t*)&sa->su_sin6.sin6_addr)[3] = sa->su_sin.sin_addr.s_addr; ((int32_t*)&sa->su_sin6.sin6_addr)[2] = htonl(0xffff); } li->li_family = sa->su_family; li->li_scope = scope; li->li_index = 0; li->li_addrlen = su_sockaddr_size(sa); li->li_addr = sa; if ((error = li_name(hints, gni_flags, sa, &canonname)) < 0) goto err; if (canonname) { if (strchr(canonname, ':') || strspn(canonname, "0123456789.") == strlen(canonname)) li->li_flags |= LI_NUMERIC; } else li->li_flags = 0; li->li_canonname = canonname; canonname = NULL; *rresult = li; return 0; }#endif# if HAVE_IFNUM /* Get the list of known IP address from the kernel */ if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0) { /* can't get number of interfaces -- fall back */ SU_DEBUG_1(("su_localinfo: SIOCGIFNUM failed: %s\n", su_strerror(su_errno()))); error = ELI_SYSTEM; goto err; } SU_DEBUG_9(("su_localinfo: %d active interfaces according to SIOCGIFNUM\n", numifs)); if (numifs < 0)# endif /* Default to 64 interfaces. Enough? */ numifs = 64; if (numifs == 0) return 0; /* * Allocate memory for SIOCGIFCONF ioctl buffer. This memory block is also * used as li_first, first localinfo struct that is returned, so it can be * freed by freelocalinfo() without any complications. */ ifc.ifc_len = numifs * sizeof (struct ifreq); buffer = malloc(sizeof(su_localinfo_t) + ifc.ifc_len + su_xtra); if (!buffer) { SU_DEBUG_1(("su_localinfo: memory exhausted\n")); error = ELI_MEMORY; goto err; } li_first = (su_localinfo_t *)buffer;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -