]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/api/internal/io/TcpSocketEngine_win_p.cpp
Added FTP support (text-tested, not BAM)
[bamtools.git] / src / api / internal / io / TcpSocketEngine_win_p.cpp
diff --git a/src/api/internal/io/TcpSocketEngine_win_p.cpp b/src/api/internal/io/TcpSocketEngine_win_p.cpp
new file mode 100644 (file)
index 0000000..6438b12
--- /dev/null
@@ -0,0 +1,303 @@
+#include "api/internal/io/TcpSocketEngine_p.h"
+#include "api/internal/io/NetWin_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <iostream>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+namespace BamTools {
+namespace Internal {
+
+static inline
+void getPortAndAddress(const sockaddr* s, uint16_t& port, HostAddress& address) {
+
+    // IPv6
+    if (s->sa_family == AF_INET6) {
+        sockaddr_in6* ip6 = (sockaddr_in6*)s;
+        port = ntohs(ip6->sin6_port);
+        IPv6Address tmp;
+        memcpy(&tmp, &ip6->sin6_addr.in6_addr, sizeof(tmp));
+        address.SetAddress(tmp);
+        return;
+    }
+
+    // IPv4
+    if ( s->sa_family == AF_INET ) {
+        sockaddr_in* ip4 = (sockaddr_in*)s;
+        port = ntohl(ip4->sin_port);
+        address.SetAddress( ntohl(ip4->sin_addr) );
+        return;
+    }
+
+    // should be unreachable
+    BT_ASSERT_X(false, "TcpSocketEngine::getPortAndAddress() : unknown network protocol ");
+    return false;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// --------------------------------
+// TcpSocketEngine implementation
+// --------------------------------
+
+void TcpSocketEngine::nativeClose(void) {
+    close(m_socketDescriptor);
+}
+
+bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
+
+    // setup connection parameters from address/port
+    sockaddr_in  sockAddrIPv4;
+    sockaddr_in6 sockAddrIPv6;
+    sockaddr*    sockAddrPtr  = 0;
+    BT_SOCKLEN_T sockAddrSize = 0;
+
+    // IPv6
+    if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
+
+        memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+        sockAddrIPv6.sin6_family = AF_INET6;
+        sockAddrIPv6.sin6_port   = htons(port);
+
+        IPv6Address ip6 = address.GetIPv6Address();
+        memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
+
+        sockAddrSize = sizeof(sockAddrIPv6);
+        sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
+    }
+
+    // IPv4
+    else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
+
+        memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+        sockAddrIPv4.sin_family      = AF_INET;
+        sockAddrIPv4.sin_port        = htons(port);
+        sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
+
+        sockAddrSize = sizeof(sockAddrIPv4);
+        sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
+    }
+
+    // unknown (should be unreachable)
+    else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
+
+    // attempt conenction
+    int connectResult = connect(socketDescriptor, sockAddrPtr, sockAddrSize);
+
+    // if hit error
+    if ( connectResult == -1 ) {
+
+        // see what error was encountered
+        switch ( errno ) {
+
+            case EISCONN:
+                m_socketState = TcpSocket::ConnectedState;
+                break;
+            case ECONNREFUSED:
+            case EINVAL:
+                m_socketError = TcpSocket::ConnectionRefusedError;
+                m_socketState = TcpSocket::UnconnectedState;
+                m_errorString = "connection refused";
+                break;
+            case ETIMEDOUT:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "connection timed out";
+                break;
+            case EHOSTUNREACH:
+                m_socketError = TcpSocket::NetworkError;
+                m_socketState = TcpSocket::UnconnectedState;
+                m_errorString = "host unreachable";
+                break;
+            case ENETUNREACH:
+                m_socketError = TcpSocket::NetworkError;
+                m_socketState = TcpSocket::UnconnectedState;
+                m_errorString = "network unreachable";
+                break;
+            case EADDRINUSE:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "address already in use";
+                break;
+            case EACCES:
+            case EPERM:
+                m_socketError = TcpSocket::SocketAccessError;
+                m_socketState = TcpSocket::UnconnectedState;
+                m_errorString = "permission denied";
+            case EAFNOSUPPORT:
+            case EBADF:
+            case EFAULT:
+            case ENOTSOCK:
+                m_socketState = TcpSocket::UnconnectedState;
+            default:
+                break;
+        }
+
+        if ( m_socketState != TcpSocket::ConnectedState )
+            return false;
+    }
+
+    // otherwise, we should be good
+    // update state & return success
+    m_socketState = TcpSocket::ConnectedState;
+    return true;
+}
+
+bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
+
+    // get protocol value for requested protocol type
+    const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
+
+    // attempt to create socket
+    int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
+
+    // if we fetched an invalid socket descriptor
+    if ( socketFd <= 0 ) {
+
+        // see what error we got
+        switch ( errno ) {
+            case EPROTONOSUPPORT:
+            case EAFNOSUPPORT:
+            case EINVAL:
+                m_socketError = TcpSocket::UnsupportedSocketOperationError;
+                m_errorString = "protocol not supported";
+                break;
+            case ENFILE:
+            case EMFILE:
+            case ENOBUFS:
+            case ENOMEM:
+                m_socketError = TcpSocket::SocketResourceError;
+                m_errorString = "out of resources";
+                break;
+            case EACCES:
+                m_socketError = TcpSocket::SocketAccessError;
+                m_errorString = "permission denied";
+                break;
+            default:
+                break;
+        }
+
+        // return failure
+        return false;
+    }
+
+    // otherwise, store our socket FD & return success
+    m_socketDescriptor = socketFd;
+    return true;
+}
+
+bool TcpSocketEngine::nativeFetchConnectionParameters(void) {
+
+    // reset addresses/ports
+    m_localAddress.Clear();
+    m_remoteAddress.Clear();
+    m_localPort  = 0;
+    m_remotePort = 0;
+
+    // skip (return failure) if invalid socket FD
+    if ( m_socketDescriptor == -1 )
+        return false;
+
+    sockaddr sa;
+    BT_SOCKLEN_T sockAddrSize = sizeof(sa);
+
+    // fetch local address info
+    memset(&sa, 0, sizeof(sa));
+    if ( getsockname(m_socketDescriptor, &sa, &sockAddrSize) == 0 ) {
+        getPortAndAddress(&sa, m_localPort, m_localAddress);
+    }
+    else if ( errno == EBADF ) {
+        m_socketError = TcpSocket::UnsupportedSocketOperationError;
+        m_errorString = "invalid socket descriptor";
+        return false;
+    }
+
+    // fetch remote address
+    if ( getpeername(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
+        getPortAndAddress(&sa, m_remotePort, m_remoteAddress);
+
+    // return success
+    return true;
+}
+
+size_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
+
+    // fetch number of bytes, return 0 on error
+    int numBytes(0);
+    if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
+        return 0;
+    return static_cast<size_t>(numBytes);
+}
+
+int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
+
+    if ( !IsValid() )
+        return -1;
+
+    ssize_t ret = read(m_socketDescriptor, dest, max);
+    if ( ret < 0 ) {
+        ret = -1;
+        switch ( errno ) {
+            case EAGAIN :
+                // No data was available for reading
+                ret = -2;
+                break;
+            case ECONNRESET :
+                ret = 0;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return static_cast<int64_t>(ret);
+}
+
+// negative value for msecs will block (forever) until
+int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
+
+    // set up FD set
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(m_socketDescriptor, &fds);
+
+    // setup our timeout
+    timeval tv;
+    tv.tv_sec  = msecs / 1000;
+    tv.tv_usec = (msecs % 1000) * 1000;
+
+    // do 'select'
+    int ret;
+    if ( isRead )
+        ret = select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
+    else
+        ret = select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
+    return ret;
+}
+
+int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
+
+    ssize_t writtenBytes = write(m_socketDescriptor, data, length);
+    if ( writtenBytes < 0 ) {
+        switch (errno) {
+            case EPIPE:
+            case ECONNRESET:
+                writtenBytes = -1;
+                m_socketError = TcpSocket::RemoteHostClosedError;
+                m_errorString = "remote host closed connection";
+                Close();
+                break;
+            case EAGAIN:
+                writtenBytes = 0;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return static_cast<int64_t>(writtenBytes);
+}