22#include <openssl/ssl.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
37static string format_error() {
40 len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
41 nullptr, WSAGetLastError(), 0, (LPTSTR)&buffer, 0,
nullptr);
43 return string(
"Unknown error message");
46 const char *text = (
const char *)buffer;
47 while (len > 0 && isspace(text[len - 1])) {
51 string result(text, len);
56#define format_error() strerror(errno)
59#if defined(_WIN32) && defined(LIBRESSL_VERSION_NUMBER)
66sock_should_retry(
int i) {
67 if (i == 0 || i == -1) {
68 int err = WSAGetLastError();
83#define sock_should_retry(err) BIO_sock_should_retry(err)
92BioPtr(
const URLSpec &url) : _connecting(false) {
95 string filename = URLSpec::unquote(url.get_path());
100 if (!filename.empty()) {
101 if (filename[0] ==
'/' || filename[0] ==
'\\') {
102 Filename fname = Filename::from_os_specific(filename.substr(1));
103 if (fname.is_local()) {
105 fname = string(
"/") + fname.get_fullpath();
107 filename = fname.to_os_specific();
113 _bio = BIO_new_file(filename.c_str(),
"rb");
123 struct addrinfo hints, *res =
nullptr;
124 memset(&hints, 0,
sizeof(hints));
125 hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
126 hints.ai_family = support_ipv6 ? AF_UNSPEC : AF_INET;
127 hints.ai_socktype = SOCK_STREAM;
130 int result = getaddrinfo(_server_name.c_str(),
nullptr, &hints, &res);
134 if (result == EAI_SYSTEM && errno != 0) {
135 errmsg = strerror(errno);
139 errmsg = gai_strerror(result);
141 downloader_cat.error()
142 <<
"Failed to resolve " << url.
get_server() <<
": " << errmsg <<
"\n";
145 nassertv(res !=
nullptr && res->ai_addr !=
nullptr);
151 DWORD bufsize =
sizeof(buf);
152 WSAAddressToStringA(res->ai_addr, res->ai_addrlen,
nullptr, buf, &bufsize);
154 if (res->ai_addr->sa_family == AF_INET) {
155 inet_ntop(AF_INET, (
char *)&((sockaddr_in *)res->ai_addr)->sin_addr, buf,
sizeof(buf));
157 }
else if (res->ai_addr->sa_family == AF_INET6) {
158 inet_ntop(AF_INET6, (
char *)&((sockaddr_in6 *)res->ai_addr)->sin6_addr, buf,
sizeof(buf));
165 if (downloader_cat.is_debug()) {
166 downloader_cat.debug()
167 <<
"Resolved " << url.
get_server() <<
" to " << buf <<
"\n";
171 int fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
173 downloader_cat.error()
174 <<
"Failed to create socket: " << format_error() <<
"\n";
181 nassertv(res->ai_addrlen <=
sizeof(_addr));
182 memcpy(&_addr, res->ai_addr, res->ai_addrlen);
183 _addrlen = res->ai_addrlen;
187 if (_addr.ss_family == AF_INET) {
188 ((sockaddr_in &)_addr).sin_port = htons(_port);
189 }
else if (_addr.ss_family == AF_INET6) {
190 ((sockaddr_in6 &)_addr).sin6_port = htons(_port);
193 _bio = BIO_new_socket(fd, 1);
202 if (_bio ==
nullptr) {
207 BIO_get_fd(_bio, &fd);
208 nassertv_always(fd >= 0);
210 BIO_socket_nbio(fd, nbio);
218 if (_bio ==
nullptr) {
223 BIO_get_fd(_bio, &fd);
224 nassertr(fd >= 0,
false);
228 result = BIO_sock_error(fd);
230 result = ::connect(fd, (sockaddr *)&_addr, _addrlen);
231 if (result != 0 && sock_should_retry(-1)) {
234 BIO_set_flags(_bio, BIO_FLAGS_SHOULD_RETRY);
239 BIO_clear_retry_flags(_bio);
243 downloader_cat.warning()
244 <<
"Failed to connect to " << _server_name <<
" port " << _port
245 <<
": " << format_error() <<
"\n";
256should_retry()
const {
257 return (_bio !=
nullptr) && BIO_should_retry(_bio);
265 if (_bio !=
nullptr) {
266 if (downloader_cat.is_debug() && !_server_name.empty()) {
267 downloader_cat.debug()
268 <<
"Dropping connection to " << _server_name <<
" port " << _port <<
"\n";
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for a URL, e.g.
get_server
Returns the server name specified by the URL, if any.
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
get_port
Returns the port number specified by the URL, or the default port if not specified.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.