2021-01-12 21:48:26 -06:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "NetUtil.hpp"
|
2021-03-24 11:47:50 -05:00
|
|
|
#include <common/Util.hpp>
|
2021-01-12 21:48:26 -06:00
|
|
|
|
|
|
|
#include "Socket.hpp"
|
|
|
|
#if ENABLE_SSL && !MOBILEAPP
|
|
|
|
#include "SslSocket.hpp"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <netdb.h>
|
|
|
|
|
|
|
|
namespace net
|
|
|
|
{
|
|
|
|
std::shared_ptr<StreamSocket>
|
|
|
|
connect(const std::string& host, const std::string& port, const bool isSSL,
|
|
|
|
const std::shared_ptr<ProtocolHandlerInterface>& protocolHandler)
|
|
|
|
{
|
|
|
|
std::shared_ptr<StreamSocket> socket;
|
|
|
|
|
2021-02-20 15:14:48 -06:00
|
|
|
if (host.empty() || port.empty())
|
|
|
|
{
|
|
|
|
LOG_ERR("Invalid host/port " << host << ':' << port);
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("Connecting to " << host << ':' << port << " (" << (isSSL ? "SSL)" : "Unencrypted)"));
|
|
|
|
|
2021-01-12 21:48:26 -06:00
|
|
|
#if !ENABLE_SSL
|
|
|
|
if (isSSL)
|
|
|
|
{
|
|
|
|
LOG_ERR("Error: isSSL socket requested but SSL is not compiled in.");
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// FIXME: store the address?
|
|
|
|
struct addrinfo* ainfo = nullptr;
|
|
|
|
struct addrinfo hints;
|
|
|
|
std::memset(&hints, 0, sizeof(hints));
|
|
|
|
const int rc = getaddrinfo(host.c_str(), port.c_str(), &hints, &ainfo);
|
|
|
|
|
|
|
|
if (!rc && ainfo)
|
|
|
|
{
|
|
|
|
for (struct addrinfo* ai = ainfo; ai; ai = ai->ai_next)
|
|
|
|
{
|
|
|
|
std::string canonicalName;
|
|
|
|
if (ai->ai_canonname)
|
|
|
|
canonicalName = ai->ai_canonname;
|
|
|
|
|
|
|
|
if (ai->ai_addrlen && ai->ai_addr)
|
|
|
|
{
|
|
|
|
int fd = ::socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
|
|
|
int res = ::connect(fd, ai->ai_addr, ai->ai_addrlen);
|
|
|
|
if (fd < 0 || (res < 0 && errno != EINPROGRESS))
|
|
|
|
{
|
|
|
|
LOG_SYS("Failed to connect to " << host);
|
|
|
|
::close(fd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if ENABLE_SSL
|
|
|
|
if (isSSL)
|
|
|
|
socket = StreamSocket::create<SslStreamSocket>(fd, true, protocolHandler);
|
|
|
|
#endif
|
|
|
|
if (!socket && !isSSL)
|
|
|
|
socket = StreamSocket::create<StreamSocket>(fd, true, protocolHandler);
|
|
|
|
|
|
|
|
if (socket)
|
|
|
|
break;
|
|
|
|
|
|
|
|
LOG_ERR("Failed to allocate socket for client websocket " << host);
|
|
|
|
::close(fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ainfo);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
LOG_ERR("Failed to lookup host [" << host << "]. Skipping.");
|
|
|
|
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:48:44 -05:00
|
|
|
std::shared_ptr<StreamSocket>
|
|
|
|
connect(std::string uri, const std::shared_ptr<ProtocolHandlerInterface>& protocolHandler)
|
|
|
|
{
|
|
|
|
std::string scheme;
|
|
|
|
std::string host;
|
|
|
|
std::string port;
|
|
|
|
if (!parseUri(std::move(uri), scheme, host, port))
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
scheme = Util::toLower(std::move(scheme));
|
|
|
|
const bool isSsl = scheme == "https://" || scheme == "wss://";
|
|
|
|
|
|
|
|
return connect(host, port, isSsl, protocolHandler);
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:47:50 -05:00
|
|
|
bool parseUri(std::string uri, std::string& scheme, std::string& host, std::string& port)
|
|
|
|
{
|
|
|
|
const auto itScheme = uri.find("://");
|
|
|
|
if (itScheme != uri.npos)
|
|
|
|
{
|
|
|
|
scheme = uri.substr(0, itScheme + 3); // Include the last slash.
|
|
|
|
uri = uri.substr(scheme.size()); // Remove the scheme.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No scheme.
|
|
|
|
scheme.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto itUrl = uri.find('/');
|
|
|
|
if (itUrl != uri.npos)
|
|
|
|
{
|
|
|
|
// Remove the URL.
|
|
|
|
uri = uri.substr(0, itUrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto itPort = uri.find(':');
|
|
|
|
if (itPort != uri.npos)
|
|
|
|
{
|
|
|
|
host = uri.substr(0, itPort);
|
|
|
|
port = uri.substr(itPort + 1); // Skip the colon.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No port, just hostname.
|
|
|
|
host = uri;
|
|
|
|
port.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
return !host.empty();
|
|
|
|
}
|
|
|
|
|
2021-01-12 21:48:26 -06:00
|
|
|
} // namespace net
|