2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013-2016 Adam <Adam@anope.org>
6 * This file is part of InspIRCd. InspIRCd is free software: you can
7 * redistribute it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, version 2.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "modules/dns.h"
29 /** Derived from Resolver, and performs user forward/reverse lookups.
31 class UserResolver : public DNS::Request
33 /** UUID we are looking up */
34 const std::string uuid;
36 /** True if the lookup is forward, false if is a reverse lookup
40 const DNS::ResourceRecord* FindAnswerOfType(const DNS::Query* response, DNS::QueryType qtype)
42 for (std::vector<DNS::ResourceRecord>::const_iterator it = response->answers.begin(); it != response->answers.end(); ++it)
44 const DNS::ResourceRecord& rr = *it;
56 /** Create a resolver.
57 * @param mgr DNS Manager
58 * @param me this module
59 * @param user The user to begin lookup on
60 * @param to_resolve The IP or host to resolve
61 * @param qt The query type
63 UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
64 : DNS::Request(mgr, me, to_resolve, qt)
66 , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
70 /** Called on successful lookup
71 * if a previous result has already come back.
72 * @param r The finished query
74 void OnLookupComplete(const DNS::Query* r)
76 LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
79 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
83 const DNS::ResourceRecord* ans_record = FindAnswerOfType(r, this->type);
84 if (ans_record == NULL)
86 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS result for %s: no record of type %d", uuid.c_str(), this->type);
90 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record->name.c_str(), ans_record->rdata.c_str());
94 // first half of resolution is done. We now need to verify that the host matches.
95 ph->set(bound_user, ans_record->rdata);
97 UserResolver* res_forward;
98 if (bound_user->client_sa.sa.sa_family == AF_INET6)
100 /* IPV6 forward lookup */
101 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
106 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
110 this->manager->Process(res_forward);
112 catch (DNS::Exception& e)
115 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
117 bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
118 dl->set(bound_user, 0);
123 /* Both lookups completed */
125 irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
126 bool rev_match = false;
127 if (user_ip->sa.sa_family == AF_INET6)
129 struct in6_addr res_bin;
130 if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
132 rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
137 struct in_addr res_bin;
138 if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
140 rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
144 dl->set(bound_user, 0);
148 std::string* hostname = ph->get(bound_user);
150 if (hostname == NULL)
152 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
153 bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
156 else if (hostname->length() <= ServerInstance->Config->Limits.MaxHost)
158 /* Hostnames starting with : are not a good thing (tm) */
159 if ((*hostname)[0] == ':')
160 hostname->insert(0, "0");
162 bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
163 bound_user->host.assign(*hostname, 0, ServerInstance->Config->Limits.MaxHost);
164 bound_user->dhost = bound_user->host;
166 /* Invalidate cache */
167 bound_user->InvalidateCache();
171 bound_user->WriteNotice("*** Your hostname is longer than the maximum of " + ConvToStr(ServerInstance->Config->Limits.MaxHost) + " characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
174 ph->unset(bound_user);
178 bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
183 /** Called on failed lookup
184 * @param query The errored query
186 void OnError(const DNS::Query* query)
188 LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
191 bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
192 dl->set(bound_user, 0);
197 class ModuleHostnameLookup : public Module
199 LocalIntExt dnsLookup;
200 LocalStringExt ptrHosts;
201 dynamic_reference<DNS::Manager> DNS;
204 ModuleHostnameLookup()
205 : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
206 , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
213 void OnUserInit(LocalUser *user)
215 if (!DNS || !user->MyClass->resolvehostnames)
217 user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
221 user->WriteNotice("*** Looking up your hostname...");
223 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
226 /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
227 * before Process() completes, which is why dnsLookup.set() is here, before Process()
229 this->dnsLookup.set(user, 1);
230 this->DNS->Process(res_reverse);
232 catch (DNS::Exception& e)
234 this->dnsLookup.set(user, 0);
236 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
240 ModResult OnCheckReady(LocalUser* user)
242 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
247 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
251 MODULE_INIT(ModuleHostnameLookup)