/* Copyright (C) 2008 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 #include 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; if ((res = setsockopt( fSockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) { StrError(NET_ERROR_CODE); } return fSockfd; } int JackNetUnixSocket::Bind() { return bind ( fSockfd, reinterpret_cast ( &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 ( &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 ); //negative timeout, or exceding 10s, return if ( ( us < 0 ) || ( us > 10000000 ) ) return SOCKET_ERROR; struct timeval timeout; //less than 1sec if ( us < 1000000 ) { timeout.tv_sec = 0; timeout.tv_usec = us; } //more than 1sec else { float sec = static_cast ( us ) / 1000000.f; timeout.tv_sec = ( int ) sec; float usec = ( sec - static_cast ( 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 return sendto ( fSockfd, buffer, nbytes, flags, reinterpret_cast ( &fSendAddr ), sizeof ( socket_address_t ) ); } 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; #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 return send ( fSockfd, buffer, nbytes, flags ); } 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 return recvfrom ( fSockfd, buffer, nbytes, flags, reinterpret_cast ( &fRecvAddr ), &addr_len ); } int JackNetUnixSocket::Recv ( void* buffer, size_t nbytes, int flags ) { #if defined(__sun__) || defined(sun) if (WaitRead() < 0) return -1; #endif return recv ( fSockfd, buffer, nbytes, flags ); } 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 return recvfrom ( fSockfd, buffer, nbytes, flags, reinterpret_cast ( &fSendAddr ), &addr_len ); } 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; } } }