#include "sock.h"
static inline void hex_dump(uint8_t* buf, size_t len)
{
if (!buf)
return;
for (size_t i = 0; i < len; i += 10) {
fprintf(stderr, "\t");
for (size_t k = i; k < i + 10; ++k) {
fprintf(stderr, "0x%02x ", buf[k]);
}
fprintf(stderr, "\n");
}
}
static inline void set_bytes(int* ptr, int nbytes)
{
if (ptr)
* ptr = nbytes;
}
static inline void* memdup(const void* src, size_t len)
{
uint8_t* dst = new uint8_t[len];
std::memcpy(dst, src, len);
return dst;
}
#ifdef _WIN32
static inline void win_get_last_error(void)
{
wchar_t* s = NULL;
FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&s, 0, NULL
);
fprintf(stderr, "%S %d\n", s, WSAGetLastError());
LocalFree(s);
}
#endif
#include <thread>
#ifdef _WIN32
#include <Windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#else
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netdb.h>
#endif
#if defined(__MINGW32__) || defined(__MINGW64__)
#include "mingw_inet.hh"
using namespace mingw;
#endif
#include <cstring>
#include <cassert>
#define WSABUF_SIZE 256
socket::socket(int rce_flags) :
socket_(0),
local_address_(),
local_ip6_address_(),
ipv6_(false),
rce_flags_(rce_flags),
#ifdef _WIN32
buffers_()
#else
header_(),
chunks_()
#endif
{}
socket::~socket()
{
// printf("Socket total sent packets is %lu and received packets is %lu", sent_packets_, received_packets_);
#ifndef _WIN32
close(socket_);
#else
closesocket(socket_);
#endif
}
rtp_error_t socket::init(short family, int type, int protocol)
{
if (family == AF_INET6) {
ipv6_ = true;
}
else {
ipv6_ = false;
}
#ifdef _WIN32
if ((socket_ = ::socket(family, type, protocol)) == INVALID_SOCKET) {
win_get_last_error();
#else
if ((socket_ = ::socket(family, type, protocol)) < 0) {
printf("Failed to create socket: %s", strerror(errno));
#endif
return -1;
}
#ifdef _WIN32
BOOL bNewBehavior = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(socket_, _WSAIOW(IOC_VENDOR, 12), &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);
#endif
return 0;
}
rtp_error_t socket::setsockopt(int level, int optname, const void* optval, socklen_t optlen)
{
std::lock_guard<std::mutex> lg(conf_mutex_);
if (::setsockopt(socket_, level, optname, (const char*)optval, optlen) < 0) {
//strerror(errno), depricated
printf("Failed to set socket options");
return -1;
}
return 0;
}
rtp_error_t socket::bind(short family, unsigned host, short port)
{
assert(family == AF_INET);
local_address_ = create_sockaddr(family, host, port);
return bind(local_address_);
}
bool socket::is_multicast(sockaddr_in & local_address)
{
// Multicast addresses ranges from 224.0.0.0 to 239.255.255.255 (0xE0000000 to 0xEFFFFFFF)
auto addr = local_address.sin_addr.s_addr;
return (ntohl(addr) & 0xF0000000) == 0xE0000000;
}
rtp_error_t socket::bind(sockaddr_in & local_address)
{
local_address_ = local_address;
printf("Binding to address %s", sockaddr_to_string(local_address_).c_str());
if (!socket::is_multicast(local_address_)) {
// Regular address
if (::bind(socket_, (struct sockaddr*)&local_address_, sizeof(local_address_)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Binding to port %u failed!", ntohs(local_address_.sin_port));
return -1;
}
}
else {
// Multicast address
// Reuse address to enabled receiving the same stream multiple times
const int enable = 1;
if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Reuse address failed!");
}
// Bind with empty address
auto bind_addr_in = local_address_;
bind_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
if (::bind(socket_, (struct sockaddr*)&bind_addr_in, sizeof(bind_addr_in)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Binding to port %u failed!", ntohs(bind_addr_in.sin_port));
return -1;
}
// Join multicast membership
struct ip_mreq mreq {};
mreq.imr_multiaddr.s_addr = local_address_.sin_addr.s_addr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (::setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Multicast join failed!");
return -1;
}
}
return 0;
}
bool socket::is_multicast(sockaddr_in6 & local_address)
{
// Multicast IP addresses have their first byte equals to 0xFF
auto addr = local_address.sin6_addr.s6_addr;
return addr[0] == 0xFF;
}
rtp_error_t socket::bind_ip6(sockaddr_in6 & local_address)
{
local_ip6_address_ = local_address;
printf("Binding to address %s", sockaddr_ip6_to_string(local_ip6_address_).c_str());
if (!socket::is_multicast(local_ip6_address_)) {
if (::bind(socket_, (struct sockaddr*)&local_ip6_address_, sizeof(local_ip6_address_)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Binding to port %u failed!", ntohs(local_ip6_address_.sin6_port));
return -1;
}
}
else {
// Multicast address
// Reuse address to enabled receiving the same stream multiple times
const int enable = 1;
if (::setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Reuse address failed!");
}
// Bind with empty address
auto bind_addr_in = local_ip6_address_;
bind_addr_in.sin6_addr = in6addr_any;
if (::bind(socket_, (struct sockaddr*)&bind_addr_in, sizeof(bind_addr_in)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Binding to port %u failed!", ntohs(bind_addr_in.sin6_port));
return -1;
}
// Join multicast membership
struct ipv6_mreq mreq {};
memcpy(&mreq.ipv6mr_multiaddr, &local_ip6_address_.sin6_addr, sizeof(mreq.ipv6mr_multiaddr));
if (::setsockopt(socket_, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&mreq, sizeof(mreq)) < 0) {
#ifdef _WIN32
win_get_last_error();
#else
fprintf(stderr, "%s\n", strerror(errno));
#endif
printf("Multicast join failed!");
return -1;
}
}
return 0;
}
int socket::check_family(std::string addr)
{
// Use getaddrinfo() to determine whether we are using ipv4 or ipv6 addresses
struct addrinfo hint, * res = NULL;
memset(&hint, '\0', sizeof(hint));
hint.ai_family = PF_UNSPE