Added IPv6 support to SocketAddr and Socket classes.

git-svn-id: http://voip.null.ro/svn/yate@5656 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2013-09-30 13:15:59 +00:00
parent c1dcd17eea
commit bb49512cd1
3 changed files with 519 additions and 13 deletions

View File

@ -279,6 +279,8 @@ NETDB_FLAGS=""
AC_CHECK_FUNC([inet_ntop],[NETDB_FLAGS="$NETDB_FLAGS -DHAVE_NTOP"])
AC_CHECK_FUNC([inet_pton],[NETDB_FLAGS="$NETDB_FLAGS -DHAVE_PTON"])
AC_CHECK_FUNC([gethostbyname_r],[NETDB_FLAGS="$NETDB_FLAGS -DHAVE_GHBN_R"])
AC_CHECK_FUNC([gethostbyname2_r],[NETDB_FLAGS="$NETDB_FLAGS -DHAVE_GHBN2_R"])
AC_CHECK_FUNC([gethostbyname2],[NETDB_FLAGS="$NETDB_FLAGS -DHAVE_GHBN2"])
AC_SUBST(NETDB_FLAGS)
THREAD_KILL=""

View File

@ -43,6 +43,7 @@
#ifndef _WINDOWS
#include <net/if.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/un.h>
@ -106,6 +107,98 @@ static void epochToFt(unsigned int secEpoch, FILETIME& ft)
#endif
//
// IPv6 module functions
//
#ifdef AF_INET6
#if defined(HAVE_GHBN2_R) || defined(HAVE_GHBN2)
#define YATE_SOCKET_GHBN2_AVAILABLE
static inline bool ghbn2Set(struct sockaddr* addr, hostent* he, int family)
{
if (!(he && he->h_addrtype == family && he->h_addr_list))
return false;
char* val = he->h_addr_list[0];
if (!val)
return false;
if (family == AF_INET6) {
((struct sockaddr_in6*)addr)->sin6_addr = *(in6_addr*)val;
return true;
}
return false;
}
// Resolve an address using gethostbyname2_r or gethostbyname2
// Return 1 on success, 0 on failure, -1 if not available
static int resolveGHBN2(struct sockaddr* addr, const char* name)
{
if (!addr || TelEngine::null(name))
return 0;
int family = AF_INET6;
#ifdef HAVE_GHBN2_R
char buf[576];
struct hostent h;
struct hostent* hr = 0;
int errn = 0;
int r = gethostbyname2_r(name,family,&h,buf,sizeof(buf),&hr,&errn);
if (r != ERANGE) {
if (!r && ghbn2Set(addr,hr,family))
return 1;
return 0;
}
// Buffer too short: fallback to gethostbyname2 if available
#endif
#ifdef HAVE_GHBN2
Lock lck(s_mutex,MAX_RESWAIT);
if (lck.locked()) {
if (ghbn2Set(addr,gethostbyname2(name,family),family))
return 1;
}
else
Alarm("engine","socket",DebugWarn,"Resolver was busy, failing '%s'",name);
#else
#ifndef HAVE_GHBN2_R
return -1;
#endif
#endif
return 0;
}
#endif // defined(HAVE_GHBN2_R) || defined(HAVE_GHBN2)
// Resolve a domain to IPv6 address
static inline bool resolveIPv6(struct sockaddr* addr, const char* name)
{
static bool s_noIPv6 = true;
#ifdef YATE_SOCKET_GHBN2_AVAILABLE
int res = resolveGHBN2(addr,name);
if (res >= 0)
return res > 0;
#endif
// TODO: implement AF_INET6 resolving
if (s_noIPv6) {
s_noIPv6 = false;
Alarm("engine","socket",DebugWarn,"Resolver for %s is not available",
SocketAddr::lookupFamily(SocketAddr::IPv6));
}
return false;
}
#endif // AF_INET6
const String s_ipv4NullAddr = "0.0.0.0";
const String s_ipv6NullAddr = "::";
const TokenDict SocketAddr::s_familyName[] = {
{"Unknown", Unknown},
{"IPv4", IPv4},
{"IPv6", IPv6},
{"Unix", Unix},
{0,0},
};
SocketAddr::SocketAddr(const struct sockaddr* addr, socklen_t len)
: m_address(0), m_length(0)
{
@ -127,6 +220,7 @@ void SocketAddr::clear()
{
m_length = 0;
m_host.clear();
m_addr.clear();
void* tmp = m_address;
m_address = 0;
if (tmp)
@ -221,6 +315,34 @@ bool SocketAddr::host(const String& name)
return false;
if (name == m_host)
return true;
if (!m_address) {
int f = family(name);
switch (f) {
case Unix:
#ifdef HAS_AF_UNIX
if (assign(AF_UNIX) && host(name))
return true;
#endif
break;
case Unknown:
// fall through to set IP host
case IPv6:
#ifdef AF_INET6
if (assign(AF_INET6) && host(name))
return true;
#endif
if (f == IPv6)
break;
// fall through to IPv4
case IPv4:
if (assign(AF_INET) && host(name))
return true;
break;
}
// Restore data
clear();
return false;
}
switch (family()) {
case AF_INET:
{
@ -256,13 +378,29 @@ bool SocketAddr::host(const String& name)
break;
#ifdef AF_INET6
case AF_INET6:
if (name.find('%') >= 0) {
String tmp, iface;
splitIface(name,tmp,&iface);
if (!host(tmp))
return false;
if (iface)
#ifndef _WINDOWS
scopeId(if_nametoindex(iface));
#else
scopeId(iface.toInteger(0,0,0));
#endif
return true;
}
#ifdef HAVE_PTON
if (inet_pton(family(),name,&((struct sockaddr_in6*)m_address)->sin6_addr) > 0) {
stringify();
return true;
}
#endif
// TODO: implement AF_INET6 resolving
if (resolveIPv6(m_address,name)) {
stringify();
return true;
}
break;
#endif // AF_INET6
#ifdef HAS_AF_UNIX
@ -277,44 +415,185 @@ bool SocketAddr::host(const String& name)
return false;
}
void SocketAddr::stringify()
// Retrieve the family of an address
int SocketAddr::family(const String& addr)
{
m_host.clear();
if (!(m_length && m_address))
return;
switch (family()) {
if (!addr)
return Unknown;
bool ipv6 = false;
for (unsigned int i = 0; i < addr.length(); i++) {
if (addr[i] == '/')
return Unix;
if (addr[i] == ':')
ipv6 = true;
}
if (ipv6)
return IPv6;
in_addr_t a = inet_addr(addr);
if (a != INADDR_NONE || addr == YSTRING("255.255.255.255"))
return IPv4;
return Unknown;
}
// Convert the host address to a String
bool SocketAddr::stringify(String& s, struct sockaddr* addr)
{
if (!addr)
return false;
switch (addr->sa_family) {
case AF_INET:
#ifdef HAVE_NTOP
{
char buf[16];
buf[0] = '\0';
m_host = inet_ntop(family(),&((struct sockaddr_in*)m_address)->sin_addr,
s = inet_ntop(addr->sa_family,&((struct sockaddr_in*)addr)->sin_addr,
buf,sizeof(buf));
}
#else
s_mutex.lock();
m_host = inet_ntoa(((struct sockaddr_in*)m_address)->sin_addr);
s = inet_ntoa(((struct sockaddr_in*)addr)->sin_addr);
s_mutex.unlock();
#endif
break;
return true;
#ifdef AF_INET6
case AF_INET6:
#ifdef HAVE_NTOP
{
char buf[48];
buf[0] = '\0';
m_host = inet_ntop(family(),&((struct sockaddr_in6*)m_address)->sin6_addr,
s = inet_ntop(addr->sa_family,&((struct sockaddr_in6*)addr)->sin6_addr,
buf,sizeof(buf));
}
return true;
#endif
break;
#endif // AF_INET6
#ifdef HAS_AF_UNIX
case AF_UNIX:
m_host = ((struct sockaddr_un*)m_address)->sun_path;
break;
s = ((struct sockaddr_un*)addr)->sun_path;
return true;
#endif
}
return false;
}
// Append an address to a buffer
String& SocketAddr::appendAddr(String& buf, const String& addr, int family)
{
if (!addr)
return buf;
// Address already starts with [
if (addr[0] == '[') {
buf << addr;
return buf;
}
if (family == Unknown && addr.find(':') >= 0)
family = IPv6;
if (family != IPv6)
buf << addr;
else
buf << "[" << addr << "]";
return buf;
}
// Check if an address is empty or null
bool SocketAddr::isNullAddr(const String& addr, int family)
{
if (!addr)
return true;
switch (family) {
case IPv4:
return addr == s_ipv4NullAddr;
case IPv6:
return addr == s_ipv6NullAddr;
}
return addr == s_ipv4NullAddr || addr == s_ipv6NullAddr;
}
// Split an interface from address
// An interface may be present in addr after a percent char (e.g. fe80::23%eth0)
void SocketAddr::splitIface(const String& buf, String& addr, String* iface)
{
if (!buf) {
addr.clear();
if (iface)
iface->clear();
return;
}
int pos = buf.find('%');
if (pos < 0) {
if (iface)
iface->clear();
addr = buf;
}
else {
if (iface)
*iface = buf.substr(pos + 1);
addr = buf.substr(0,pos);
}
}
// Split an address into ip/port
// Handle addr, addr:port, [addr], [addr]:port
void SocketAddr::split(const String& buf, String& addr, int& port, bool portPresent)
{
if (!buf) {
addr.clear();
return;
}
if (buf[0] == '[') {
int p = buf.find(']',1);
if (p >= 1) {
if (p < ((int)buf.length() - 1) && buf[p + 1] == ':')
port = buf.substr(p + 2).toInteger();
addr.assign(buf.c_str() + 1,p - 1);
return;
}
}
int p = buf.find(':');
if (p >= 0) {
// Check for a second ':': it may be an IPv6 address
// or we expect a port at the end of an IPv6 address
int p2 = buf.rfind(':');
if (p == p2 || portPresent) {
port = buf.substr(p2 + 1).toInteger();
addr.assign(buf.c_str(),p2);
}
else
addr = buf;
}
else
addr = buf;
}
const String& SocketAddr::ipv4NullAddr()
{
return s_ipv4NullAddr;
}
const String& SocketAddr::ipv6NullAddr()
{
return s_ipv6NullAddr;
}
const TokenDict* SocketAddr::dictFamilyName()
{
return s_familyName;
}
void SocketAddr::stringify()
{
m_host.clear();
m_addr.clear();
if (m_length && m_address)
stringify(m_host,m_address);
}
// Store host:port in m_addr
void SocketAddr::updateAddr() const
{
m_addr.clear();
appendTo(m_addr,host(),port(),family());
}
int SocketAddr::port() const
@ -348,6 +627,7 @@ bool SocketAddr::port(int newport)
default:
return false;
}
m_addr.clear();
return true;
}

View File

@ -5451,6 +5451,26 @@ class Socket;
class YATE_API SocketAddr : public GenObject
{
public:
/**
* Known address families
*/
enum Family {
Unknown = AF_UNSPEC,
IPv4 = AF_INET,
AfMax = AF_MAX,
AfUnsupported = AfMax,
#ifdef AF_INET6
IPv6 = AF_INET6,
#else
IPv6 = AfUnsupported + 1,
#endif
#ifdef HAS_AF_UNIX
Unix = AF_UNIX,
#else
Unix = AfUnsupported + 2,
#endif
};
/**
* Default constructor of an empty address
*/
@ -5554,6 +5574,28 @@ public:
inline int family() const
{ return m_address ? m_address->sa_family : 0; }
/**
* Retrieve address family name
* @return Address family name
*/
inline const char* familyName()
{ return lookupFamily(family()); }
/**
* Retrieve the sin6_scope_id value of an IPv6 address
* @return The requested value (it may be 0), 0 if not available
*/
inline unsigned int scopeId() const
{ return scopeId(address()); }
/**
* Set the sin6_scope_id value of an IPv6 address
* @param val Value to set
* @return True on success, false if not available
*/
inline bool scopeId(unsigned int val)
{ return scopeId(address(),val); }
/**
* Get the host of this address
* @return Host name as String
@ -5562,7 +5604,19 @@ public:
{ return m_host; }
/**
* Set the hostname of this address
* Get the host and port of this address
* @return Address String (host:port)
*/
inline const String& addr() const {
if (!m_addr)
updateAddr();
return m_addr;
}
/**
* Set the hostname of this address.
* Guess address family if not initialized
* @param name Host to set
* @return True if new host set, false if name could not be parsed
*/
virtual bool host(const String& name);
@ -5594,6 +5648,13 @@ public:
inline socklen_t length() const
{ return m_length; }
/**
* Check if this address is empty or null
* @return True if the address is empty or '0.0.0.0' (IPv4) or '::' IPv6
*/
inline bool isNullAddr() const
{ return isNullAddr(m_host,family()); }
/**
* Check if an address family is supported by the library
* @param family Family of the address to check
@ -5601,15 +5662,161 @@ public:
*/
static bool supports(int family);
/**
* Retrieve the family of an address
* @param addr The address to check
* @return Address family
*/
static int family(const String& addr);
/**
* Convert the host address to a String
* @param buf Destination buffer
* @param addr Socket address
* @return True on success, false if address family is not supported
*/
static bool stringify(String& buf, struct sockaddr* addr);
/**
* Retrieve the scope id value of an IPv6 address
* @param addr The address
* @return The requested value (it may be 0), 0 if not available
*/
static inline unsigned int scopeId(struct sockaddr* addr) {
#ifdef AF_INET6
if (addr && addr->sa_family == AF_INET6)
return ((struct sockaddr_in6*)addr)->sin6_scope_id;
#endif
return 0;
}
/**
* Set the scope id value of an IPv6 address
* @param addr Address to set
* @param val Value to set
* @return True on success, false if not available
*/
static inline bool scopeId(struct sockaddr* addr, unsigned int val) {
#ifdef AF_INET6
if (addr && addr->sa_family == AF_INET6) {
((struct sockaddr_in6*)addr)->sin6_scope_id = val;
return true;
}
#endif
return false;
}
/**
* Append an address to a buffer
* @param buf Destination buffer
* @param addr Address to append
* @param family Address family, set it to Unknown to detect
* @return Buffer address
*/
static String& appendAddr(String& buf, const String& addr, int family = Unknown);
/**
* Append an address to a buffer in the form addr:port
* @param buf Destination buffer
* @param addr Address to append
* @param port Port to append
* @param family Address family, set it to Unknown to detect
* @return Buffer address
*/
static inline String& appendTo(String& buf, const String& addr, int port,
int family = Unknown) {
appendAddr(buf,addr,family) << ":" << port;
return buf;
}
/**
* Append an address to a buffer in the form addr:port
* @param addr Address to append
* @param port Port to append
* @param family Address family, set it to Unknown to detect
* @return A String with concatenated address and port
*/
static inline String appendTo(const String& addr, int port, int family = Unknown) {
String buf;
appendTo(buf,addr,port,family);
return buf;
}
/**
* Check if an address is empty or null
* @param addr Address to check
* @param family Address family, set it to Unknown to detect
* @return True if the address is empty or '0.0.0.0' (IPv4) or '::' IPv6
*/
static bool isNullAddr(const String& addr, int family = Unknown);
/**
* Split an interface from address
* An interface may be present in addr after a percent char (e.g. fe80::23%eth0)
* It is safe call this method with the same destination and source string
* @param buf Source buffer
* @param addr Destination buffer for address
* @param iface Optional pointer to be filled with interface name
*/
static void splitIface(const String& buf, String& addr, String* iface = 0);
/**
* Split an address into ip/port.
* Handled formats: addr, addr:port, [addr], [addr]:port
* It is safe call this method with the same destination and source string
* @param buf Source buffer
* @param addr Destination buffer for address
* @param port Destination port
* @param portPresent Set it to true if the port is always present after the last ':'.
* This will handle IPv6 addresses without square brackets and port present
* (e.g. fe80::23:5060 will split into addr=fe80::23 and port=5060)
*/
static void split(const String& buf, String& addr, int& port, bool portPresent = false);
/**
* Retrieve address family name
* @param family Address family to retrieve
* @return Address family name
*/
static inline const char* lookupFamily(int family)
{ return lookup(family,s_familyName); }
/**
* Retrieve IPv4 null address
* @return IPv4 null address (0.0.0.0)
*/
static const String& ipv4NullAddr();
/**
* Retrieve IPv6 null address
* @return IPv6 null address (::)
*/
static const String& ipv6NullAddr();
/**
* Retrieve the family name dictionary
* @return Pointer to family name dictionary
*/
static const TokenDict* dictFamilyName();
protected:
/**
* Convert the host address to a String stored in m_host
*/
virtual void stringify();
/**
* Store host:port in m_addr
*/
virtual void updateAddr() const;
struct sockaddr* m_address;
socklen_t m_length;
String m_host;
mutable String m_addr;
private:
static const TokenDict s_familyName[];
};
/**
@ -6274,6 +6481,23 @@ public:
*/
virtual bool setOption(int level, int name, const void* value = 0, socklen_t length = 0);
/**
* Set or reset socket IPv6 only option.
* This option will tell to an IPv6 socket to accept only IPv6 packets.
* IPv4 packets will be accepted if disabled.
* This method will fail for non PF_INET6 sockets
* @param on True to set, false to reset it
* @return True if operation was successfull, false if an error occured
*/
inline bool setIpv6OnlyOption(bool on) {
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
int value = on ? 1 : 0;
return setOption(IPPROTO_IPV6,IPV6_V6ONLY,&value,sizeof(value));
#else
return false;
#endif
}
/**
* Get socket options
* @param level Level of the option to set