linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #07604
[Merge] lp:~maksis/adchpp/adchpp-hbri into lp:adchpp
maksis has proposed merging lp:~maksis/adchpp/adchpp-hbri into lp:adchpp.
Requested reviews:
Dcplusplus-team (dcplusplus-team)
For more details, see:
https://code.launchpad.net/~maksis/adchpp/adchpp-hbri/+merge/194959
* Add support for hybrid IPv4/IPv6 client connectivity: http://www.dcbase.org/forum/viewtopic.php?f=55&t=771
* [L#1088638] Allow clients to reconnect in event of overflow
* [L#1106226] Fix a crash when joining the hub with a local IPv6 address
* Make login timeout work independently from other connecting users
* Allow configuring custom bind addresses
--
https://code.launchpad.net/~maksis/adchpp/adchpp-hbri/+merge/194959
Your team Dcplusplus-team is requested to review the proposed merge of lp:~maksis/adchpp/adchpp-hbri into lp:adchpp.
=== modified file 'adchpp/AdcCommand.cpp'
--- adchpp/AdcCommand.cpp 2013-01-18 21:41:53 +0000
+++ adchpp/AdcCommand.cpp 2013-11-12 23:29:50 +0000
@@ -40,7 +40,7 @@
AdcCommand::AdcCommand() : cmdInt(0), priority(PRIORITY_NORMAL), from(INVALID_SID), to(INVALID_SID), type(TYPE_INFO) { }
AdcCommand::AdcCommand(Severity sev, Error err, const string& desc, char aType /* = TYPE_INFO */) : cmdInt(CMD_STA), priority(PRIORITY_NORMAL), from(HUB_SID), to(INVALID_SID), type(aType) {
- addParam(Util::toString(sev * 100 + err));
+ addParam((sev == SEV_SUCCESS && err == SUCCESS) ? "000" : Util::toString(sev * 100 + err));
addParam(desc);
}
=== modified file 'adchpp/AdcCommand.h'
--- adchpp/AdcCommand.h 2013-01-18 21:41:53 +0000
+++ adchpp/AdcCommand.h 2013-11-12 23:29:50 +0000
@@ -36,6 +36,7 @@
};
enum Error {
+ SUCCESS = 0,
ERROR_GENERIC = 0,
ERROR_HUB_GENERIC = 10,
ERROR_HUB_FULL = 11,
@@ -63,7 +64,8 @@
ERROR_FILE_NOT_AVAILABLE = 51,
ERROR_FILE_PART_NOT_AVAILABLE = 52,
ERROR_SLOTS_FULL = 53,
- ERROR_NO_CLIENT_HASH = 54
+ ERROR_NO_CLIENT_HASH = 54,
+ ERROR_HBRI_TIMEOUT = 55
};
enum Severity {
@@ -109,6 +111,7 @@
C(CMD, 'C','M','D');
C(NAT, 'N','A','T');
C(RNT, 'R','N','T');
+ C(TCP, 'T','C','P');
#undef C
static const uint32_t HUB_SID = static_cast<uint32_t>(-1);
@@ -225,6 +228,7 @@
C(CMD);
C(NAT);
C(RNT);
+ C(TCP);
default:
dcdebug("Unknown ADC command: %.50s\n", cmd.toString().c_str());
return true;
=== modified file 'adchpp/Client.h'
--- adchpp/Client.h 2013-03-07 00:41:35 +0000
+++ adchpp/Client.h 2013-11-12 23:29:50 +0000
@@ -43,6 +43,8 @@
/** @param reason The statistic to update */
ADCHPP_DLL virtual void disconnect(Util::Reason reason, const std::string &info = Util::emptyString) throw();
const std::string& getIp() const throw() { return socket->getIp(); }
+ bool getHbriParams(AdcCommand& cmd) const throw() { return socket->getHbriParams(cmd); }
+ bool isV6() const { return socket->isV6(); }
/**
* Set data mode for aBytes bytes.
=== modified file 'adchpp/ClientManager.cpp'
--- adchpp/ClientManager.cpp 2013-07-16 22:48:12 +0000
+++ adchpp/ClientManager.cpp 2013-11-12 23:29:50 +0000
@@ -20,6 +20,7 @@
#include "ClientManager.h"
+#include "Core.h"
#include "File.h"
#include "Client.h"
#include "LogManager.h"
@@ -33,6 +34,15 @@
#include <boost/asio/ip/address_v6.hpp>
#include <boost/locale.hpp>
+#include <boost/range/algorithm/find.hpp>
+#include <boost/range/algorithm/find_if.hpp>
+#include <boost/range/algorithm/remove_if.hpp>
+#include <boost/range/adaptor/map.hpp>
+
+using boost::adaptors::map_values;
+using boost::range::find;
+using boost::range::find_if;
+
namespace adchpp {
using namespace std;
@@ -43,10 +53,52 @@
core(core),
hub(*this),
maxCommandSize(16 * 1024),
-logTimeout(30 * 1000)
+logTimeout(30 * 1000),
+hbriTimeout(5000)
{
+ core.getSocketManager().addTimedJob(1000, std::bind(&ClientManager::onTimerSecond, this));
+}
+
+void ClientManager::prepareSupports(bool addHbri) {
hub.addSupports(AdcCommand::toFourCC("BASE"));
hub.addSupports(AdcCommand::toFourCC("TIGR"));
+
+ if (addHbri)
+ hub.addSupports(AdcCommand::toFourCC("HBRI"));
+}
+
+void ClientManager::onTimerSecond() {
+ // HBRI
+ auto timeoutHbri = time::now() - time::millisec(hbriTimeout);
+ for (auto i = hbriTokens.begin(); i != hbriTokens.end();) {
+ if (timeoutHbri > i->second.second) {
+ auto cc = dynamic_cast<Client*>(i->second.first);
+ i = hbriTokens.erase(i);
+
+ dcdebug("ClientManager: HBRI timeout in state %d\n", cc->getState());
+
+ std::string proto = cc->isV6() ? "IPv4" : "IPv6";
+ AdcCommand sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_HBRI_TIMEOUT, proto + " validation timed out");
+ cc->send(sta);
+
+ cc->unsetFlag(Entity::FLAG_VALIDATE_HBRI);
+ cc->stripProtocolSupports();
+ if (cc->getState() == Entity::STATE_HBRI)
+ enterNormal(*cc, true, true);
+ } else {
+ i++;
+ }
+ }
+
+ // Logins
+ auto timeoutLogin = time::now() - time::millisec(getLogTimeout());
+ while (!logins.empty() && (timeoutLogin > logins.front().second)) {
+ auto cc = logins.front().first;
+
+ dcdebug("ClientManager: Login timeout in state %d\n", cc->getState());
+ cc->disconnect(Util::REASON_LOGIN_TIMEOUT);
+ logins.pop_front();
+ }
}
Bot* ClientManager::createBot(const Bot::SendHandler& handler) {
@@ -149,16 +201,6 @@
void ClientManager::onConnected(Client& c) throw() {
dcdebug("%s connected\n", AdcCommand::fromSID(c.getSID()).c_str());
- // First let's check if any clients have passed the login timeout...
- auto timeout = time::now() - time::millisec(getLogTimeout());
-
- while(!logins.empty() && (timeout > logins.front().second)) {
- Client* cc = logins.front().first;
-
- dcdebug("ClientManager: Login timeout in state %d\n", cc->getState());
- cc->disconnect(Util::REASON_LOGIN_TIMEOUT);
- logins.pop_front();
- }
logins.push_back(make_pair(&c, time::now()));
@@ -225,6 +267,7 @@
badState(c, cmd);
return false;
}
+
return true;
}
@@ -245,26 +288,30 @@
}
bool ClientManager::verifyINF(Entity& c, AdcCommand& cmd) throw() {
+ if (!verifyCID(c, cmd))
+ return false;
+
+ if (!verifyNick(c, cmd))
+ return false;
+
+ if (cmd.getParam("DE", 0, strtmp)) {
+ if (!Util::validateCharset(strtmp, 32)) {
+ disconnect(c, Util::REASON_INVALID_DESCRIPTION, "Invalid character in description");
+ return false;
+ }
+ }
+
Client* cc = dynamic_cast<Client*>(&c);
if(cc) {
- if(!verifyIp(*cc, cmd))
- return false;
- }
-
- if(!verifyCID(c, cmd))
- return false;
-
- if(!verifyNick(c, cmd))
- return false;
-
- if(cmd.getParam("DE", 0, strtmp)) {
- if(!Util::validateCharset(strtmp, 32)) {
- disconnect(c, Util::REASON_INVALID_DESCRIPTION, "Invalid character in description");
- return false;
- }
- }
+ if(!verifyIp(*cc, cmd, false))
+ return false;
+ }
+
c.updateFields(cmd);
+ if (!c.isSet(Entity::FLAG_VALIDATE_HBRI) && c.getState() != Entity::STATE_HBRI)
+ c.stripProtocolSupports();
+
return true;
}
@@ -307,13 +354,35 @@
}
if(overflowing > 3 && overflowing > (entities.size() / 4)) {
- disconnect(c, Util::REASON_NO_BANDWIDTH, "Not enough bandwidth available, please try again later", AdcCommand::ERROR_HUB_FULL);
+ disconnect(c, Util::REASON_NO_BANDWIDTH, "Not enough bandwidth available, please try again later", AdcCommand::ERROR_HUB_FULL, Util::emptyString, 1);
return false;
}
return true;
}
+bool ClientManager::sendHBRI(Entity& c) {
+ if (c.hasSupport(AdcCommand::toFourCC("HBRI"))) {
+ AdcCommand cmd(AdcCommand::CMD_TCP);
+ if (!dynamic_cast<Client*>(&c)->getHbriParams(cmd)) {
+ return false;
+ }
+
+ c.setFlag(Entity::FLAG_VALIDATE_HBRI);
+ if (c.getState() != Entity::STATE_NORMAL)
+ c.setState(Entity::STATE_HBRI);
+
+ auto token = Util::toString(Util::rand());
+ hbriTokens.insert(make_pair(token, make_pair(&c, time::now())));
+
+ cmd.addParam("TO", token);
+ c.send(cmd);
+ return true;
+ }
+
+ return false;
+}
+
bool ClientManager::handle(AdcCommand::INF, Entity& c, AdcCommand& cmd) throw() {
if(c.getState() != Entity::STATE_IDENTIFY && c.getState() != Entity::STATE_NORMAL) {
badState(c, cmd);
@@ -335,23 +404,98 @@
return true;
}
-bool ClientManager::verifyIp(Client& c, AdcCommand& cmd) throw() {
+static const int allowedCount = 3;
+static const char* allowedV4[allowedCount] = { "I4", "U4", "SU" };
+static const char* allowedV6[allowedCount] = { "I6", "U6", "SU" };
+bool ClientManager::handle(AdcCommand::TCP, Entity& c, AdcCommand& cmd) throw() {
+ dcdebug("Received HBRI TCP: %s", cmd.toString().c_str());
+
+ string error;
+ string token;
+ if(cmd.getParam("TO", 0, token)) {
+ auto p = hbriTokens.find(token);
+ if (p != hbriTokens.end()) {
+ Client* mainCC = dynamic_cast<Client*>(p->second.first);
+ mainCC->unsetFlag(Entity::FLAG_VALIDATE_HBRI);
+
+ if (mainCC->getState() != Entity::STATE_HBRI && mainCC->getState() != Entity::STATE_NORMAL) {
+ badState(c, cmd);
+ return false;
+ }
+
+ hbriTokens.erase(p);
+
+ if (!verifyIp(*dynamic_cast<Client*>(&c), cmd, true))
+ return false;
+
+ // disconnect the validation connection
+ AdcCommand sta(AdcCommand::SEV_SUCCESS, AdcCommand::SUCCESS, "Validation succeed");
+ c.send(sta);
+ c.disconnect(Util::REASON_HBRI);
+
+ // remove extra parameters
+ auto& params = cmd.getParameters();
+ const auto& allowed = dynamic_cast<Client*>(&c)->isV6() ? allowedV6 : allowedV4;
+
+ params.erase(boost::remove_if(params, [&](const string& s) {
+ return find(allowed, allowed + allowedCount, s.substr(0, 2)) == &allowed[allowedCount];
+ }), params.end());
+
+ // update the fields for the main entity
+ mainCC->updateFields(cmd);
+
+ if (mainCC->getState() == Entity::STATE_HBRI) {
+ // continue with the normal login
+ enterNormal(*mainCC, true, true);
+ } else {
+ // send the updated fields
+ AdcCommand inf(AdcCommand::CMD_INF, AdcCommand::TYPE_BROADCAST, mainCC->getSID());
+ inf.getParameters() = cmd.getParameters();
+ sendToAll(inf.getBuffer());
+ }
+ return true;
+ } else {
+ error = "Unknown validation token";
+ }
+ } else {
+ error = "Validation token missing";
+ }
+
+ dcassert(!error.empty());
+ AdcCommand sta(AdcCommand::SEV_FATAL, AdcCommand::ERROR_LOGIN_GENERIC, error);
+ c.send(sta);
+
+ c.disconnect(Util::REASON_HBRI);
+ return true;
+}
+
+bool ClientManager::verifyIp(Client& c, AdcCommand& cmd, bool isHbriConn) throw() {
if(c.isSet(Entity::FLAG_OK_IP))
return true;
using namespace boost::asio::ip;
- auto remote = address::from_string(c.getIp());
+ address remote;
+
+ try {
+ remote = address::from_string(c.getIp());
+ } catch(const boost::system::system_error&) {
+ printf("Error when reading IP %s\n", c.getIp().c_str());
+ return false;
+ }
+
std::string ip;
- if(remote.is_v4() || (remote.is_v6() && remote.to_v6().is_v4_mapped())) {
+ bool doValidation = false;
+ if (!c.isV6()) {
auto v4 = remote.is_v4() ? remote.to_v4() : remote.to_v6().to_v4();
if(cmd.getParam("I4", 0, ip)) {
dcdebug("%s verifying IP %s\n", AdcCommand::fromSID(c.getSID()).c_str(), ip.c_str());
if(ip.empty() || address_v4::from_string(ip) == address_v4::any()) {
cmd.delParam("I4", 0);
- } else if(address_v4::from_string(ip) != v4 && !Util::isPrivateIp(c.getIp())) {
+ cmd.addParam("I4", c.getIp());
+ } else if(address_v4::from_string(ip) != v4 && !Util::isPrivateIp(c.getIp(), false)) {
disconnect(c, Util::REASON_INVALID_IP, "Your IP is " + c.getIp() +
", reconfigure your client settings", AdcCommand::ERROR_BAD_IP, "IP" + c.getIp());
return false;
@@ -360,21 +504,24 @@
}
}
- if(!c.hasField("I4")) {
- c.setField("I4", v4.to_string());
- }
-
- if(c.getState() != Entity::STATE_NORMAL) {
- cmd.addParam("I4", v4.to_string());
- }
-
- cmd.delParam("I6", 0); // We can't check this so we remove it instead...fix?
- } else if(remote.is_v6()) {
+ if (!isHbriConn) {
+ if(!c.hasField("I4")) {
+ c.setField("I4", v4.to_string());
+ }
+
+ string tmp;
+ doValidation = cmd.getParam("I6", 0, tmp) && !tmp.empty();
+ }
+
+ cmd.delParam("U6", 0);
+ cmd.delParam("I6", 0);
+ } else {
if(cmd.getParam("I6", 0, ip)) {
dcdebug("%s verifying IPv6 %s\n", AdcCommand::fromSID(c.getSID()).c_str(), ip.c_str());
if(ip.empty() || address_v6::from_string(ip) == address_v6::any()) {
cmd.delParam("I6", 0);
- } else if(address_v6::from_string(ip) != remote.to_v6() && !Util::isPrivateIp(c.getIp())) {
+ cmd.addParam("I6", c.getIp());
+ } else if(address_v6::from_string(ip) != remote.to_v6() && !Util::isPrivateIp(c.getIp(), true)) {
disconnect(c, Util::REASON_INVALID_IP, "Your IP is " + c.getIp() +
", reconfigure your client settings", AdcCommand::ERROR_BAD_IP, "IP" + c.getIp());
return false;
@@ -383,15 +530,25 @@
}
}
- if(!c.hasField("I6")) {
- c.setField("I6", c.getIp());
- }
-
- if(c.getState() != Entity::STATE_NORMAL) {
- cmd.addParam("I6", c.getIp());
- }
-
- cmd.delParam("I4", 0); // We can't check this so we remove it instead...fix?
+ if (!isHbriConn) {
+ if (!c.hasField("I6")) {
+ c.setField("I6", c.getIp());
+ }
+
+ string tmp;
+ doValidation = cmd.getParam("I4", 0, tmp) && !tmp.empty();
+ }
+
+ cmd.delParam("I4", 0);
+ cmd.delParam("U4", 0);
+ }
+
+ if (doValidation) {
+ if (c.getState() == Entity::STATE_NORMAL) {
+ sendHBRI(c);
+ } else {
+ c.setFlag(Entity::FLAG_VALIDATE_HBRI);
+ }
}
return true;
@@ -516,7 +673,7 @@
signalState_(c, oldState);
}
-void ClientManager::disconnect(Entity& c, Util::Reason reason, const std::string& info, AdcCommand::Error error, const std::string& staParam) {
+void ClientManager::disconnect(Entity& c, Util::Reason reason, const std::string& info, AdcCommand::Error error, const std::string& staParam, int aReconnectTime) {
// send a fatal STA
AdcCommand sta(AdcCommand::SEV_FATAL, error, info);
if(!staParam.empty())
@@ -525,7 +682,7 @@
// send a QUI
c.send(AdcCommand(AdcCommand::CMD_QUI).addParam(AdcCommand::fromSID(c.getSID()))
- .addParam("DI", "1").addParam("MS", info).addParam("TL", "-1"));
+ .addParam("DI", "1").addParam("MS", info).addParam("TL", Util::toString(aReconnectTime)));
c.disconnect(reason);
}
@@ -562,6 +719,14 @@
}
bool ClientManager::enterNormal(Entity& c, bool sendData, bool sendOwnInf) throw() {
+ if (c.isSet(Entity::FLAG_VALIDATE_HBRI)) {
+ if (sendHBRI(c)) {
+ return false;
+ }
+
+ c.unsetFlag(Entity::FLAG_VALIDATE_HBRI);
+ }
+
dcassert(c.getState() == Entity::STATE_IDENTIFY || c.getState() == Entity::STATE_VERIFY);
dcdebug("%s entering NORMAL\n", AdcCommand::fromSID(c.getSID()).c_str());
@@ -596,6 +761,13 @@
if(i != logins.end()) {
logins.erase(i);
}
+
+ if (e.hasSupport(AdcCommand::toFourCC("HBRI"))) {
+ auto i = find_if(hbriTokens | map_values, CompareFirst<Entity*, time::ptime>(c)).base();
+ if (i != hbriTokens.end()) {
+ hbriTokens.erase(i);
+ }
+ }
}
void ClientManager::removeEntity(Entity& c, Util::Reason reason, const std::string &info) throw() {
@@ -644,4 +816,4 @@
removeEntity(c, reason, info);
}
-}
+}
\ No newline at end of file
=== modified file 'adchpp/ClientManager.h'
--- adchpp/ClientManager.h 2013-01-18 21:41:53 +0000
+++ adchpp/ClientManager.h 2013-11-12 23:29:50 +0000
@@ -131,7 +131,7 @@
/**
* Verify that IP is correct and replace any zero addresses.
*/
- ADCHPP_DLL bool verifyIp(Client& c, AdcCommand& cmd) throw();
+ ADCHPP_DLL bool verifyIp(Client& c, AdcCommand& cmd, bool isHbriConn) throw();
/**
* Verify that CID is correct and corresponds to PID
@@ -169,10 +169,13 @@
void setMaxCommandSize(size_t newSize) { maxCommandSize = newSize; }
size_t getMaxCommandSize() const { return maxCommandSize; }
+ void setHbriTimeout(size_t millis) { hbriTimeout = millis; }
+ size_t getHbriTimeout() const { return hbriTimeout; }
void setLogTimeout(size_t millis) { logTimeout = millis; }
size_t getLogTimeout() const { return logTimeout; }
Core &getCore() const { return core; }
+ void prepareSupports(bool addHbri);
private:
friend class Core;
friend class Client;
@@ -183,6 +186,9 @@
std::list<std::pair<Client*, time::ptime> > logins;
+ typedef std::unordered_map<std::string, std::pair<Entity*, time::ptime>> TokenMap;
+ TokenMap hbriTokens;
+
EntityMap entities;
typedef std::unordered_map<std::string, Entity*> NickMap;
NickMap nicks;
@@ -193,6 +199,7 @@
size_t maxCommandSize;
size_t logTimeout;
+ size_t hbriTimeout;
// Temporary string to use whenever a temporary string is needed (to avoid (de)allocating memory all the time...)
std::string strtmp;
@@ -203,6 +210,7 @@
uint32_t makeSID();
+ bool sendHBRI(Entity& c);
void maybeSend(Entity& c, const AdcCommand& cmd);
void removeLogins(Entity& c) throw();
@@ -210,6 +218,7 @@
bool handle(AdcCommand::SUP, Entity& c, AdcCommand& cmd) throw();
bool handle(AdcCommand::INF, Entity& c, AdcCommand& cmd) throw();
+ bool handle(AdcCommand::TCP, Entity& c, AdcCommand& cmd) throw();
bool handleDefault(Entity& c, AdcCommand& cmd) throw();
template<typename T> bool handle(T, Entity& c, AdcCommand& cmd) throw() { return handleDefault(c, cmd); }
@@ -225,7 +234,7 @@
void badState(Entity& c, const AdcCommand& cmd) throw();
/** send a fatal STA, a QUI with TL-1, then disconnect. */
void disconnect(Entity& c, Util::Reason reason, const std::string& info,
- AdcCommand::Error error = AdcCommand::ERROR_PROTOCOL_GENERIC, const std::string& staParam = Util::emptyString);
+ AdcCommand::Error error = AdcCommand::ERROR_PROTOCOL_GENERIC, const std::string& staParam = Util::emptyString, int aReconnectTime = -1);
SignalConnected::Signal signalConnected_;
SignalReady::Signal signalReady_;
@@ -236,6 +245,7 @@
SignalDisconnected::Signal signalDisconnected_;
ClientManager(Core &core) throw();
+ void onTimerSecond();
};
}
=== modified file 'adchpp/Entity.cpp'
--- adchpp/Entity.cpp 2013-01-18 21:41:53 +0000
+++ adchpp/Entity.cpp 2013-11-12 23:29:50 +0000
@@ -48,10 +48,9 @@
if(code == AdcCommand::toField("SU")) {
filters.clear();
-
- if((value.size() + 1) % 5 == 0) {
+ if ((value.size() + 1) % 5 == 0) {
filters.reserve((value.size() + 1) / 5);
- for(size_t i = 0; i < value.size(); i += 5) {
+ for (size_t i = 0; i < value.size(); i += 5) {
filters.push_back(AdcCommand::toFourCC(value.data() + i));
}
}
@@ -154,6 +153,41 @@
}
}
+bool Entity::hasClientSupport(uint32_t feature) const {
+ return std::find(filters.begin(), filters.end(), feature) != filters.end();
+}
+
+bool Entity::removeClientSupport(uint32_t feature) {
+ auto f = std::find(filters.begin(), filters.end(), feature);
+ if (f == filters.end()) {
+ return false;
+ }
+
+ filters.erase(f);
+
+ auto& supports = fields.find(AdcCommand::toField("SU"))->second;
+
+ auto p = supports.find(AdcCommand::fromFourCC(feature));
+ dcassert(p != std::string::npos);
+ supports.erase(p, 5);
+
+ if (!supports.empty() && supports.back() == ',')
+ supports.pop_back();
+
+ return true;
+}
+
+static const int protoSupportCount = 2;
+static uint32_t supports4[protoSupportCount] = { AdcCommand::toFourCC("TCP4"), AdcCommand::toFourCC("UDP4") };
+static uint32_t supports6[protoSupportCount] = { AdcCommand::toFourCC("TCP6"), AdcCommand::toFourCC("UDP6") };
+
+void Entity::stripProtocolSupports() throw() {
+ const auto& sup = dynamic_cast<Client*>(this)->isV6() ? supports4 : supports6;
+ for (auto i = 0; i < protoSupportCount; ++i) {
+ removeClientSupport(sup[i]);
+ }
+}
+
bool Entity::isFiltered(const std::string& features) const {
if(filters.empty()) {
return true;
=== modified file 'adchpp/Entity.h'
--- adchpp/Entity.h 2013-01-18 21:41:53 +0000
+++ adchpp/Entity.h 2013-11-12 23:29:50 +0000
@@ -33,6 +33,8 @@
enum State {
/** Initial protocol negotiation (wait for SUP) */
STATE_PROTOCOL,
+ /** Validating the secondary address */
+ STATE_HBRI,
/** Identify the connecting client (wait for INF) */
STATE_IDENTIFY,
/** Verify the client (wait for PAS) */
@@ -63,7 +65,10 @@
FLAG_OK_IP = 0x400,
/** This entity is now a ghost being disconnected, totally ignored by ADCH++ */
- FLAG_GHOST = 0x800
+ FLAG_GHOST = 0x800,
+
+ /** Set in the login phase if the users provides an IP for another protocol **/
+ FLAG_VALIDATE_HBRI = 0x1000
};
@@ -87,6 +92,12 @@
ADCHPP_DLL bool hasSupport(uint32_t feature) const;
ADCHPP_DLL bool removeSupports(uint32_t feature);
+ ADCHPP_DLL bool hasClientSupport(uint32_t feature) const;
+ ADCHPP_DLL bool removeClientSupport(uint32_t feature);
+
+ /** Remove supports for the protocol that wasn't used for connecting **/
+ ADCHPP_DLL void stripProtocolSupports() throw();
+
ADCHPP_DLL const BufferPtr& getSUP() const;
uint32_t getSID() const { return sid; }
=== modified file 'adchpp/ManagedSocket.cpp'
--- adchpp/ManagedSocket.cpp 2013-07-19 19:47:27 +0000
+++ adchpp/ManagedSocket.cpp 2013-11-12 23:29:50 +0000
@@ -22,18 +22,21 @@
#include "SocketManager.h"
+#include <boost/asio/ip/address.hpp>
+
namespace adchpp {
using namespace std;
using namespace boost::asio;
-ManagedSocket::ManagedSocket(SocketManager &sm, const AsyncStreamPtr &sock_) :
+ManagedSocket::ManagedSocket(SocketManager &sm, const AsyncStreamPtr &sock_, const ServerInfoPtr& aServer) :
sock(sock_),
overflow(time::not_a_date_time),
disc(time::not_a_date_time),
lastWrite(time::not_a_date_time),
- sm(sm)
+ sm(sm),
+ server(aServer)
{ }
ManagedSocket::~ManagedSocket() throw() {
@@ -251,6 +254,39 @@
std::string info;
};
+bool ManagedSocket::getHbriParams(AdcCommand& cmd) const throw() {
+ if (!isV6()) {
+ if (!server->address6.empty()) {
+ cmd.addParam("I6", server->address6);
+ } else {
+ return false;
+ }
+
+ cmd.addParam("P6", server->port);
+ } else {
+ if (!server->address4.empty()) {
+ cmd.addParam("I4", server->address4);
+ } else {
+ return false;
+ }
+
+ cmd.addParam("P4", server->port);
+ }
+
+ return true;
+}
+
+bool ManagedSocket::isV6() const throw() {
+ using namespace boost::asio::ip;
+
+ address remote;
+ remote = address::from_string(ip);
+ if (remote.is_v4() || (remote.is_v6() && remote.to_v6().is_v4_mapped()))
+ return false;
+
+ return true;
+}
+
void ManagedSocket::disconnect(Util::Reason reason, const std::string &info) throw() {
if(disconnecting()) {
return;
=== modified file 'adchpp/ManagedSocket.h'
--- adchpp/ManagedSocket.h 2013-07-19 19:47:27 +0000
+++ adchpp/ManagedSocket.h 2013-11-12 23:29:50 +0000
@@ -22,6 +22,8 @@
#include "common.h"
#include "forward.h"
+
+#include "AdcCommand.h"
#include "Signal.h"
#include "Util.h"
#include "Buffer.h"
@@ -35,7 +37,7 @@
*/
class ManagedSocket : private boost::noncopyable, public enable_shared_from_this<ManagedSocket> {
public:
- ManagedSocket(SocketManager &sm, const AsyncStreamPtr& sock_);
+ ManagedSocket(SocketManager &sm, const AsyncStreamPtr& sock_, const ServerInfoPtr& aServer);
/** Asynchronous write */
ADCHPP_DLL void write(const BufferPtr& buf, bool lowPrio = false) throw();
@@ -68,6 +70,8 @@
~ManagedSocket() throw();
+ bool getHbriParams(AdcCommand& cmd) const throw();
+ bool isV6() const throw();
private:
friend class SocketManager;
friend class SocketFactory;
@@ -110,6 +114,8 @@
FailedHandler failedHandler;
SocketManager &sm;
+
+ ServerInfoPtr server;
};
}
=== modified file 'adchpp/ServerInfo.h'
--- adchpp/ServerInfo.h 2013-01-18 21:41:53 +0000
+++ adchpp/ServerInfo.h 2013-11-12 23:29:50 +0000
@@ -22,7 +22,10 @@
namespace adchpp {
struct ServerInfo {
- std::string ip;
+ std::string bind4;
+ std::string bind6;
+ std::string address4;
+ std::string address6;
std::string port;
struct TLSInfo {
=== modified file 'adchpp/SocketManager.cpp'
--- adchpp/SocketManager.cpp 2013-07-19 19:47:27 +0000
+++ adchpp/SocketManager.cpp 2013-11-12 23:29:50 +0000
@@ -20,6 +20,7 @@
#include "SocketManager.h"
+#include "ClientManager.h"
#include "LogManager.h"
#include "ManagedSocket.h"
#include "ServerInfo.h"
@@ -47,7 +48,9 @@
bufferSize(1024),
maxBufferSize(16 * 1024),
overflowTimeout(60 * 1000),
-disconnectTimeout(10 * 1000)
+disconnectTimeout(10 * 1000),
+hasV4Address(false),
+hasV6Address(false)
{
}
@@ -192,10 +195,11 @@
class SocketFactory : public enable_shared_from_this<SocketFactory>, boost::noncopyable {
public:
- SocketFactory(SocketManager& sm, const SocketManager::IncomingHandler& handler_, const ServerInfo& info, const ip::tcp::endpoint& endpoint) :
+ SocketFactory(SocketManager& sm, const SocketManager::IncomingHandler& handler_, ServerInfoPtr& info, const ip::tcp::endpoint& endpoint) :
sm(sm),
acceptor(sm.io),
- handler(handler_)
+ handler(handler_),
+ si(info)
{
acceptor.open(endpoint.protocol());
acceptor.set_option(socket_base::reuse_address(true));
@@ -203,21 +207,23 @@
acceptor.set_option(ip::v6_only(true));
}
+ auto a = endpoint.address().to_string();
+
acceptor.bind(endpoint);
acceptor.listen(socket_base::max_connections);
LOGC(sm.getCore(), SocketManager::className,
"Listening on " + formatEndpoint(endpoint) +
- " (Encrypted: " + (info.secure() ? "Yes)" : "No)"));
+ " (Encrypted: " + (info->secure() ? "Yes)" : "No)"));
#ifdef HAVE_OPENSSL
- if(info.secure()) {
+ if(info->secure()) {
context.reset(new ssl::context(sm.io, ssl::context::tlsv1_server));
context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3 | ssl::context::single_dh_use);
//context->set_password_callback(boost::bind(&server::get_password, this));
- context->use_certificate_chain_file(info.TLSParams.cert);
- context->use_private_key_file(info.TLSParams.pkey, ssl::context::pem);
- context->use_tmp_dh_file(info.TLSParams.dh);
+ context->use_certificate_chain_file(info->TLSParams.cert);
+ context->use_private_key_file(info->TLSParams.pkey, ssl::context::pem);
+ context->use_tmp_dh_file(info->TLSParams.dh);
}
#endif
}
@@ -230,12 +236,12 @@
#ifdef HAVE_OPENSSL
if(context) {
auto s = make_shared<TLSSocketStream>(sm.io, *context);
- auto socket = make_shared<ManagedSocket>(sm, s);
+ auto socket = make_shared<ManagedSocket>(sm, s, si);
acceptor.async_accept(s->sock.lowest_layer(), std::bind(&SocketFactory::handleAccept, shared_from_this(), std::placeholders::_1, socket));
} else {
#endif
auto s = make_shared<SimpleSocketStream>(sm.io);
- auto socket = make_shared<ManagedSocket>(sm, s);
+ auto socket = make_shared<ManagedSocket>(sm, s, si);
acceptor.async_accept(s->sock.lowest_layer(), std::bind(&SocketFactory::handleAccept, shared_from_this(), std::placeholders::_1, socket));
#ifdef HAVE_OPENSSL
}
@@ -245,7 +251,9 @@
void handleAccept(const error_code& ec, const ManagedSocketPtr& socket) {
if(!ec) {
socket->sock->setOptions(sm.getBufferSize());
- socket->setIp(socket->sock->getIp());
+ auto ip = socket->sock->getIp();
+ auto p = ip.find("%");
+ socket->setIp(p != string::npos ? ip.substr(0, p) : ip);
}
completeAccept(ec, socket);
@@ -263,37 +271,68 @@
SocketManager &sm;
ip::tcp::acceptor acceptor;
SocketManager::IncomingHandler handler;
-
+ ServerInfoPtr si;
#ifdef HAVE_OPENSSL
unique_ptr<ssl::context> context;
#endif
};
+void SocketManager::prepareProtocol(ServerInfoPtr& si, bool v6) {
+ const string proto = v6 ? "IPv6" : "IPv4";
+ try {
+ using ip::tcp;
+ tcp::resolver r(io);
+
+ // Resolve the public address
+ string& hubAddress = v6 ? si->address6 : si->address4;
+ if (!hubAddress.empty()) {
+ try {
+ auto remote = r.resolve(tcp::resolver::query(v6 ? tcp::v6() : tcp::v4(), hubAddress, si->port,
+ tcp::resolver::query::address_configured | tcp::resolver::query::passive));
+ hubAddress = remote->endpoint().address().to_string();
+ v6 ? hasV6Address = true : hasV4Address = true;
+ } catch (const std::exception& e) {
+ LOG(SocketManager::className, "Error when resolving the " + proto + " hub address " + hubAddress + ": " + e.what());
+ hubAddress = Util::emptyString;
+ }
+ }
+
+ // Resolve the bind address
+ auto local = r.resolve(tcp::resolver::query(v6 ? tcp::v6() : tcp::v4(), v6 ? si->bind6 : si->bind4, si->port,
+ tcp::resolver::query::address_configured | tcp::resolver::query::passive));
+
+ for (auto i = local; i != tcp::resolver::iterator(); ++i) {
+ auto factory = make_shared<SocketFactory>(*this, incomingHandler, si, *i);
+ factory->prepareAccept();
+ factories.push_back(factory);
+ }
+ } catch (const std::exception& e) {
+ LOG(SocketManager::className, "Error while loading " + proto + " server on port " + si->port + ": " + e.what());
+ }
+}
+
int SocketManager::run() {
+ //Sleep(10000);
LOG(SocketManager::className, "Starting");
work.reset(new io_service::work(io));
for(auto i = servers.begin(), iend = servers.end(); i != iend; ++i) {
auto& si = *i;
-
- try {
- using ip::tcp;
- tcp::resolver r(io);
- auto local = r.resolve(tcp::resolver::query(si->ip, si->port,
- tcp::resolver::query::address_configured | tcp::resolver::query::passive));
-
- for(auto i = local; i != tcp::resolver::iterator(); ++i) {
- SocketFactoryPtr factory = make_shared<SocketFactory>(*this, incomingHandler, *si, *i);
- factory->prepareAccept();
- factories.push_back(factory);
- }
- } catch(const std::exception& e) {
- LOG(SocketManager::className, "Error while loading server on port " + si->port +": " + e.what());
+ bool listenAll = si->bind4.empty() && si->bind6.empty();
+
+ if (!si->bind4.empty() || listenAll) {
+ prepareProtocol(si, false);
+ }
+
+ if (!si->bind6.empty() || listenAll) {
+ prepareProtocol(si, true);
}
}
+ core.getClientManager().prepareSupports(hasV4Address && hasV6Address);
+
io.run();
io.reset();
=== modified file 'adchpp/SocketManager.h'
--- adchpp/SocketManager.h 2013-07-19 19:47:27 +0000
+++ adchpp/SocketManager.h 2013-11-12 23:29:50 +0000
@@ -94,6 +94,7 @@
friend class ManagedSocket;
friend class SocketFactory;
+ void prepareProtocol(ServerInfoPtr& si, bool v6);
void closeFactories();
Core &core;
@@ -126,6 +127,9 @@
void onLoad(const SimpleXML& xml) throw();
SocketManager(Core &core);
+
+ bool hasV4Address;
+ bool hasV6Address;
};
}
=== modified file 'adchpp/Util.cpp'
--- adchpp/Util.cpp 2013-01-18 21:41:53 +0000
+++ adchpp/Util.cpp 2013-11-12 23:29:50 +0000
@@ -338,7 +338,11 @@
}
#endif
-bool Util::isPrivateIp(std::string const& ip) {
+bool Util::isPrivateIp(std::string const& ip, bool v6) {
+ if (v6) {
+ return strncmp(ip.c_str(), "fe80", 4) == 0;
+ }
+
struct in_addr addr;
addr.s_addr = inet_addr(ip.c_str());
=== modified file 'adchpp/Util.h'
--- adchpp/Util.h 2013-01-18 21:41:53 +0000
+++ adchpp/Util.h 2013-11-12 23:29:50 +0000
@@ -102,6 +102,7 @@
REASON_INVALID_DESCRIPTION,
REASON_WRITE_TIMEOUT,
REASON_SOCKET_ERROR,
+ REASON_HBRI,
REASON_LAST,
};
@@ -228,7 +229,7 @@
static uint32_t rand(uint32_t low, uint32_t high) { return rand(high-low) + low; }
static double randd() { return ((double)rand()) / ((double)0xffffffff); }
- ADCHPP_DLL static bool isPrivateIp(std::string const& ip);
+ ADCHPP_DLL static bool isPrivateIp(std::string const& ip, bool v6);
ADCHPP_DLL static bool validateCharset(std::string const& field, int p);
};
=== modified file 'adchppd/adchppd.cpp'
--- adchppd/adchppd.cpp 2013-07-19 19:47:27 +0000
+++ adchppd/adchppd.cpp 2013-11-12 23:29:50 +0000
@@ -72,6 +72,8 @@
core.getSocketManager().setDisconnectTimeout(Util::toInt(xml.getChildData()));
} else if(xml.getChildName() == "LogTimeout") {
core.getClientManager().setLogTimeout(Util::toInt(xml.getChildData()));
+ } else if (xml.getChildName() == "HbriTimeout") {
+ core.getClientManager().setHbriTimeout(Util::toInt(xml.getChildData()));
}
}
@@ -85,6 +87,11 @@
ServerInfoPtr server = make_shared<ServerInfo>();
server->port = xml.getChildAttrib("Port", Util::emptyString);
+ server->bind4 = xml.getChildAttrib("BindAddress4", Util::emptyString);
+ server->bind6 = xml.getChildAttrib("BindAddress6", Util::emptyString);
+ server->address4 = xml.getChildAttrib("HubAddress4", Util::emptyString);
+ server->address6 = xml.getChildAttrib("HubAddress6", Util::emptyString);
+
if(xml.getBoolChildAttrib("TLS")) {
server->TLSParams.cert = File::makeAbsolutePath(xml.getChildAttrib("Certificate"));
server->TLSParams.pkey = File::makeAbsolutePath(xml.getChildAttrib("PrivateKey"));
=== modified file 'changelog.txt'
--- changelog.txt 2013-09-20 17:37:55 +0000
+++ changelog.txt 2013-11-12 23:29:50 +0000
@@ -1,3 +1,8 @@
+* Add support for hybrid IPv4/IPv6 client connectivity (maksis)
+* [L#1088638] Allow clients to reconnect in event of overflow (maksis)
+* [L#1106226] Fix a crash when joining the hub with a local IPv6 address (maksis)
+* Make login timeout work independently from other connecting users (maksis)
+* Allow configuring custom bind addresses (maksis)
* Improve Lua scripts
-- 2.11.0 2013-07-19 --
=== modified file 'etc/adchpp.xml'
--- etc/adchpp.xml 2013-07-19 19:47:27 +0000
+++ etc/adchpp.xml 2013-11-12 23:29:50 +0000
@@ -51,6 +51,10 @@
<!-- Timeout (ms) before disconnecting users whose login process is taking too long. -->
<LogTimeout>10000</LogTimeout>
+
+ <!-- Timeout (ms) before aborting validation for the secondary IP protocol.
+ The login procedure will continue normally with the primary IP protocol then. -->
+ <HbriTimeout>3000</HbriTimeout>
</Settings>
<Servers>
@@ -59,15 +63,22 @@
To create secure connections, set TLS="1" and define the following (preferably absolute)
paths: Certificate, PrivateKey, TrustedPath, DHParams. An example secure server setting:
- <Server Port="2780" TLS="1" Certificate="certs/cacert.pem" PrivateKey="certs/privkey.pem" TrustedPath="certs/trusted/" DHParams="certs/dhparam.pem"/>
+ <Server Port="2780" TLS="1" Certificate="certs/cacert.pem" PrivateKey="certs/privkey.pem" TrustedPath="certs/trusted/" DHParams="certs/dhparam.pem" BindAddress4="0.0.0.0" BindAddress6="::"/>
Simple OpenSSL commands to generate files used for secure connections:
openssl genrsa -out privkey.pem 2048
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
openssl dhparam -outform PEM -out dhparam.pem 1024
-
+
Alternatively, you can use the cert generator contributed on
<http://launchpadlibrarian.net/31960965/Cert_Generator.7z>.
+
+ To allow hybrid connectivity, add address fields for the used protocols: HubAddress4 and HubAddress6. You may also use the same
+ DNS entry for both if it has A and AAAA records. You may also use plain IP addresses.
+ Example:
+ <Server Port="2780" HubAddress4="mydomain4.net" HubAddress6="mydomain6.net"/>
+
+ Bind addresses may be set with: BindAddress4 and BindAddress6 (connections will be accepted for both protocols if none is set)
-->
<Server Port="2780"/>
=== modified file 'swig/adchpp.i'
--- swig/adchpp.i 2013-07-19 19:47:27 +0000
+++ swig/adchpp.i 2013-11-12 23:29:50 +0000
@@ -126,7 +126,10 @@
typedef shared_ptr<ManagedConnection> ManagedConnectionPtr;
struct ServerInfo {
- std::string ip;
+ std::string bind4;
+ std::string bind6;
+ std::string address4;
+ std::string address6;
std::string port;
TLSInfo TLSParams;
@@ -291,7 +294,7 @@
static uint32_t rand(uint32_t low, uint32_t high);
static double randd();
- static bool isPrivateIp(std::string const& ip);
+ static bool isPrivateIp(std::string const& ip, bool v6);
static bool validateCharset(std::string const& field, int p);
};
@@ -723,7 +726,7 @@
bool verifyPassword(Entity& c, const std::string& password, const ByteVector& salt, const std::string& suppliedHash);
bool verifyHashedPassword(Entity& c, const ByteVector& hashedPassword, int64_t hashedPasswordLen,
const ByteVector& salt, const std::string& suppliedHash) throw();
- bool verifyIp(Client& c, AdcCommand& cmd) throw();
+ bool verifyIp(Client& c, AdcCommand& cmd, bool isHbriConn) throw();
bool verifyCID(Entity& c, AdcCommand& cmd) throw();
bool verifyOverflow(Entity& c);
void setState(Entity& c, Entity::State newState) throw();
=== modified file 'swig/lua.i'
--- swig/lua.i 2013-02-22 22:36:03 +0000
+++ swig/lua.i 2013-11-12 23:29:50 +0000
@@ -322,7 +322,7 @@
if(ret) {
return *reinterpret_cast<SWIGLUA_REF*>(ret);
}
- return {0, 0};
+ return SWIGLUA_REF();
}
void setPluginData(const PluginDataHandle& handle, SWIGLUA_REF data) {
Follow ups