]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/TcpSocketEngine_unix_p.cpp
Updated file headers (filename, license, description, etc)
[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: 10 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 //static inline
28 //void getPortAndAddress(const sockaddr* s, uint16_t& port, HostAddress& address) {
29
30 //    // IPv6
31 //    if (s->sa_family == AF_INET6) {
32 //        sockaddr_in6* ip6 = (sockaddr_in6*)s;
33 //        port = ntohs(ip6->sin6_port);
34 //        IPv6Address tmp;
35 //        memcpy(&tmp.data, &(ip6->sin6_addr.s6_addr), sizeof(tmp));
36 //        address.SetAddress(tmp);
37 //        return;
38 //    }
39
40 //    // IPv4
41 //    if ( s->sa_family == AF_INET ) {
42 //        sockaddr_in* ip4 = (sockaddr_in*)s;
43 //        port = ntohl(ip4->sin_port);
44 //        address.SetAddress( ntohl(ip4->sin_addr.s_addr) );
45 //        return;
46 //    }
47
48 //    // should be unreachable
49 //    BT_ASSERT_X(false, "TcpSocketEngine::getPortAndAddress() : unknown network protocol ");
50 //}
51
52 } // namespace Internal
53 } // namespace BamTools
54
55 // --------------------------------
56 // TcpSocketEngine implementation
57 // --------------------------------
58
59 void TcpSocketEngine::nativeClose(void) {
60     close(m_socketDescriptor);
61 }
62
63 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
64
65     // setup connection parameters from address/port
66     sockaddr_in  sockAddrIPv4;
67     sockaddr_in6 sockAddrIPv6;
68     sockaddr*    sockAddrPtr  = 0;
69     BT_SOCKLEN_T sockAddrSize = 0;
70
71     // IPv6
72     if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
73
74         memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
75         sockAddrIPv6.sin6_family = AF_INET6;
76         sockAddrIPv6.sin6_port   = htons(port);
77
78         IPv6Address ip6 = address.GetIPv6Address();
79         memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
80
81         sockAddrSize = sizeof(sockAddrIPv6);
82         sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
83     }
84
85     // IPv4
86     else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
87
88         memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
89         sockAddrIPv4.sin_family      = AF_INET;
90         sockAddrIPv4.sin_port        = htons(port);
91         sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
92
93         sockAddrSize = sizeof(sockAddrIPv4);
94         sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
95     }
96
97     // unknown (should be unreachable)
98     else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
99
100     // attempt connection
101     int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize);
102
103     // if hit error
104     if ( connectResult == -1 ) {
105
106         // see what error was encountered
107         switch ( errno ) {
108
109             case EISCONN:
110                 m_socketState = TcpSocket::ConnectedState;
111                 break;
112             case ECONNREFUSED:
113             case EINVAL:
114                 m_socketError = TcpSocket::ConnectionRefusedError;
115                 m_socketState = TcpSocket::UnconnectedState;
116                 m_errorString = "connection refused";
117                 break;
118             case ETIMEDOUT:
119                 m_socketError = TcpSocket::NetworkError;
120                 m_errorString = "connection timed out";
121                 break;
122             case EHOSTUNREACH:
123                 m_socketError = TcpSocket::NetworkError;
124                 m_socketState = TcpSocket::UnconnectedState;
125                 m_errorString = "host unreachable";
126                 break;
127             case ENETUNREACH:
128                 m_socketError = TcpSocket::NetworkError;
129                 m_socketState = TcpSocket::UnconnectedState;
130                 m_errorString = "network unreachable";
131                 break;
132             case EADDRINUSE:
133                 m_socketError = TcpSocket::NetworkError;
134                 m_errorString = "address already in use";
135                 break;
136             case EACCES:
137             case EPERM:
138                 m_socketError = TcpSocket::SocketAccessError;
139                 m_socketState = TcpSocket::UnconnectedState;
140                 m_errorString = "permission denied";
141             case EAFNOSUPPORT:
142             case EBADF:
143             case EFAULT:
144             case ENOTSOCK:
145                 m_socketState = TcpSocket::UnconnectedState;
146             default:
147                 break;
148         }
149
150         if ( m_socketState != TcpSocket::ConnectedState )
151             return false;
152     }
153
154     // otherwise, we should be good
155     // update state & return success
156     m_socketState = TcpSocket::ConnectedState;
157     return true;
158 }
159
160 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
161
162     // get protocol value for requested protocol type
163     const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6
164                                                                       : AF_INET );
165
166     // attempt to create socket
167     int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
168
169     // if we fetched an invalid socket descriptor
170     if ( socketFd <= 0 ) {
171
172         // see what error we got
173         switch ( errno ) {
174             case EPROTONOSUPPORT:
175             case EAFNOSUPPORT:
176             case EINVAL:
177                 m_socketError = TcpSocket::UnsupportedSocketOperationError;
178                 m_errorString = "protocol not supported";
179                 break;
180             case ENFILE:
181             case EMFILE:
182             case ENOBUFS:
183             case ENOMEM:
184                 m_socketError = TcpSocket::SocketResourceError;
185                 m_errorString = "out of resources";
186                 break;
187             case EACCES:
188                 m_socketError = TcpSocket::SocketAccessError;
189                 m_errorString = "permission denied";
190                 break;
191             default:
192                 break;
193         }
194
195         // return failure
196         return false;
197     }
198
199     // otherwise, store our socket FD & return success
200     m_socketDescriptor = socketFd;
201     return true;
202 }
203
204 //bool TcpSocketEngine::nativeFetchConnectionParameters(void) {
205
206 //    // reset addresses/ports
207 ////    m_localAddress.Clear();
208 //    m_remoteAddress.Clear();
209 ////    m_localPort  = 0;
210 //    m_remotePort = 0;
211
212 //    // skip (return failure) if invalid socket FD
213 //    if ( m_socketDescriptor == -1 )
214 //        return false;
215
216 //    sockaddr sa;
217 //    BT_SOCKLEN_T sockAddrSize = sizeof(sa);
218
219 //    // fetch local address info
220 //    memset(&sa, 0, sizeof(sa));
221 //    if ( getsockname(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
222 //        getPortAndAddress(&sa, m_localPort, m_localAddress);
223 //    else if ( errno == EBADF ) {
224 //        m_socketError = TcpSocket::UnsupportedSocketOperationError;
225 //        m_errorString = "invalid socket descriptor";
226 //        return false;
227 //    }
228
229 //    // fetch remote address
230 //    if ( getpeername(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
231 //        getPortAndAddress(&sa, m_remotePort, m_remoteAddress);
232
233 //    // return success
234 //    return true;
235 //}
236
237 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
238
239     // fetch number of bytes, return 0 on error
240     int numBytes(0);
241     if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
242         return -1;
243     return static_cast<int64_t>(numBytes);
244 }
245
246 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
247
248     if ( !IsValid() )
249         return -1;
250
251     ssize_t ret = read(m_socketDescriptor, dest, max);
252     if ( ret < 0 ) {
253         ret = -1;
254         switch ( errno ) {
255             case EAGAIN :
256                 // No data was available for reading
257                 ret = -2;
258                 break;
259             case ECONNRESET :
260                 ret = 0;
261                 break;
262             default:
263                 break;
264         }
265     }
266     return static_cast<int64_t>(ret);
267 }
268
269 // negative value for msecs will block (forever) until ready
270 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
271
272     // set up FD set
273     fd_set fds;
274     FD_ZERO(&fds);
275     FD_SET(m_socketDescriptor, &fds);
276
277     // setup our timeout
278     timeval tv;
279     tv.tv_sec  = msecs / 1000;
280     tv.tv_usec = (msecs % 1000) * 1000;
281
282     // do 'select'
283     int ret;
284     if ( isRead )
285         ret = select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
286     else
287         ret = select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
288     return ret;
289 }
290
291 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
292
293     ssize_t writtenBytes = write(m_socketDescriptor, data, length);
294     if ( writtenBytes < 0 ) {
295         switch (errno) {
296             case EPIPE:
297             case ECONNRESET:
298                 writtenBytes = -1;
299                 m_socketError = TcpSocket::RemoteHostClosedError;
300                 m_errorString = "remote host closed connection";
301                 Close();
302                 break;
303             case EAGAIN:
304                 writtenBytes = 0;
305                 break;
306             default:
307                 break;
308         }
309     }
310     return static_cast<int64_t>(writtenBytes);
311 }