diff options
Diffstat (limited to 'src/Sockets/SctpSocket.cpp')
-rw-r--r-- | src/Sockets/SctpSocket.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/src/Sockets/SctpSocket.cpp b/src/Sockets/SctpSocket.cpp new file mode 100644 index 0000000..2267086 --- /dev/null +++ b/src/Sockets/SctpSocket.cpp @@ -0,0 +1,496 @@ +/** + ** \file SctpSocket.cpp + ** \date 2006-09-04 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2007 Anders Hedstrom + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "SctpSocket.h" +#ifdef USE_SCTP +#include "Utility.h" +#include "ISocketHandler.h" +#include <errno.h> +#include "Ipv4Address.h" +#include "Ipv6Address.h" +#ifdef ENABLE_EXCEPTIONS +#include "Exception.h" +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE +{ +#endif + + +SctpSocket::SctpSocket(ISocketHandler& h,int type) : StreamSocket(h) +,m_type(type) +,m_buf(new char[SCTP_BUFSIZE_READ]) +{ + if (type != SOCK_STREAM && type != SOCK_SEQPACKET) + { + } +} + + +SctpSocket::~SctpSocket() +{ + delete[] m_buf; +} + + +int SctpSocket::Bind(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return Bind(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return Bind(ad); +} + + +int SctpSocket::Bind(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), m_type, "sctp")); + } + if (GetSocket() != INVALID_SOCKET) + { + int n = bind(GetSocket(), ad, ad); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "bind() failed", LOG_LEVEL_ERROR); +#ifdef ENABLE_EXCEPTIONS + throw Exception("bind() failed for SctpSocket, port: " + Utility::l2string(ad.GetPort())); +#endif + } + return n; + } + return -1; +} + + +int SctpSocket::AddAddress(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return AddAddress(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return AddAddress(ad); +} + + +int SctpSocket::AddAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "AddAddress called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_ADD_ADDR); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR); + } + return n; +} + + +int SctpSocket::RemoveAddress(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return RemoveAddress(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return RemoveAddress(ad); +} + + +int SctpSocket::RemoveAddress(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "RemoveAddress called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_REM_ADDR); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR); + } + return n; +} + + +int SctpSocket::Open(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return Open(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return Open(ad); +} + + +int SctpSocket::Open(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Attach(CreateSocket(ad.GetFamily(), m_type, "sctp")); + } + if (GetSocket() != INVALID_SOCKET) + { + if (!SetNonblocking(true)) + { + return -1; + } + int n = connect(GetSocket(), ad, ad); + if (n == -1) + { + // check error code that means a connect is in progress +#ifdef _WIN32 + if (Errno == WSAEWOULDBLOCK) +#else + if (Errno == EINPROGRESS) +#endif + { + Handler().LogError(this, "connect: connection pending", Errno, StrError(Errno), LOG_LEVEL_INFO); + SetConnecting( true ); // this flag will control fd_set's + } + else + { + Handler().LogError(this, "SctpSocket", -1, "connect() failed", LOG_LEVEL_ERROR); + } + } + return n; + } + return -1; +} + + +#ifndef SOLARIS +int SctpSocket::AddConnection(const std::string& a,port_t p) +{ +#ifdef ENABLE_IPV6 +#ifdef IPPROTO_IPV6 + if (IsIpv6()) + { + Ipv6Address ad(a, p); + return AddConnection(ad); + } +#endif +#endif + Ipv4Address ad(a, p); + return AddConnection(ad); +} + + +int SctpSocket::AddConnection(SocketAddress& ad) +{ + if (!ad.IsValid()) + { + Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR); + return -1; + } + if (GetSocket() == INVALID_SOCKET) + { + Handler().LogError(this, "SctpSocket", -1, "AddConnection called with invalid file descriptor", LOG_LEVEL_ERROR); + return -1; + } + int n = sctp_connectx(GetSocket(), ad, ad); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_connectx() failed", LOG_LEVEL_ERROR); + } + else + { + SetConnecting(); + } + return n; +} +#endif + + +int SctpSocket::getpaddrs(sctp_assoc_t id,std::list<std::string>& vec) +{ + struct sockaddr *p = NULL; + int n = sctp_getpaddrs(GetSocket(), id, &p); + if (!n || n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_getpaddrs failed", LOG_LEVEL_WARNING); + return n; + } + for (int i = 0; i < n; i++) + { + vec.push_back(Utility::Sa2String(&p[i])); + } + sctp_freepaddrs(p); + return n; +} + + +int SctpSocket::getladdrs(sctp_assoc_t id,std::list<std::string>& vec) +{ + struct sockaddr *p = NULL; + int n = sctp_getladdrs(GetSocket(), id, &p); + if (!n || n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "sctp_getladdrs failed", LOG_LEVEL_WARNING); + return n; + } + for (int i = 0; i < n; i++) + { + vec.push_back(Utility::Sa2String(&p[i])); + } + sctp_freeladdrs(p); + return n; +} + + +int SctpSocket::PeelOff(sctp_assoc_t id) +{ + int n = sctp_peeloff(GetSocket(), id); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", -1, "PeelOff failed", LOG_LEVEL_WARNING); + return -1; + } + Socket *p = Create(); + p -> Attach(n); + p -> SetDeleteByHandler(); + Handler().Add(p); + return n; +} + + +void SctpSocket::OnRead() +{ +/* + int sctp_recvmsg(int sd, void * msg, size_t * len, + struct sockaddr * from, socklen_t * fromlen, + struct sctp_sndrcvinfo * sinfo, int * msg_flags); + + DESCRIPTION + sctp_recvmsg is a wrapper library function that can be used to receive a message from a socket while using the advanced + features of SCTP. sd is the socket descriptor on which the message pointed to by msg of length len is received. + + If from is not NULL, the source address of the message is filled in. The argument fromlen is a value-result parameter. + initialized to the size of the buffer associated with from , and modified on return to indicate the actual size of the + address stored. + + sinfo is a pointer to a sctp_sndrcvinfo structure to be filled upon receipt of the message. msg_flags is a pointer to a + integer that is filled with any message flags like MSG_NOTIFICATION or MSG_EOR. + +*/ + struct sockaddr sa; + socklen_t sa_len = 0; + struct sctp_sndrcvinfo sinfo; + int flags = 0; + int n = sctp_recvmsg(GetSocket(), m_buf, SCTP_BUFSIZE_READ, &sa, &sa_len, &sinfo, &flags); + if (n == -1) + { + Handler().LogError(this, "SctpSocket", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } + else + { + OnReceiveMessage(m_buf, n, &sa, sa_len, &sinfo, flags); + } +} + + +void SctpSocket::OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags) +{ +} + + +void SctpSocket::OnWrite() +{ + if (Connecting()) + { + int err = SoError(); + + // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on + /// \todo add to read fd_set here + if (!err) // ok + { + Set(!IsDisableRead(), false); + SetConnecting(false); + SetCallOnConnect(); + return; + } + Handler().LogError(this, "sctp: connect failed", err, StrError(err), LOG_LEVEL_FATAL); + Set(false, false); // no more monitoring because connection failed + + // failed +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + return; + } +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + // even though the connection failed at once, only retry after + // the connection timeout. + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + return; + } + SetConnecting(false); + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + return; + } +} + + +void SctpSocket::OnConnectTimeout() +{ + Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL); +#ifdef ENABLE_SOCKS4 + if (Socks4()) + { + OnSocks4ConnectFailed(); + // retry direct connection + } + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) ) + { + IncreaseConnectionRetries(); + // ask socket via OnConnectRetry callback if we should continue trying + if (OnConnectRetry()) + { + SetRetryClientConnect(); + } + else + { + SetCloseAndDelete( true ); + /// \todo state reason why connect failed + OnConnectFailed(); + } + } + else + { + SetCloseAndDelete(true); + /// \todo state reason why connect failed + OnConnectFailed(); + } + // + SetConnecting(false); +} + + +#ifdef _WIN32 +void SctpSocket::OnException() +{ + if (Connecting()) + { +#ifdef ENABLE_SOCKS4 + if (Socks4()) + OnSocks4ConnectFailed(); + else +#endif + if (GetConnectionRetry() == -1 || + (GetConnectionRetry() && + GetConnectionRetries() < GetConnectionRetry() )) + { + // even though the connection failed at once, only retry after + // the connection timeout + // should we even try to connect again, when CheckConnect returns + // false it's because of a connection error - not a timeout... + } + else + { + SetConnecting(false); // tnx snibbe + SetCloseAndDelete(); + OnConnectFailed(); + } + return; + } + // %! exception doesn't always mean something bad happened, this code should be reworked + // errno valid here? + int err = SoError(); + Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL); + SetCloseAndDelete(); +} +#endif // _WIN32 + + +int SctpSocket::Protocol() +{ + return IPPROTO_SCTP; +} + + +#ifdef SOCKETS_NAMESPACE +} // namespace SOCKETS_NAMESPACE +#endif + + +#endif // USE_SCTP + |