540 lines
16 KiB
C++
540 lines
16 KiB
C++
/*
|
|
Copyright (C) 2008-2011 Romain Moret at Grame
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
#include "JackNetUnixSocket.h"
|
|
#include "JackError.h"
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
|
|
using namespace std;
|
|
|
|
namespace Jack
|
|
{
|
|
//utility *********************************************************************************************************
|
|
int GetHostName(char * name, int size)
|
|
{
|
|
if (gethostname(name, size) == SOCKET_ERROR) {
|
|
jack_error("Can't get 'hostname' : %s", strerror(NET_ERROR_CODE));
|
|
strcpy(name, "default");
|
|
return SOCKET_ERROR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//construct/destruct***********************************************************************************************
|
|
JackNetUnixSocket::JackNetUnixSocket()
|
|
{
|
|
fSockfd = 0;
|
|
fPort = 0;
|
|
fTimeOut = 0;
|
|
fSendAddr.sin_family = AF_INET;
|
|
fSendAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
memset(&fSendAddr.sin_zero, 0, 8);
|
|
fRecvAddr.sin_family = AF_INET;
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
memset(&fRecvAddr.sin_zero, 0, 8);
|
|
}
|
|
|
|
JackNetUnixSocket::JackNetUnixSocket(const char* ip, int port)
|
|
{
|
|
fSockfd = 0;
|
|
fPort = port;
|
|
fTimeOut = 0;
|
|
fSendAddr.sin_family = AF_INET;
|
|
fSendAddr.sin_port = htons(port);
|
|
inet_aton(ip, &fSendAddr.sin_addr);
|
|
memset(&fSendAddr.sin_zero, 0, 8);
|
|
fRecvAddr.sin_family = AF_INET;
|
|
fRecvAddr.sin_port = htons(port);
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
memset(&fRecvAddr.sin_zero, 0, 8);
|
|
}
|
|
|
|
JackNetUnixSocket::JackNetUnixSocket(const JackNetUnixSocket& socket)
|
|
{
|
|
fSockfd = 0;
|
|
fTimeOut = 0;
|
|
fPort = socket.fPort;
|
|
fSendAddr = socket.fSendAddr;
|
|
fRecvAddr = socket.fRecvAddr;
|
|
}
|
|
|
|
JackNetUnixSocket::~JackNetUnixSocket()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
JackNetUnixSocket& JackNetUnixSocket::operator=(const JackNetUnixSocket& socket)
|
|
{
|
|
if (this != &socket) {
|
|
fSockfd = 0;
|
|
fPort = socket.fPort;
|
|
fSendAddr = socket.fSendAddr;
|
|
fRecvAddr = socket.fRecvAddr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//socket***********************************************************************************************************
|
|
int JackNetUnixSocket::NewSocket()
|
|
{
|
|
if (fSockfd) {
|
|
Close();
|
|
Reset();
|
|
}
|
|
fSockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
/* Enable address reuse */
|
|
int res, on = 1;
|
|
#ifdef __APPLE__
|
|
if ((res = setsockopt(fSockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) < 0) {
|
|
#else
|
|
if ((res = setsockopt(fSockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
|
|
#endif
|
|
StrError(NET_ERROR_CODE);
|
|
}
|
|
|
|
int tos = 0; /* see <netinet/in.h> */
|
|
|
|
/*
|
|
DSCP Field Hex/Bin/Dec Layer 2 Prio Traffic Type Acronym WMM Access Category
|
|
0x38 / 111000 / 56 7 Network Control NC AC_VO
|
|
0x30 / 110000 / 48 6 Voice VO AC_VO
|
|
0x28 / 101000 / 40 5 Video VI AC_VI
|
|
0x20 / 100000 / 32 4 Controlled Load CL AC_VI
|
|
0x18 / 011000 / 24 3 Excellent Effort EE AC_BE
|
|
0x10 / 010000 / 16 2 Spare -- AC_BK
|
|
0x08 / 001000 / 8 1 Background BK AC_BK
|
|
0x00 / 000000 / 0 0 Best Effort BE AC_BE
|
|
*/
|
|
|
|
socklen_t len = sizeof(tos);
|
|
|
|
res = getsockopt(fSockfd, IPPROTO_IP, IP_TOS, &tos, &len);
|
|
|
|
tos = 46 * 4; // see <netinet/in.h>
|
|
res = setsockopt(fSockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
|
|
|
|
return fSockfd;
|
|
}
|
|
|
|
bool JackNetUnixSocket::IsLocal(char* ip)
|
|
{
|
|
if (strcmp(ip, "127.0.0.1") == 0) {
|
|
return true;
|
|
}
|
|
|
|
char host_name[32];
|
|
GetHostName(host_name, sizeof(host_name));
|
|
|
|
struct hostent* host = gethostbyname(host_name);
|
|
if (host) {
|
|
for (int i = 0; host->h_addr_list[i] != 0; ++i) {
|
|
struct in_addr addr;
|
|
memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr));
|
|
if (strcmp(inet_ntoa(addr), ip) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int JackNetUnixSocket::Bind()
|
|
{
|
|
return ::bind(fSockfd, reinterpret_cast<socket_address_t*>(&fRecvAddr), sizeof(socket_address_t));
|
|
}
|
|
|
|
int JackNetUnixSocket::BindWith(const char* ip)
|
|
{
|
|
int addr_conv = inet_aton(ip, &fRecvAddr.sin_addr);
|
|
if (addr_conv < 0) {
|
|
return addr_conv;
|
|
}
|
|
return Bind();
|
|
}
|
|
|
|
int JackNetUnixSocket::BindWith(int port)
|
|
{
|
|
fRecvAddr.sin_port = htons(port);
|
|
return Bind();
|
|
}
|
|
|
|
int JackNetUnixSocket::Connect()
|
|
{
|
|
return connect(fSockfd, reinterpret_cast<socket_address_t*>(&fSendAddr), sizeof(socket_address_t));
|
|
}
|
|
|
|
int JackNetUnixSocket::ConnectTo(const char* ip)
|
|
{
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr);
|
|
if (addr_conv < 0) {
|
|
return addr_conv;
|
|
}
|
|
return Connect();
|
|
}
|
|
|
|
void JackNetUnixSocket::Close()
|
|
{
|
|
if (fSockfd) {
|
|
close(fSockfd);
|
|
}
|
|
fSockfd = 0;
|
|
}
|
|
|
|
void JackNetUnixSocket::Reset()
|
|
{
|
|
fSendAddr.sin_family = AF_INET;
|
|
fSendAddr.sin_port = htons(fPort);
|
|
fSendAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
memset(&fSendAddr.sin_zero, 0, 8);
|
|
fRecvAddr.sin_family = AF_INET;
|
|
fRecvAddr.sin_port = htons(fPort);
|
|
fRecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
memset(&fRecvAddr.sin_zero, 0, 8);
|
|
}
|
|
|
|
bool JackNetUnixSocket::IsSocket()
|
|
{
|
|
return(fSockfd) ? true : false;
|
|
}
|
|
|
|
//IP/PORT***********************************************************************************************************
|
|
void JackNetUnixSocket::SetPort(int port)
|
|
{
|
|
fPort = port;
|
|
fSendAddr.sin_port = htons(port);
|
|
fRecvAddr.sin_port = htons(port);
|
|
}
|
|
|
|
int JackNetUnixSocket::GetPort()
|
|
{
|
|
return fPort;
|
|
}
|
|
|
|
//address***********************************************************************************************************
|
|
int JackNetUnixSocket::SetAddress(const char* ip, int port)
|
|
{
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr);
|
|
if (addr_conv < 0) {
|
|
return addr_conv;
|
|
}
|
|
fSendAddr.sin_port = htons(port);
|
|
return 0;
|
|
}
|
|
|
|
char* JackNetUnixSocket::GetSendIP()
|
|
{
|
|
return inet_ntoa(fSendAddr.sin_addr);
|
|
}
|
|
|
|
char* JackNetUnixSocket::GetRecvIP()
|
|
{
|
|
return inet_ntoa(fRecvAddr.sin_addr);
|
|
}
|
|
|
|
//utility************************************************************************************************************
|
|
int JackNetUnixSocket::GetName(char* name)
|
|
{
|
|
return gethostname(name, 255);
|
|
}
|
|
|
|
int JackNetUnixSocket::JoinMCastGroup(const char* ip)
|
|
{
|
|
struct ip_mreq multicast_req;
|
|
inet_aton(ip, &multicast_req.imr_multiaddr);
|
|
multicast_req.imr_interface.s_addr = htonl(INADDR_ANY);
|
|
return SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &multicast_req, sizeof(multicast_req));
|
|
}
|
|
|
|
//options************************************************************************************************************
|
|
int JackNetUnixSocket::SetOption(int level, int optname, const void* optval, socklen_t optlen)
|
|
{
|
|
return setsockopt(fSockfd, level, optname, optval, optlen);
|
|
}
|
|
|
|
int JackNetUnixSocket::GetOption(int level, int optname, void* optval, socklen_t* optlen)
|
|
{
|
|
return getsockopt(fSockfd, level, optname, optval, optlen);
|
|
}
|
|
|
|
//timeout************************************************************************************************************
|
|
|
|
#if defined(__sun__) || defined(sun)
|
|
int JackNetUnixSocket::SetTimeOut(int us)
|
|
{
|
|
int flags;
|
|
fTimeOut = us;
|
|
|
|
if ((flags = fcntl(fSockfd, F_GETFL, 0)) < 0) {
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_GETFL");
|
|
return -1;
|
|
}
|
|
|
|
flags |= O_NONBLOCK;
|
|
if (fcntl(fSockfd, F_SETFL, flags) < 0) {
|
|
jack_error("JackNetUnixSocket::SetTimeOut error in fcntl F_SETFL");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int JackNetUnixSocket::WaitRead()
|
|
{
|
|
if (fTimeOut > 0) {
|
|
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
ssize_t res;
|
|
|
|
tv.tv_sec = fTimeOut / 1000000;
|
|
tv.tv_usec = fTimeOut % 1000000;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(fSockfd, &fdset);
|
|
|
|
do {
|
|
res = select(fSockfd + 1, &fdset, NULL, NULL, &tv);
|
|
} while (res < 0 && errno == EINTR);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
} else if (res == 0) {
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int JackNetUnixSocket::WaitWrite()
|
|
{
|
|
if (fTimeOut > 0) {
|
|
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
ssize_t res;
|
|
|
|
tv.tv_sec = fTimeOut / 1000000;
|
|
tv.tv_usec = fTimeOut % 1000000;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(fSockfd, &fdset);
|
|
|
|
do {
|
|
res = select(fSockfd + 1, NULL, &fdset, NULL, &tv);
|
|
} while (res < 0 && errno == EINTR);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
} else if (res == 0) {
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
int JackNetUnixSocket::SetTimeOut(int us)
|
|
{
|
|
jack_log("JackNetUnixSocket::SetTimeout %d usecs", us);
|
|
struct timeval timeout;
|
|
|
|
//less than 1 sec
|
|
if (us < 1000000) {
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = us;
|
|
} else {
|
|
//more than 1 sec
|
|
float sec = float(us) / 1000000.f;
|
|
timeout.tv_sec = (int)sec;
|
|
float usec = (sec - float(timeout.tv_sec)) * 1000000;
|
|
timeout.tv_usec =(int)usec;
|
|
}
|
|
return SetOption(SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
|
}
|
|
#endif
|
|
|
|
//local loop**********************************************************************************************************
|
|
int JackNetUnixSocket::SetLocalLoop()
|
|
{
|
|
char disable = 0;
|
|
return SetOption(IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable));
|
|
}
|
|
|
|
//network operations**************************************************************************************************
|
|
int JackNetUnixSocket::SendTo(const void* buffer, size_t nbytes, int flags)
|
|
{
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitWrite() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
int res;
|
|
if ((res = sendto(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fSendAddr), sizeof(socket_address_t))) < 0) {
|
|
jack_error("SendTo fd = %ld err = %s", fSockfd, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int JackNetUnixSocket::SendTo(const void* buffer, size_t nbytes, int flags, const char* ip)
|
|
{
|
|
int addr_conv = inet_aton(ip, &fSendAddr.sin_addr);
|
|
if (addr_conv < 1) {
|
|
return addr_conv;
|
|
}
|
|
fSendAddr.sin_port = htons(fPort);
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitWrite() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
return SendTo(buffer, nbytes, flags);
|
|
}
|
|
|
|
int JackNetUnixSocket::Send(const void* buffer, size_t nbytes, int flags)
|
|
{
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitWrite() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
int res;
|
|
if ((res = send(fSockfd, buffer, nbytes, flags)) < 0) {
|
|
jack_error("Send fd = %ld err = %s", fSockfd, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int JackNetUnixSocket::RecvFrom(void* buffer, size_t nbytes, int flags)
|
|
{
|
|
socklen_t addr_len = sizeof(socket_address_t);
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitRead() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
int res;
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fRecvAddr), &addr_len)) < 0) {
|
|
jack_error("RecvFrom fd = %ld err = %s", fSockfd, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int JackNetUnixSocket::Recv(void* buffer, size_t nbytes, int flags)
|
|
{
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitRead() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
int res;
|
|
if ((res = recv(fSockfd, buffer, nbytes, flags)) < 0) {
|
|
jack_error("Recv fd = %ld err = %s", fSockfd, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int JackNetUnixSocket::CatchHost(void* buffer, size_t nbytes, int flags)
|
|
{
|
|
socklen_t addr_len = sizeof(socket_address_t);
|
|
#if defined(__sun__) || defined(sun)
|
|
if (WaitRead() < 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
int res;
|
|
if ((res = recvfrom(fSockfd, buffer, nbytes, flags, reinterpret_cast<socket_address_t*>(&fSendAddr), &addr_len)) < 0) {
|
|
jack_log("CatchHost fd = %ld err = %s", fSockfd, strerror(errno));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
net_error_t JackNetUnixSocket::GetError()
|
|
{
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
case ETIMEDOUT:
|
|
return NET_NO_DATA;
|
|
|
|
case ECONNABORTED:
|
|
case ECONNREFUSED:
|
|
case ECONNRESET:
|
|
case EINVAL:
|
|
case EHOSTDOWN:
|
|
case EHOSTUNREACH:
|
|
case ENETDOWN:
|
|
case ENETUNREACH:
|
|
return NET_CONN_ERROR;
|
|
|
|
default:
|
|
//return NET_OP_ERROR;
|
|
return NET_CONN_ERROR;
|
|
}
|
|
}
|
|
|
|
void JackNetUnixSocket::PrintError()
|
|
{
|
|
switch (errno) {
|
|
|
|
case EAGAIN:
|
|
jack_error("JackNetUnixSocket : EAGAIN");
|
|
break;
|
|
case ETIMEDOUT:
|
|
jack_error("JackNetUnixSocket : ETIMEDOUT");
|
|
break;
|
|
case ECONNABORTED:
|
|
jack_error("JackNetUnixSocket : ECONNABORTED");
|
|
break;
|
|
case ECONNREFUSED:
|
|
jack_error("JackNetUnixSocket : ECONNREFUSED");
|
|
break;
|
|
case ECONNRESET:
|
|
jack_error("JackNetUnixSocket : ECONNRESET");
|
|
break;
|
|
case EINVAL:
|
|
jack_error("JackNetUnixSocket : EINVAL");
|
|
break;
|
|
case EHOSTDOWN:
|
|
jack_error("JackNetUnixSocket : EHOSTDOWN");
|
|
break;
|
|
case EHOSTUNREACH:
|
|
jack_error("JackNetUnixSocket : EHOSTUNREACH");
|
|
break;
|
|
case ENETDOWN:
|
|
jack_error("JackNetUnixSocket : ENETDOWN");
|
|
break;
|
|
case ENETUNREACH:
|
|
jack_error("JackNetUnixSocket : ENETUNREACH");
|
|
break;
|
|
default:
|
|
jack_error("JackNetUnixSocket : %d", errno);
|
|
break;
|
|
}
|
|
}
|
|
}
|