]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/TcpSocketEngine_win_p.cpp
Added FTP support (text-tested, not BAM)
[bamtools.git] / src / api / internal / io / TcpSocketEngine_win_p.cpp
1 #include "api/internal/io/TcpSocketEngine_p.h"
2 #include "api/internal/io/NetWin_p.h"
3 using namespace BamTools;
4 using namespace BamTools::Internal;
5
6 #include <iostream>
7 using namespace std;
8
9 // ------------------------
10 // static utility methods
11 // ------------------------
12
13 namespace BamTools {
14 namespace Internal {
15
16 static inline
17 void getPortAndAddress(const sockaddr* s, uint16_t& port, HostAddress& address) {
18
19     // IPv6
20     if (s->sa_family == AF_INET6) {
21         sockaddr_in6* ip6 = (sockaddr_in6*)s;
22         port = ntohs(ip6->sin6_port);
23         IPv6Address tmp;
24         memcpy(&tmp, &ip6->sin6_addr.in6_addr, sizeof(tmp));
25         address.SetAddress(tmp);
26         return;
27     }
28
29     // IPv4
30     if ( s->sa_family == AF_INET ) {
31         sockaddr_in* ip4 = (sockaddr_in*)s;
32         port = ntohl(ip4->sin_port);
33         address.SetAddress( ntohl(ip4->sin_addr) );
34         return;
35     }
36
37     // should be unreachable
38     BT_ASSERT_X(false, "TcpSocketEngine::getPortAndAddress() : unknown network protocol ");
39     return false;
40 }
41
42 } // namespace Internal
43 } // namespace BamTools
44
45 // --------------------------------
46 // TcpSocketEngine implementation
47 // --------------------------------
48
49 void TcpSocketEngine::nativeClose(void) {
50     close(m_socketDescriptor);
51 }
52
53 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
54
55     // setup connection parameters from address/port
56     sockaddr_in  sockAddrIPv4;
57     sockaddr_in6 sockAddrIPv6;
58     sockaddr*    sockAddrPtr  = 0;
59     BT_SOCKLEN_T sockAddrSize = 0;
60
61     // IPv6
62     if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
63
64         memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
65         sockAddrIPv6.sin6_family = AF_INET6;
66         sockAddrIPv6.sin6_port   = htons(port);
67
68         IPv6Address ip6 = address.GetIPv6Address();
69         memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
70
71         sockAddrSize = sizeof(sockAddrIPv6);
72         sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
73     }
74
75     // IPv4
76     else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
77
78         memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
79         sockAddrIPv4.sin_family      = AF_INET;
80         sockAddrIPv4.sin_port        = htons(port);
81         sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
82
83         sockAddrSize = sizeof(sockAddrIPv4);
84         sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
85     }
86
87     // unknown (should be unreachable)
88     else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
89
90     // attempt conenction
91     int connectResult = connect(socketDescriptor, sockAddrPtr, sockAddrSize);
92
93     // if hit error
94     if ( connectResult == -1 ) {
95
96         // see what error was encountered
97         switch ( errno ) {
98
99             case EISCONN:
100                 m_socketState = TcpSocket::ConnectedState;
101                 break;
102             case ECONNREFUSED:
103             case EINVAL:
104                 m_socketError = TcpSocket::ConnectionRefusedError;
105                 m_socketState = TcpSocket::UnconnectedState;
106                 m_errorString = "connection refused";
107                 break;
108             case ETIMEDOUT:
109                 m_socketError = TcpSocket::NetworkError;
110                 m_errorString = "connection timed out";
111                 break;
112             case EHOSTUNREACH:
113                 m_socketError = TcpSocket::NetworkError;
114                 m_socketState = TcpSocket::UnconnectedState;
115                 m_errorString = "host unreachable";
116                 break;
117             case ENETUNREACH:
118                 m_socketError = TcpSocket::NetworkError;
119                 m_socketState = TcpSocket::UnconnectedState;
120                 m_errorString = "network unreachable";
121                 break;
122             case EADDRINUSE:
123                 m_socketError = TcpSocket::NetworkError;
124                 m_errorString = "address already in use";
125                 break;
126             case EACCES:
127             case EPERM:
128                 m_socketError = TcpSocket::SocketAccessError;
129                 m_socketState = TcpSocket::UnconnectedState;
130                 m_errorString = "permission denied";
131             case EAFNOSUPPORT:
132             case EBADF:
133             case EFAULT:
134             case ENOTSOCK:
135                 m_socketState = TcpSocket::UnconnectedState;
136             default:
137                 break;
138         }
139
140         if ( m_socketState != TcpSocket::ConnectedState )
141             return false;
142     }
143
144     // otherwise, we should be good
145     // update state & return success
146     m_socketState = TcpSocket::ConnectedState;
147     return true;
148 }
149
150 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
151
152     // get protocol value for requested protocol type
153     const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
154
155     // attempt to create socket
156     int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
157
158     // if we fetched an invalid socket descriptor
159     if ( socketFd <= 0 ) {
160
161         // see what error we got
162         switch ( errno ) {
163             case EPROTONOSUPPORT:
164             case EAFNOSUPPORT:
165             case EINVAL:
166                 m_socketError = TcpSocket::UnsupportedSocketOperationError;
167                 m_errorString = "protocol not supported";
168                 break;
169             case ENFILE:
170             case EMFILE:
171             case ENOBUFS:
172             case ENOMEM:
173                 m_socketError = TcpSocket::SocketResourceError;
174                 m_errorString = "out of resources";
175                 break;
176             case EACCES:
177                 m_socketError = TcpSocket::SocketAccessError;
178                 m_errorString = "permission denied";
179                 break;
180             default:
181                 break;
182         }
183
184         // return failure
185         return false;
186     }
187
188     // otherwise, store our socket FD & return success
189     m_socketDescriptor = socketFd;
190     return true;
191 }
192
193 bool TcpSocketEngine::nativeFetchConnectionParameters(void) {
194
195     // reset addresses/ports
196     m_localAddress.Clear();
197     m_remoteAddress.Clear();
198     m_localPort  = 0;
199     m_remotePort = 0;
200
201     // skip (return failure) if invalid socket FD
202     if ( m_socketDescriptor == -1 )
203         return false;
204
205     sockaddr sa;
206     BT_SOCKLEN_T sockAddrSize = sizeof(sa);
207
208     // fetch local address info
209     memset(&sa, 0, sizeof(sa));
210     if ( getsockname(m_socketDescriptor, &sa, &sockAddrSize) == 0 ) {
211         getPortAndAddress(&sa, m_localPort, m_localAddress);
212     }
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 size_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 0;
233     return static_cast<size_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 }