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