1 // Socket.cpp - an IOChannel for sockets
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <boost/cstdint.hpp>
26 #include "GnashSystemNetHeaders.h"
27 #include "GnashSystemFDHeaders.h"
31 #include <boost/lexical_cast.hpp>
37 #include "GnashAlgorithm.h"
51 Socket::connected() const
53 if (_connected
) return true;
54 if (!_socket
) return false;
60 while (retries
-- > 0) {
63 FD_SET(_socket
, &fdset
);
68 const int ret
= ::select(_socket
+ 1, NULL
, &fdset
, NULL
, &tval
);
71 if (ret
== 0) continue;
75 socklen_t len
= sizeof(val
);
76 // NB: the cast to char* is needed for windows and is harmless
78 if (::getsockopt(_socket
, SOL_SOCKET
, SO_ERROR
,
79 reinterpret_cast<char*>(&val
), &len
) < 0) {
93 // If interrupted by a system call, try again
95 const int err
= errno
;
97 log_debug(_("Socket interrupted by a system call"));
101 log_error(_("XMLSocket: The socket was never available"));
113 if (_socket
) ::close(_socket
);
122 Socket::connect(const std::string
& hostname
, boost::uint16_t port
)
125 // We use _socket here because connected() or _connected might not
126 // be true if a connection attempt is underway but not completed.
128 log_error("Connection attempt while already connected");
132 // If _socket is 0, either there has been no connection, or close() has
133 // been called. There must not be an error in either case.
136 if (hostname
.empty()) return false;
138 struct sockaddr_in addr
;
139 std::memset(&addr
, 0, sizeof(addr
));
140 addr
.sin_family
= AF_INET
;
142 addr
.sin_addr
.s_addr
= ::inet_addr(hostname
.c_str());
143 if (addr
.sin_addr
.s_addr
== INADDR_NONE
) {
144 struct hostent
* host
= ::gethostbyname(hostname
.c_str());
145 if (!host
|| !host
->h_addr
) {
148 addr
.sin_addr
= *reinterpret_cast<in_addr
*>(host
->h_addr
);
151 addr
.sin_port
= htons(port
);
153 _socket
= ::socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
156 const int err
= errno
;
157 log_debug("Socket creation failed: %s", std::strerror(err
));
163 const int flag
= ::fcntl(_socket
, F_GETFL
, 0);
164 ::fcntl(_socket
, F_SETFL
, flag
| O_NONBLOCK
);
167 const struct sockaddr
* a
= reinterpret_cast<struct sockaddr
*>(&addr
);
169 // Attempt connection
170 if (::connect(_socket
, a
, sizeof(struct sockaddr
)) < 0) {
171 const int err
= errno
;
173 if (err
!= EINPROGRESS
) {
174 log_error("Failed to connect socket: %s", std::strerror(err
));
183 // Magic timeout number. Use rcfile ?
184 const struct timeval tv
= { 120, 0 };
186 // NB: the cast to const char* is needed for windows and is harmless
188 if (::setsockopt(_socket
, SOL_SOCKET
, SO_RCVTIMEO
,
189 reinterpret_cast<const char*>(&tv
), sizeof(tv
))) {
190 log_error("Setting socket timeout failed");
194 // NB: the cast to const char* is needed for windows and is harmless
196 ::setsockopt(_socket
, IPPROTO_TCP
, TCP_NODELAY
,
197 reinterpret_cast<const char*>(&on
), sizeof(on
));
207 // Read position is always _pos + _size wrapped.
208 const size_t cacheSize
= arraySize(_cache
);
209 size_t start
= (_pos
+ _size
) % cacheSize
;
211 // Try to fill the whole remaining buffer.
212 const size_t completeRead
= cacheSize
- _size
;
214 // End is start + read size, wrapped.
215 size_t end
= (start
+ completeRead
) % cacheSize
;
216 if (end
== 0) end
= cacheSize
;
218 char* startpos
= _cache
+ start
;
222 // The end pos is either the end of the cache or the first
224 char* endpos
= _cache
+ ((startpos
< _cache
+ _pos
) ?
227 const int thisRead
= endpos
- startpos
;
228 assert(thisRead
>= 0);
230 const int bytesRead
= ::recv(_socket
, startpos
, thisRead
, 0);
232 if (bytesRead
== -1) {
233 const int err
= errno
;
235 if (err
== EWOULDBLOCK
|| err
== EAGAIN
) {
236 // Nothing to read. Carry on.
240 log_error("Socket receive error %s", std::strerror(err
));
248 // If there weren't enough bytes, that's it.
249 if (bytesRead
< thisRead
) break;
251 // If we wrote up to the end of the cache, try writing more to the
259 // Do a single read and report how many bytes were read.
261 Socket::read(void* dst
, std::streamsize num
)
264 if (num
< 0) return 0;
266 if (_size
< num
&& !_error
) {
270 if (_size
< num
) return 0;
271 return readNonBlocking(dst
, num
);
276 Socket::readNonBlocking(void* dst
, std::streamsize num
)
280 char* ptr
= static_cast<char*>(dst
);
282 if (!_size
&& !_error
) {
286 size_t cacheSize
= arraySize(_cache
);
288 // First read from pos to end
290 // Maximum bytes available to read.
291 const size_t canRead
= std::min
<size_t>(_size
, num
);
293 size_t toRead
= canRead
;
295 // Space to the end (for the first read).
296 const int thisRead
= std::min
<size_t>(canRead
, cacheSize
- _pos
);
298 std::copy(_cache
+ _pos
, _cache
+ _pos
+ thisRead
, ptr
);
304 std::copy(_cache
, _cache
+ toRead
, ptr
+ thisRead
);
310 return canRead
- toRead
;
314 Socket::write(const void* src
, std::streamsize num
)
321 const char* buf
= static_cast<const char*>(src
);
323 while (toWrite
> 0) {
324 bytesSent
= ::send(_socket
, buf
, toWrite
, 0);
326 const int err
= errno
;
327 log_error("Socket send error %s", std::strerror(err
));
332 if (!bytesSent
) break;
333 toWrite
-= bytesSent
;
336 return num
- toWrite
;
342 log_error("tell() called for Socket");
343 return static_cast<std::streamsize
>(-1);
347 Socket::seek(std::streampos
)
349 log_error("seek() called for Socket");
356 log_error("go_to_end() called for Socket");
362 log_error("eof() called for Socket");