]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/TcpSocketEngine_unix_p.cpp
5b100e48b2ea90874e1d644024d9474aa681147b
[bamtools.git] / src / api / internal / io / TcpSocketEngine_unix_p.cpp
1 // ***************************************************************************
2 // TcpSocketEngine_unix_p.cpp (c) 2011 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // ---------------------------------------------------------------------------
5 // Last modified: 15 November 2011 (DB)
6 // ---------------------------------------------------------------------------
7 // Provides low-level implementation of TCP I/O for all UNIX-like systems
8 // ***************************************************************************
9
10 #include "api/internal/io/TcpSocketEngine_p.h"
11 #include "api/internal/io/NetUnix_p.h"
12 using namespace BamTools;
13 using namespace BamTools::Internal;
14
15 #include <cerrno>
16 #include <ctime>
17 #include <iostream>
18 using namespace std;
19
20 // ------------------------
21 // static utility methods
22 // ------------------------
23
24 namespace BamTools {
25 namespace Internal {
26
27 } // namespace Internal
28 } // namespace BamTools
29
30 // --------------------------------
31 // TcpSocketEngine implementation
32 // --------------------------------
33
34 void TcpSocketEngine::nativeClose(void) {
35     close(m_socketDescriptor);
36 }
37
38 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
39
40     // setup connection parameters from address/port
41     sockaddr_in  sockAddrIPv4;
42     sockaddr_in6 sockAddrIPv6;
43     sockaddr*    sockAddrPtr  = 0;
44     BT_SOCKLEN_T sockAddrSize = 0;
45
46     // IPv6
47     if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
48
49         memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
50         sockAddrIPv6.sin6_family = AF_INET6;
51         sockAddrIPv6.sin6_port   = htons(port);
52
53         IPv6Address ip6 = address.GetIPv6Address();
54         memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
55
56         sockAddrSize = sizeof(sockAddrIPv6);
57         sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
58     }
59
60     // IPv4
61     else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
62
63         memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
64         sockAddrIPv4.sin_family      = AF_INET;
65         sockAddrIPv4.sin_port        = htons(port);
66         sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
67
68         sockAddrSize = sizeof(sockAddrIPv4);
69         sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
70     }
71
72     // unknown (should be unreachable)
73     else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
74
75     // attempt connection
76     int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize);
77
78     // if failed, handle error
79     if ( connectResult == -1 ) {
80
81         // ensure state is set before checking errno
82         m_socketState = TcpSocket::UnconnectedState;
83
84         // set error type/message depending on errno
85         switch ( errno ) { // <-- potential thread issues later? but can't get error type from connectResult
86
87             case EISCONN:
88                 m_socketState = TcpSocket::ConnectedState; // socket was already connected
89                 break;
90             case ECONNREFUSED:
91             case EINVAL:
92                 m_socketError = TcpSocket::ConnectionRefusedError;
93                 m_errorString = "connection refused";
94                 break;
95             case ETIMEDOUT:
96                 m_socketError = TcpSocket::NetworkError;
97                 m_errorString = "connection timed out";
98                 break;
99             case EHOSTUNREACH:
100                 m_socketError = TcpSocket::NetworkError;
101                 m_errorString = "host unreachable";
102                 break;
103             case ENETUNREACH:
104                 m_socketError = TcpSocket::NetworkError;
105                 m_errorString = "network unreachable";
106                 break;
107             case EADDRINUSE:
108                 m_socketError = TcpSocket::SocketResourceError;
109                 m_errorString = "address already in use";
110                 break;
111             case EACCES:
112             case EPERM:
113                 m_socketError = TcpSocket::SocketAccessError;
114                 m_errorString = "permission denied";
115                 break;
116             default:
117                 break;
118         }
119
120         // double check that we're not in 'connected' state; if so, return failure
121         if ( m_socketState != TcpSocket::ConnectedState )
122             return false;
123     }
124
125     // otherwise, we should be good
126     // update state & return success
127     m_socketState = TcpSocket::ConnectedState;
128     return true;
129 }
130
131 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
132
133     // get protocol value for requested protocol type
134     const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6
135                                                                       : AF_INET );
136
137     // attempt to create socket
138     int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
139
140     // if we fetched an invalid socket descriptor
141     if ( socketFd <= 0 ) {
142
143         // see what error we got
144         switch ( errno ) {
145             case EPROTONOSUPPORT:
146             case EAFNOSUPPORT:
147             case EINVAL:
148                 m_socketError = TcpSocket::UnsupportedSocketOperationError;
149                 m_errorString = "protocol not supported";
150                 break;
151             case ENFILE:
152             case EMFILE:
153             case ENOBUFS:
154             case ENOMEM:
155                 m_socketError = TcpSocket::SocketResourceError;
156                 m_errorString = "out of resources";
157                 break;
158             case EACCES:
159                 m_socketError = TcpSocket::SocketAccessError;
160                 m_errorString = "permission denied";
161                 break;
162             default:
163                 break;
164         }
165
166         // return failure
167         return false;
168     }
169
170     // otherwise, store our socket FD & return success
171     m_socketDescriptor = socketFd;
172     return true;
173 }
174
175 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
176
177     // fetch number of bytes, return 0 on error
178     int numBytes(0);
179     if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
180         return -1;
181     return static_cast<int64_t>(numBytes);
182 }
183
184 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
185     const ssize_t ret = read(m_socketDescriptor, dest, max);
186     return static_cast<int64_t>(ret);
187 }
188
189 // negative value for msecs will block (forever) until ready
190 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
191
192     // set up FD set
193     fd_set fds;
194     FD_ZERO(&fds);
195     FD_SET(m_socketDescriptor, &fds);
196
197     // setup our timeout
198     timeval tv;
199     tv.tv_sec  = msecs / 1000;
200     tv.tv_usec = (msecs % 1000) * 1000;
201
202     // do 'select'
203     if ( isRead )
204         return select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
205     else
206         return select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
207 }
208
209 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
210     const ssize_t writtenBytes = write(m_socketDescriptor, data, length);
211     return static_cast<int64_t>(writtenBytes);
212 }