mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-10 02:59:01 -04:00
Fix not being able to look up hostnames that point to multiple IPs.
This commit is contained in:
parent
71c62ea7cb
commit
e8e7a57492
@ -28,18 +28,24 @@ namespace
|
||||
LocalIntExt* dl;
|
||||
}
|
||||
|
||||
/** Derived from Resolver, and performs user forward/reverse lookups.
|
||||
*/
|
||||
class UserResolver : public DNS::Request
|
||||
class UserResolver
|
||||
: public DNS::Request
|
||||
{
|
||||
private:
|
||||
/** The socket address that the user we are looking up is connected from. */
|
||||
protected:
|
||||
// The socket address that the associated user was connected from at the time of lookup.
|
||||
const irc::sockets::sockaddrs sa;
|
||||
|
||||
/** UUID we are looking up */
|
||||
// The UUID of the user we are looking up the hostname of.
|
||||
const std::string uuid;
|
||||
|
||||
/** Handles errors which happen during DNS resolution. */
|
||||
UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
|
||||
: DNS::Request(mgr, me, to_resolve, qt)
|
||||
, sa(user->client_sa)
|
||||
, uuid(user->uuid)
|
||||
{
|
||||
}
|
||||
|
||||
// Handles errors which happen during DNS resolution.
|
||||
static void HandleError(LocalUser* user, const std::string& message)
|
||||
{
|
||||
user->WriteNotice("*** " + message + "; using your IP address (" + user->GetIPString() + ") instead.");
|
||||
@ -50,104 +56,139 @@ class UserResolver : public DNS::Request
|
||||
dl->unset(user);
|
||||
}
|
||||
|
||||
public:
|
||||
/** Create a resolver.
|
||||
* @param mgr DNS Manager
|
||||
* @param me this module
|
||||
* @param user The user to begin lookup on
|
||||
* @param to_resolve The IP or host to resolve
|
||||
* @param qt The query type
|
||||
*/
|
||||
UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
|
||||
: DNS::Request(mgr, me, to_resolve, qt)
|
||||
, sa(user->client_sa)
|
||||
, uuid(user->uuid)
|
||||
/** Logs the result of a DNS lookup. */
|
||||
inline void LogLookup(const DNS::ResourceRecord& rr, bool cached) const
|
||||
{
|
||||
}
|
||||
|
||||
/** Called on successful lookup
|
||||
* if a previous result has already come back.
|
||||
* @param r The finished query
|
||||
*/
|
||||
void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
|
||||
{
|
||||
LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
|
||||
if (!bound_user || bound_user->client_sa != sa)
|
||||
return;
|
||||
|
||||
const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
|
||||
if (ans_record == NULL)
|
||||
{
|
||||
HandleError(bound_user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS %s result for %s: '%s' -> '%s'%s",
|
||||
this->manager->GetTypeStr(question.type).c_str(), uuid.c_str(),
|
||||
ans_record->name.c_str(), ans_record->rdata.c_str(),
|
||||
r->cached ? " (cached)" : "");
|
||||
|
||||
if (this->question.type == DNS::QUERY_PTR)
|
||||
{
|
||||
const DNS::QueryType qt = bound_user->client_sa.family() == AF_INET6 ? DNS::QUERY_AAAA : DNS::QUERY_A;
|
||||
UserResolver* res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, qt);
|
||||
try
|
||||
{
|
||||
this->manager->Process(res_forward);
|
||||
}
|
||||
catch (DNS::Exception& e)
|
||||
{
|
||||
delete res_forward;
|
||||
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
|
||||
|
||||
HandleError(bound_user, "There was an internal error resolving your host");
|
||||
}
|
||||
}
|
||||
else if (this->question.type == DNS::QUERY_A || this->question.type == DNS::QUERY_AAAA)
|
||||
{
|
||||
/* Both lookups completed */
|
||||
|
||||
irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
|
||||
bool rev_match = false;
|
||||
if (user_ip->family() == AF_INET6)
|
||||
{
|
||||
struct in6_addr res_bin;
|
||||
if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
|
||||
{
|
||||
rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in_addr res_bin;
|
||||
if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
|
||||
{
|
||||
rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
|
||||
}
|
||||
}
|
||||
|
||||
if (rev_match)
|
||||
{
|
||||
bound_user->WriteNotice("*** Found your hostname (" + this->question.name + (r->cached ? ") -- cached" : ")"));
|
||||
bool display_is_real = bound_user->GetDisplayedHost() == bound_user->GetRealHost();
|
||||
bound_user->ChangeRealHost(this->question.name, display_is_real);
|
||||
dl->unset(bound_user);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleError(bound_user, "Your hostname does not match up with your IP address");
|
||||
}
|
||||
}
|
||||
manager->GetTypeStr(question.type).c_str(), uuid.c_str(), rr.name.c_str(),
|
||||
rr.rdata.c_str(), cached ? " (cached)" : "");
|
||||
}
|
||||
|
||||
/** Called on failed lookup
|
||||
* @param query The errored query
|
||||
*/
|
||||
public:
|
||||
void OnError(const DNS::Query* query) CXX11_OVERRIDE
|
||||
{
|
||||
LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
|
||||
if (bound_user && bound_user->client_sa == sa)
|
||||
HandleError(bound_user, "Could not resolve your hostname: " + this->manager->GetErrorStr(query->error));
|
||||
LocalUser* user = IS_LOCAL(ServerInstance->FindUUID(uuid));
|
||||
if (user && user->client_sa == sa)
|
||||
HandleError(user, "Could not resolve your hostname: " + this->manager->GetErrorStr(query->error));
|
||||
}
|
||||
};
|
||||
|
||||
// Handles checking the retrieved PTR record against its A/AAAA records.
|
||||
class UserIPResolver CXX11_FINAL
|
||||
: public UserResolver
|
||||
{
|
||||
public:
|
||||
UserIPResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& host)
|
||||
: UserResolver(mgr, me, user, host, user->client_sa.family() == AF_INET6 ? DNS::QUERY_AAAA : DNS::QUERY_A)
|
||||
{
|
||||
}
|
||||
|
||||
void OnLookupComplete(const DNS::Query* query) CXX11_OVERRIDE
|
||||
{
|
||||
LocalUser* user = IS_LOCAL(ServerInstance->FindUUID(uuid));
|
||||
if (!user || user->client_sa != sa)
|
||||
return;
|
||||
|
||||
bool hasrecord = false;
|
||||
bool matches = false;
|
||||
const DNS::QueryType qt = user->client_sa.family() == AF_INET6 ? DNS::QUERY_AAAA : DNS::QUERY_A;
|
||||
for (std::vector<DNS::ResourceRecord>::const_iterator i = query->answers.begin(); i != query->answers.end(); ++i)
|
||||
{
|
||||
const DNS::ResourceRecord& rr = *i;
|
||||
if (rr.type != qt)
|
||||
continue;
|
||||
|
||||
// We've seen an A/AAAA record.
|
||||
hasrecord = true;
|
||||
|
||||
switch (user->client_sa.family())
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
// Does this result match the user's IPv4 address?
|
||||
struct in_addr v4addr;
|
||||
if (inet_pton(AF_INET, rr.rdata.c_str(), &v4addr) == 1)
|
||||
matches = !memcmp(&user->client_sa.in4.sin_addr, &v4addr, sizeof(v4addr));
|
||||
break;
|
||||
}
|
||||
|
||||
case AF_INET6:
|
||||
{
|
||||
// Does this result match the user's IPv6 address?
|
||||
struct in6_addr v6addr;
|
||||
if (inet_pton(AF_INET6, rr.rdata.c_str(), &v6addr) == 1)
|
||||
matches = !memcmp(&user->client_sa.in6.sin6_addr, &v6addr, sizeof(v6addr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've found a valid match then stop processing records.
|
||||
if (matches)
|
||||
{
|
||||
LogLookup(rr, query->cached);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches)
|
||||
{
|
||||
// We have found the hostname for the user.
|
||||
user->WriteNotice("*** Found your hostname (" + this->question.name + (query->cached ? ") -- cached" : ")"));
|
||||
bool display_is_real = user->GetDisplayedHost() == user->GetRealHost();
|
||||
user->ChangeRealHost(this->question.name, display_is_real);
|
||||
dl->unset(user);
|
||||
}
|
||||
else if (hasrecord)
|
||||
{
|
||||
// The hostname has A/AAAA records but none of them are the IP address of the user.
|
||||
HandleError(user, "Your hostname does not match up with your IP address");
|
||||
}
|
||||
else
|
||||
{
|
||||
// The hostname has no A/AAAA records.
|
||||
HandleError(user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handles retrieving the PTR record for users.
|
||||
class UserHostResolver CXX11_FINAL
|
||||
: public UserResolver
|
||||
{
|
||||
public:
|
||||
UserHostResolver(DNS::Manager* mgr, Module* me, LocalUser* user)
|
||||
: UserResolver(mgr, me, user, user->GetIPString(), DNS::QUERY_PTR)
|
||||
{
|
||||
}
|
||||
|
||||
void OnLookupComplete(const DNS::Query* query) CXX11_OVERRIDE
|
||||
{
|
||||
LocalUser* user = IS_LOCAL(ServerInstance->FindUUID(uuid));
|
||||
if (!user || user->client_sa != sa)
|
||||
return;
|
||||
|
||||
// An IP can have multiple PTR records but it is considered bad practise to have
|
||||
// more than one so to simplify the lookup logic we only use the first.
|
||||
const DNS::ResourceRecord* rr = query->FindAnswerOfType(DNS::QUERY_PTR);
|
||||
if (!rr)
|
||||
{
|
||||
HandleError(user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
|
||||
return;
|
||||
}
|
||||
|
||||
LogLookup(*rr, query->cached);
|
||||
UserResolver* res_forward = new UserIPResolver(this->manager, this->creator, user, rr->rdata);
|
||||
try
|
||||
{
|
||||
this->manager->Process(res_forward);
|
||||
}
|
||||
catch (const DNS::Exception& e)
|
||||
{
|
||||
delete res_forward;
|
||||
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
|
||||
|
||||
HandleError(user, "There was an internal error resolving your host");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -178,7 +219,7 @@ class ModuleHostnameLookup : public Module
|
||||
|
||||
user->WriteNotice("*** Looking up your hostname...");
|
||||
|
||||
UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
|
||||
UserResolver* res_reverse = new UserHostResolver(*this->DNS, this, user);
|
||||
try
|
||||
{
|
||||
/* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
|
||||
|
Loading…
x
Reference in New Issue
Block a user