]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/TcpSocketEngine_win_p.cpp
d1691aca72dd874585680e099c6c5272b2e69c76
[bamtools.git] / src / api / internal / io / TcpSocketEngine_win_p.cpp
1 // ***************************************************************************
2 // TcpSocketEngine_win_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 Windows systems
8 // ***************************************************************************
9
10 #include "api/internal/io/TcpSocketEngine_p.h"
11 #include "api/internal/io/NetWin_p.h"
12 using namespace BamTools;
13 using namespace BamTools::Internal;
14
15 #include <cstring>
16 #include <iostream>
17 using namespace std;
18
19 // ------------------------
20 // static utility methods
21 // ------------------------
22
23 namespace BamTools {
24 namespace Internal {
25
26
27 } // namespace Internal
28 } // namespace BamTools
29
30 // --------------------------------
31 // TcpSocketEngine implementation
32 // --------------------------------
33
34 void TcpSocketEngine::nativeClose(void) {
35     closesocket(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 conenction
76     const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
77
78     // if failed, handle error
79     if ( connectResult == SOCKET_ERROR ) {
80
81         // ensure state is set before checking error code
82         m_socketState = TcpSocket::UnconnectedState;
83
84         // set error type/message depending on errorCode
85         const int errorCode = WSAGetLastError();
86         switch ( errorCode ) {
87             case WSANOTINITIALISED:
88                 m_socketError = TcpSocket::UnknownSocketError;
89                 m_errorString = "Windows socket functionality not properly initialized";
90                 break;
91             case WSAEISCONN:
92                 m_socketState = TcpSocket::ConnectedState; // socket already connected
93                 break;
94             case WSAECONNREFUSED:
95             case WSAEINVAL:
96                 m_socketError = TcpSocket::ConnectionRefusedError;
97                 m_errorString = "connection refused";
98                 break;
99             case WSAETIMEDOUT:
100                 m_socketError = TcpSocket::NetworkError;
101                 m_errorString = "connection timed out";
102                 break;
103             case WSAEHOSTUNREACH:
104                 m_socketError = TcpSocket::NetworkError;
105                 m_errorString = "host unreachable";
106                 break;
107             case WSAENETUNREACH:
108                 m_socketError = TcpSocket::NetworkError;
109                 m_errorString = "network unreachable";
110                 break;
111             case WSAEADDRINUSE:
112                 m_socketError = TcpSocket::SocketResourceError;
113                 m_errorString = "address already in use";
114                 break;
115             case WSAEACCES:
116                 m_socketError = TcpSocket::SocketAccessError;
117                 m_errorString = "permission denied";
118                 break;
119             default:
120                 break;
121         }
122
123         // double check that we're not in 'connected' state; if so, return failure
124         if ( m_socketState != TcpSocket::ConnectedState )
125             return false;
126     }
127
128     // otherwise, we should be good
129     // update state & return success
130     m_socketState = TcpSocket::ConnectedState;
131     return true;
132 }
133
134 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
135
136     // get protocol value for requested protocol type
137     const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
138
139     // attempt to create socket
140     SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
141
142     // if we fetched an invalid socket descriptor
143     if ( socketFd == INVALID_SOCKET ) {
144
145         // set error type/message depending on error code
146         const int errorCode = WSAGetLastError();
147         switch ( errorCode ) {
148             case WSANOTINITIALISED:
149                 m_socketError = TcpSocket::UnknownSocketError;
150                 m_errorString = "Windows socket functionality not properly initialized";
151                 break;
152             case WSAEAFNOSUPPORT:
153             case WSAESOCKTNOSUPPORT:
154             case WSAEPROTOTYPE:
155             case WSAEINVAL:
156                 m_socketError = TcpSocket::UnsupportedSocketOperationError;
157                 m_errorString = "protocol not supported";
158                 break;
159             case WSAEMFILE:
160             case WSAENOBUFS:
161                 m_socketError = TcpSocket::SocketResourceError;
162                 m_errorString = "out of resources";
163                 break;
164             default:
165                 break;
166         }
167
168         // return failure
169         return false;
170     }
171
172     // otherwise, store our socket FD & return success
173     m_socketDescriptor = static_cast<int>(socketFd);
174     return true;
175 }
176
177 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
178
179     int64_t numBytes(0);
180     int64_t dummy(0);
181     DWORD bytesWritten(0);
182
183     const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
184                                     , &dummy, sizeof(dummy)
185                                     , &numBytes, sizeof(numBytes)
186                                     , &bytesWritten, 0, 0
187                                     );
188     return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
189 }
190
191 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
192
193     // skip if invalid socket
194     if ( !IsValid() )
195         return -1;
196
197     // set up our WSA output buffer
198     WSABUF buf;
199     buf.buf = dest;
200     buf.len = max;
201
202     // attempt to read bytes
203     DWORD flags = 0;
204     DWORD bytesRead = 0;
205     const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
206
207     // if error encountered
208     if ( readResult == SOCKET_ERROR ) {
209         const int errorCode = WSAGetLastError();
210         switch ( errorCode ) {
211             case WSAEWOULDBLOCK: // nothing read this time, but more coming later
212                 return -2;
213             default:
214                 return -1;        // on any other errors
215         }
216     }
217
218     //  check if nothing was read this time, but more is coming
219     if ( WSAGetLastError() == WSAEWOULDBLOCK )
220         return -2;
221
222     // otherwise return number of bytes read
223     return static_cast<int64_t>(bytesRead);
224 }
225
226 // negative value for msecs will block (forever) until
227 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
228
229     fd_set fds;
230     FD_ZERO(&fds);
231     FD_SET(m_socketDescriptor, &fds);
232
233     timeval tv;
234     tv.tv_sec  = msecs / 1000;
235     tv.tv_usec = (msecs % 1000) * 1000;
236
237     // do 'select'
238     if ( isRead )
239         return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
240     else
241         return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
242 }
243
244 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
245
246     // setup our WSA write buffer
247     WSABUF buf;
248     buf.buf = (char*)data;
249     buf.len = length;
250
251     // attempt to write bytes
252     DWORD flags = 0;
253     DWORD bytesWritten = 0;
254     const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
255
256     // error encountered
257     if ( writeResult == SOCKET_ERROR )  {
258
259         const int errorCode = WSAGetLastError();
260         switch ( errorCode ) {
261             case WSAEWOULDBLOCK:
262                 return 0;
263             case WSAECONNRESET:
264             case WSAECONNABORTED:
265                 m_socketError = TcpSocket::NetworkError;
266                 m_errorString = "connection reset or aborted";
267                 return -1;
268             default:
269                 return -1;
270         }
271     }
272
273     // otherwise return number of bytes written
274     return static_cast<int64_t>(bytesWritten);
275 }