2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2020 Matt Schatz <genius3000@g3k.solutions>
5 * Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
6 * Copyright (C) 2013, 2017-2020 Sadie Powell <sadie@witchery.services>
7 * Copyright (C) 2013, 2016 Adam <Adam@anope.org>
9 * This file is part of InspIRCd. InspIRCd is free software: you can
10 * redistribute it and/or modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation, version 2.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "modules/dns.h"
31 /** Derived from Resolver, and performs user forward/reverse lookups.
33 class UserResolver : public DNS::Request
36 /** UUID we are looking up */
37 const std::string uuid;
39 /** Handles errors which happen during DNS resolution. */
40 static void HandleError(LocalUser* user, const std::string& message)
42 user->WriteNotice("*** " + message + "; using your IP address (" + user->GetIPString() + ") instead.");
44 bool display_is_real = user->GetDisplayedHost() == user->GetRealHost();
45 user->ChangeRealHost(user->GetIPString(), display_is_real);
51 /** Create a resolver.
52 * @param mgr DNS Manager
53 * @param me this module
54 * @param user The user to begin lookup on
55 * @param to_resolve The IP or host to resolve
56 * @param qt The query type
58 UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
59 : DNS::Request(mgr, me, to_resolve, qt)
64 /** Called on successful lookup
65 * if a previous result has already come back.
66 * @param r The finished query
68 void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
70 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
73 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
77 const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
78 if (ans_record == NULL)
80 HandleError(bound_user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
84 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS %s result for %s: '%s' -> '%s'%s",
85 this->manager->GetTypeStr(question.type).c_str(), uuid.c_str(),
86 ans_record->name.c_str(), ans_record->rdata.c_str(),
87 r->cached ? " (cached)" : "");
89 if (this->question.type == DNS::QUERY_PTR)
91 UserResolver* res_forward;
92 if (bound_user->client_sa.family() == AF_INET6)
94 /* IPV6 forward lookup */
95 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
100 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
104 this->manager->Process(res_forward);
106 catch (DNS::Exception& e)
109 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
111 HandleError(bound_user, "There was an internal error resolving your host");
114 else if (this->question.type == DNS::QUERY_A || this->question.type == DNS::QUERY_AAAA)
116 /* Both lookups completed */
118 irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
119 bool rev_match = false;
120 if (user_ip->family() == AF_INET6)
122 struct in6_addr res_bin;
123 if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
125 rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
130 struct in_addr res_bin;
131 if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
133 rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
139 bound_user->WriteNotice("*** Found your hostname (" + this->question.name + (r->cached ? ") -- cached" : ")"));
140 bool display_is_real = bound_user->GetDisplayedHost() == bound_user->GetRealHost();
141 bound_user->ChangeRealHost(this->question.name, display_is_real);
142 dl->unset(bound_user);
146 HandleError(bound_user, "Your hostname does not match up with your IP address");
151 /** Called on failed lookup
152 * @param query The errored query
154 void OnError(const DNS::Query* query) CXX11_OVERRIDE
156 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
158 HandleError(bound_user, "Could not resolve your hostname: " + this->manager->GetErrorStr(query->error));
162 class ModuleHostnameLookup : public Module
165 LocalIntExt dnsLookup;
166 dynamic_reference<DNS::Manager> DNS;
169 ModuleHostnameLookup()
170 : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
176 void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
178 // If core_dns is not loaded or hostname resolution is disabled for the user's
179 // connect class then the logic in this function does not apply.
180 if (!DNS || user->quitting || !user->MyClass->resolvehostnames)
183 // Clients can't have a DNS hostname if they aren't connected via IPv4 or IPv6.
184 if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
187 user->WriteNotice("*** Looking up your hostname...");
189 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
192 /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
193 * before Process() completes, which is why dnsLookup.set() is here, before Process()
195 this->dnsLookup.set(user, 1);
196 this->DNS->Process(res_reverse);
198 catch (DNS::Exception& e)
200 this->dnsLookup.set(user, 0);
202 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
206 ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
208 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
211 Version GetVersion() CXX11_OVERRIDE
213 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
217 MODULE_INIT(ModuleHostnameLookup)