Update with current status
[gnash.git] / libbase / Socket.cpp
blob43a10acb42318a72cf4aab8e8deb350018bd3580
1 // Socket.cpp - an IOChannel for sockets
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
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 #ifdef HAVE_CONFIG_H
22 # include "gnashconfig.h"
23 #endif
25 #include "Socket.h"
27 #include <cerrno>
28 #include <csignal>
29 #include <cstdint>
31 #include "GnashSystemNetHeaders.h"
32 #include "GnashSystemFDHeaders.h"
33 #include "log.h"
34 #include "utility.h"
35 #include "GnashAlgorithm.h"
36 #include "GnashSystemNetHeaders.h"
38 namespace gnash {
40 Socket::Socket()
42 _connected(false),
43 _socket(0),
44 _size(0),
45 _pos(0),
46 _error(false)
47 { }
49 bool
50 Socket::connected() const
52 if (_connected) {
53 return true;
55 if (!_socket) {
56 return false;
59 size_t retries = 10;
60 fd_set fdset;
61 struct timeval tval;
63 while (retries-- > 0) {
65 FD_ZERO(&fdset);
66 FD_SET(_socket, &fdset);
68 tval.tv_sec = 0;
69 tval.tv_usec = 103;
71 const int ret = ::select(_socket + 1, nullptr, &fdset, nullptr, &tval);
73 // Select timeout
74 if (ret == 0) continue;
76 if (ret > 0) {
77 int val = 0;
78 socklen_t len = sizeof(val);
79 // NB: the cast to char* is needed for windows and is harmless
80 // for POSIX.
81 if (::getsockopt(_socket, SOL_SOCKET, SO_ERROR,
82 reinterpret_cast<char*>(&val), &len) < 0) {
83 log_debug("Socket Error");
84 _error = true;
85 return false;
88 if (!val) {
89 _connected = true;
90 return true;
92 _error = true;
93 return false;
96 // If interrupted by a system call, try again
97 if (ret == -1) {
98 const int err = errno;
99 if (err == EINTR) {
100 log_debug("Socket interrupted by a system call");
101 continue;
104 log_error(_("XMLSocket: The socket was never available"));
105 _error = true;
106 return false;
109 return false;
113 void
114 Socket::close()
116 if (_socket) ::close(_socket);
117 _socket = 0;
118 _size = 0;
119 _pos = 0;
120 _connected = false;
121 _error = false;
124 namespace {
126 addrinfo* getAddrInfo(const std::string& hostname, std::uint16_t port)
128 addrinfo req = addrinfo(), *ans = nullptr;
130 req.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
131 req.ai_socktype = SOCK_STREAM;
133 std::string portNo = std::to_string(port);
134 int code = getaddrinfo(hostname.c_str(), portNo.c_str(), &req, &ans);
135 if (code != 0) {
136 log_error(_("getaddrinfo() failed with code: #%d - %s"),
137 code, gai_strerror(code));
138 return nullptr;
141 return ans;
146 bool
147 Socket::connect(const std::string& hostname, std::uint16_t port)
149 // We use _socket here because connected() or _connected might not
150 // be true if a connection attempt is underway but not completed.
151 if (_socket) {
152 log_error(_("Connection attempt while already connected"));
153 return false;
156 // If _socket is 0, either there has been no connection, or close() has
157 // been called. There must not be an error in either case.
158 assert(!_error);
160 if (hostname.empty()) {
161 return false;
164 // This is used for ::connect()
165 std::unique_ptr<addrinfo, decltype(freeaddrinfo)*> ans(getAddrInfo(hostname, port),
166 freeaddrinfo);
168 if (!ans) {
169 return false;
172 // display all the IP numbers
173 if (LogFile::getDefaultInstance().getVerbosity() != 0) {
174 for(const addrinfo* ot = ans.get(); ot; ot = ot->ai_next) {
176 char clienthost [INET6_ADDRSTRLEN] = {};
177 int code = getnameinfo(ot->ai_addr, ot->ai_addrlen,
178 clienthost, sizeof(clienthost),
179 nullptr, 0, NI_NUMERICHOST);
181 if (code != 0) {
182 log_error(_("getnameinfo() failed: %1%"), gai_strerror(code));
183 } else {
184 log_debug("%s has address of: %s", hostname, clienthost);
189 // Multiple IPV$ and IPV6 numbers may be returned, so we try them all if
190 // required
191 const addrinfo *it = ans.get();
192 while (it) {
193 _socket = ::socket(it->ai_family, it->ai_socktype, it->ai_protocol);
194 if (_socket < 0) {
195 const int err = errno;
196 log_error(_("Socket creation failed: %s"), std::strerror(err));
197 _socket = 0;
198 // Try the next IP number
199 it = it->ai_next;
200 } else {
201 break;
205 if (!it) {
206 log_error(_("Socket creation attempt(s) failed: giving up."));
207 return false;
210 #ifndef _WIN32
211 // Set non-blocking.
212 const int flag = ::fcntl(_socket, F_GETFL, 0);
213 ::fcntl(_socket, F_SETFL, flag | O_NONBLOCK);
214 #endif
216 // Attempt connection
217 int ret = ::connect(_socket, it->ai_addr, it->ai_addrlen);
218 if (ret < 0) {
219 const int err = errno;
220 #ifndef _WIN32
221 if (err != EINPROGRESS) {
222 log_error(_("Failed to connect to socket: %s"), std::strerror(err));
223 _socket = 0;
224 return false;
226 #else
227 return false;
228 #endif
231 // Magic timeout number. Use rcfile ?
232 const struct timeval tv = { 120, 0 };
234 // NB: the cast to const char* is needed for windows and is harmless
235 // for POSIX.
236 if (::setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
237 reinterpret_cast<const char*>(&tv), sizeof(tv))) {
238 log_error(_("Setting socket timeout failed"));
241 const int on = 1;
242 // NB: the cast to const char* is needed for windows and is harmless
243 // for POSIX.
244 ::setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
245 reinterpret_cast<const char*>(&on), sizeof(on));
247 assert(_socket);
249 return true;
252 void
253 Socket::fillCache()
255 // Read position is always _pos + _size wrapped.
256 const size_t cacheSize = arraySize(_cache);
257 size_t start = (_pos + _size) % cacheSize;
259 char* startpos = _cache + start;
261 while (1) {
263 // The end pos is either the end of the cache or the first
264 // unprocessed byte.
265 char* endpos = _cache + ((startpos < _cache + _pos) ?
266 _pos : cacheSize);
268 const int thisRead = endpos - startpos;
269 assert(thisRead >= 0);
271 const int bytesRead = ::recv(_socket, startpos, thisRead, 0);
273 if (bytesRead == -1) {
274 const int err = errno;
275 #ifndef _WIN32
276 if (err == EWOULDBLOCK || err == EAGAIN) {
277 // Nothing to read. Carry on.
278 return;
280 #endif
281 log_error(_("Socket receive error %s"), std::strerror(err));
282 _error = true;
283 return;
287 _size += bytesRead;
289 // If there weren't enough bytes, that's it.
290 if (bytesRead < thisRead) break;
292 // If we wrote up to the end of the cache, try writing more to the
293 // beginning.
294 startpos = _cache;
300 // Do a single read and report how many bytes were read.
301 std::streamsize
302 Socket::read(void* dst, std::streamsize num)
305 if (num < 0) return 0;
307 if (_size < num && !_error) {
308 fillCache();
311 if (_size < num) return 0;
312 return readNonBlocking(dst, num);
316 std::streamsize
317 Socket::readNonBlocking(void* dst, std::streamsize num)
319 if (bad()) return 0;
321 char* ptr = static_cast<char*>(dst);
323 if (!_size && !_error) {
324 fillCache();
327 size_t cacheSize = arraySize(_cache);
329 // First read from pos to end
331 // Maximum bytes available to read.
332 const size_t canRead = std::min<size_t>(_size, num);
334 size_t toRead = canRead;
336 // Space to the end (for the first read).
337 const int thisRead = std::min<size_t>(canRead, cacheSize - _pos);
339 std::copy(_cache + _pos, _cache + _pos + thisRead, ptr);
340 _pos += thisRead;
341 _size -= thisRead;
342 toRead -= thisRead;
344 if (toRead) {
345 std::copy(_cache, _cache + toRead, ptr + thisRead);
346 _pos = toRead;
347 _size -= toRead;
348 toRead = 0;
351 return canRead - toRead;
354 std::streamsize
355 Socket::write(const void* src, std::streamsize num)
357 if (bad()) return 0;
358 int toWrite = num;
360 const char* buf = static_cast<const char*>(src);
362 #ifndef _WIN32
363 // Prevent sigpipe (which isn't a standard C signal)
364 // until leaving this function.
365 const struct SignalSetter
367 typedef void(*SigHandler)(int);
368 SignalSetter() : _h(std::signal(SIGPIPE, SIG_IGN)) {}
369 ~SignalSetter() { std::signal(SIGPIPE, _h); }
370 private:
371 const SigHandler _h;
372 } setter;
373 #endif
375 // For broken pipe we prefer being notified with
376 // a return of -1 from ::send.
378 while (toWrite > 0) {
379 int bytesSent = ::send(_socket, buf, toWrite, 0);
380 if (bytesSent < 0) {
381 const int err = errno;
382 log_error(_("Socket send error %s"), std::strerror(err));
383 _error = true;
384 return 0;
387 if (!bytesSent) break;
388 toWrite -= bytesSent;
389 buf += bytesSent;
391 return num - toWrite;
394 std::streampos
395 Socket::tell() const
397 log_error(_("tell() called for Socket"));
398 return static_cast<std::streamsize>(-1);
401 bool
402 Socket::seek(std::streampos)
404 log_error(_("seek() called for Socket"));
405 return false;
408 void
409 Socket::go_to_end()
411 log_error(_("go_to_end() called for Socket"));
414 bool
415 Socket::eof() const
417 return !_size && bad();
420 } // namespace gnash