mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
426 lines
12 KiB
C++
426 lines
12 KiB
C++
/*
|
|
* SRT - Secure, Reliable, Transport
|
|
* Copyright (c) 2018 Haivision Systems Inc.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
written by
|
|
Haivision Systems Inc.
|
|
*****************************************************************************/
|
|
|
|
#ifndef INC_SRT_NETINET_ANY_H
|
|
#define INC_SRT_NETINET_ANY_H
|
|
|
|
#include <cstring> // memcmp
|
|
#include <string>
|
|
#include <sstream>
|
|
#include "platform_sys.h"
|
|
|
|
// This structure should replace every use of sockaddr and its currently
|
|
// used specializations, sockaddr_in and sockaddr_in6. This is to simplify
|
|
// the use of the original BSD API that relies on type-violating type casts.
|
|
// You can use the instances of sockaddr_any in every place where sockaddr is
|
|
// required.
|
|
|
|
namespace srt
|
|
{
|
|
|
|
struct sockaddr_any
|
|
{
|
|
union
|
|
{
|
|
sockaddr_in sin;
|
|
sockaddr_in6 sin6;
|
|
sockaddr sa;
|
|
};
|
|
|
|
// The type is intended to be the same as the length
|
|
// parameter in ::accept, ::bind and ::connect functions.
|
|
|
|
// This is the type used by SRT.
|
|
typedef int len_t;
|
|
|
|
// This is the type used by system functions
|
|
#ifdef _WIN32
|
|
typedef int syslen_t;
|
|
#else
|
|
typedef socklen_t syslen_t;
|
|
#endif
|
|
|
|
// Note: by having `len_t` type here the usage in
|
|
// API functions is here limited to SRT. For system
|
|
// functions you can pass the address here as (socklen_t*)&sa.len,
|
|
// but just do it on your own risk, as there's no guarantee
|
|
// that sizes of `int` and `socklen_t` do not differ. The safest
|
|
// way seems to be using an intermediate proxy to be written
|
|
// back here from the value of `syslen_t`.
|
|
len_t len;
|
|
|
|
struct SysLenWrapper
|
|
{
|
|
syslen_t syslen;
|
|
len_t& backwriter;
|
|
syslen_t* operator&() { return &syslen; }
|
|
|
|
SysLenWrapper(len_t& source): syslen(source), backwriter(source)
|
|
{
|
|
}
|
|
|
|
~SysLenWrapper()
|
|
{
|
|
backwriter = syslen;
|
|
}
|
|
};
|
|
|
|
// Usage:
|
|
// ::accept(lsn_sock, sa.get(), &sa.syslen());
|
|
SysLenWrapper syslen()
|
|
{
|
|
return SysLenWrapper(len);
|
|
}
|
|
|
|
static size_t storage_size()
|
|
{
|
|
typedef union
|
|
{
|
|
sockaddr_in sin;
|
|
sockaddr_in6 sin6;
|
|
sockaddr sa;
|
|
} ucopy;
|
|
return sizeof (ucopy);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
// sin6 is the largest field
|
|
memset((&sin6), 0, sizeof sin6);
|
|
len = 0;
|
|
}
|
|
|
|
// Default domain is unspecified, and
|
|
// in this case the size is 0.
|
|
// Note that AF_* (and alias PF_*) types have
|
|
// many various values, of which only
|
|
// AF_INET and AF_INET6 are handled here.
|
|
// Others make the same effect as unspecified.
|
|
explicit sockaddr_any(int domain = AF_UNSPEC)
|
|
{
|
|
// Default domain is "unspecified", 0
|
|
reset();
|
|
|
|
// Overriding family as required in the parameters
|
|
// and the size then accordingly.
|
|
sa.sa_family = domain == AF_INET || domain == AF_INET6 ? domain : AF_UNSPEC;
|
|
switch (domain)
|
|
{
|
|
case AF_INET:
|
|
len = len_t(sizeof (sockaddr_in));
|
|
break;
|
|
|
|
// Use size of sin6 as the default size
|
|
// len must be properly set so that the
|
|
// family-less sockaddr is passed to bind/accept
|
|
default:
|
|
len = len_t(sizeof (sockaddr_in6));
|
|
break;
|
|
}
|
|
}
|
|
|
|
sockaddr_any(const sockaddr_storage& stor)
|
|
{
|
|
// Here the length isn't passed, so just rely on family.
|
|
set((const sockaddr*)&stor);
|
|
}
|
|
|
|
sockaddr_any(const sockaddr* source, len_t namelen = 0)
|
|
{
|
|
if (namelen == 0)
|
|
set(source);
|
|
else
|
|
set(source, namelen);
|
|
}
|
|
|
|
void set(const sockaddr* source)
|
|
{
|
|
// Less safe version, simply trust the caller that the
|
|
// memory at 'source' is also large enough to contain
|
|
// all data required for particular family.
|
|
if (source->sa_family == AF_INET)
|
|
{
|
|
memcpy((&sin), source, sizeof sin);
|
|
len = sizeof sin;
|
|
}
|
|
else if (source->sa_family == AF_INET6)
|
|
{
|
|
memcpy((&sin6), source, sizeof sin6);
|
|
len = sizeof sin6;
|
|
}
|
|
else
|
|
{
|
|
// Error fallback: no other families than IP are regarded.
|
|
// Note: socket set up this way isn't intended to be used
|
|
// for bind/accept.
|
|
sa.sa_family = AF_UNSPEC;
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
void set(const sockaddr* source, syslen_t namelen)
|
|
{
|
|
// It's not safe to copy it directly, so check.
|
|
if (source->sa_family == AF_INET && namelen >= syslen_t(sizeof sin))
|
|
{
|
|
memcpy((&sin), source, sizeof sin);
|
|
len = sizeof sin;
|
|
}
|
|
else if (source->sa_family == AF_INET6 && namelen >= syslen_t(sizeof sin6))
|
|
{
|
|
// Note: this isn't too safe, may crash for stupid values
|
|
// of source->sa_family or any other data
|
|
// in the source structure, so make sure it's correct first.
|
|
memcpy((&sin6), source, sizeof sin6);
|
|
len = sizeof sin6;
|
|
}
|
|
else
|
|
{
|
|
reset();
|
|
}
|
|
}
|
|
|
|
void set(const sockaddr_in& in4)
|
|
{
|
|
memcpy((&sin), &in4, sizeof in4);
|
|
len = sizeof in4;
|
|
}
|
|
|
|
void set(const sockaddr_in6& in6)
|
|
{
|
|
memcpy((&sin6), &in6, sizeof in6);
|
|
len = sizeof in6;
|
|
}
|
|
|
|
sockaddr_any(const in_addr& i4_adr, uint16_t port)
|
|
{
|
|
// Some cases require separately IPv4 address passed as in_addr,
|
|
// so port is given separately.
|
|
sa.sa_family = AF_INET;
|
|
sin.sin_addr = i4_adr;
|
|
sin.sin_port = htons(port);
|
|
len = sizeof sin;
|
|
}
|
|
|
|
sockaddr_any(const in6_addr& i6_adr, uint16_t port)
|
|
{
|
|
sa.sa_family = AF_INET6;
|
|
sin6.sin6_addr = i6_adr;
|
|
sin6.sin6_port = htons(port);
|
|
len = sizeof sin6;
|
|
}
|
|
|
|
static len_t size(int family)
|
|
{
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
return len_t(sizeof (sockaddr_in));
|
|
|
|
case AF_INET6:
|
|
return len_t(sizeof (sockaddr_in6));
|
|
|
|
default:
|
|
return 0; // fallback
|
|
}
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
bool isempty = true; // unspec-family address is always empty
|
|
|
|
if (sa.sa_family == AF_INET)
|
|
{
|
|
isempty = (sin.sin_port == 0
|
|
&& sin.sin_addr.s_addr == 0);
|
|
}
|
|
else if (sa.sa_family == AF_INET6)
|
|
{
|
|
isempty = (sin6.sin6_port == 0
|
|
&& memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any) == 0);
|
|
}
|
|
// otherwise isempty stays with default false
|
|
return isempty;
|
|
}
|
|
|
|
len_t size() const
|
|
{
|
|
return size(sa.sa_family);
|
|
}
|
|
|
|
int family() const { return sa.sa_family; }
|
|
void family(int val)
|
|
{
|
|
sa.sa_family = val;
|
|
len = size();
|
|
}
|
|
|
|
// port is in exactly the same location in both sin and sin6
|
|
// and has the same size. This is actually yet another common
|
|
// field, just not mentioned in the sockaddr structure.
|
|
uint16_t& r_port() { return sin.sin_port; }
|
|
uint16_t r_port() const { return sin.sin_port; }
|
|
int hport() const { return ntohs(sin.sin_port); }
|
|
|
|
void hport(int value)
|
|
{
|
|
// Port is fortunately located at the same position
|
|
// in both sockaddr_in and sockaddr_in6 and has the
|
|
// same size.
|
|
sin.sin_port = htons(value);
|
|
}
|
|
|
|
sockaddr* get() { return &sa; }
|
|
const sockaddr* get() const { return &sa; }
|
|
|
|
// Sometimes you need to get the address
|
|
// the way suitable for e.g. inet_ntop.
|
|
const void* get_addr() const
|
|
{
|
|
if (sa.sa_family == AF_INET)
|
|
return &sin.sin_addr.s_addr;
|
|
|
|
if (sa.sa_family == AF_INET6)
|
|
return &sin6.sin6_addr;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void* get_addr()
|
|
{
|
|
const sockaddr_any* that = this;
|
|
return (void*)that->get_addr();
|
|
}
|
|
|
|
template <int> struct TypeMap;
|
|
|
|
template <int af_domain>
|
|
typename TypeMap<af_domain>::type& get();
|
|
|
|
struct Equal
|
|
{
|
|
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
|
{
|
|
if (c1.family() != c2.family())
|
|
return false;
|
|
|
|
// Cannot use memcmp due to having in some systems
|
|
// another field like sockaddr_in::sin_len. This exists
|
|
// in some BSD-derived systems, but is not required by POSIX.
|
|
// Therefore sockaddr_any class cannot operate with it,
|
|
// as in this situation it would be safest to state that
|
|
// particular implementations may have additional fields
|
|
// of different purpose beside those required by POSIX.
|
|
//
|
|
// The only reliable way to compare two underlying sockaddr
|
|
// object is then to compare the port value and the address
|
|
// value.
|
|
//
|
|
// Fortunately the port is 16-bit and located at the same
|
|
// offset in both sockaddr_in and sockaddr_in6.
|
|
|
|
return c1.sin.sin_port == c2.sin.sin_port
|
|
&& c1.equal_address(c2);
|
|
}
|
|
};
|
|
|
|
struct EqualAddress
|
|
{
|
|
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
|
{
|
|
if ( c1.sa.sa_family == AF_INET )
|
|
{
|
|
return c1.sin.sin_addr.s_addr == c2.sin.sin_addr.s_addr;
|
|
}
|
|
|
|
if ( c1.sa.sa_family == AF_INET6 )
|
|
{
|
|
return memcmp(&c1.sin6.sin6_addr, &c2.sin6.sin6_addr, sizeof (in6_addr)) == 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
bool equal_address(const sockaddr_any& rhs) const
|
|
{
|
|
return EqualAddress()(*this, rhs);
|
|
}
|
|
|
|
struct Less
|
|
{
|
|
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
|
{
|
|
return memcmp(&c1, &c2, sizeof(c1)) < 0;
|
|
}
|
|
};
|
|
|
|
// Tests if the current address is the "any" wildcard.
|
|
bool isany() const
|
|
{
|
|
if (sa.sa_family == AF_INET)
|
|
return sin.sin_addr.s_addr == INADDR_ANY;
|
|
|
|
if (sa.sa_family == AF_INET6)
|
|
return memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Debug support
|
|
std::string str() const
|
|
{
|
|
if (family() != AF_INET && family() != AF_INET6)
|
|
return "unknown:0";
|
|
|
|
std::ostringstream output;
|
|
char hostbuf[1024];
|
|
int flags;
|
|
|
|
#if ENABLE_GETNAMEINFO
|
|
flags = NI_NAMEREQD;
|
|
#else
|
|
flags = NI_NUMERICHOST | NI_NUMERICSERV;
|
|
#endif
|
|
|
|
if (!getnameinfo(get(), size(), hostbuf, 1024, NULL, 0, flags))
|
|
{
|
|
output << hostbuf;
|
|
}
|
|
|
|
output << ":" << hport();
|
|
return output.str();
|
|
}
|
|
|
|
bool operator==(const sockaddr_any& other) const
|
|
{
|
|
return Equal()(*this, other);
|
|
}
|
|
|
|
bool operator!=(const sockaddr_any& other) const { return !(*this == other); }
|
|
};
|
|
|
|
template<> struct sockaddr_any::TypeMap<AF_INET> { typedef sockaddr_in type; };
|
|
template<> struct sockaddr_any::TypeMap<AF_INET6> { typedef sockaddr_in6 type; };
|
|
|
|
template <>
|
|
inline sockaddr_any::TypeMap<AF_INET>::type& sockaddr_any::get<AF_INET>() { return sin; }
|
|
template <>
|
|
inline sockaddr_any::TypeMap<AF_INET6>::type& sockaddr_any::get<AF_INET6>() { return sin6; }
|
|
|
|
} // namespace srt
|
|
|
|
#endif
|