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