Fix not being able to look up hostnames that point to multiple IPs.

This commit is contained in:
Sadie Powell 2021-12-24 01:42:56 +00:00
parent 71c62ea7cb
commit e8e7a57492

View File

@ -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