]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_hostname_lookup.cpp
Avoid doing "IP changed" event stuff on quitting users.
[user/henk/code/inspircd.git] / src / coremods / core_hostname_lookup.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2020 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2013, 2017-2019 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2013, 2016 Adam <Adam@anope.org>
8  *
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.
12  *
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
16  * details.
17  *
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/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "modules/dns.h"
25
26 namespace
27 {
28         LocalIntExt* dl;
29 }
30
31 /** Derived from Resolver, and performs user forward/reverse lookups.
32  */
33 class UserResolver : public DNS::Request
34 {
35  private:
36         /** UUID we are looking up */
37         const std::string uuid;
38
39         /** Handles errors which happen during DNS resolution. */
40         static void HandleError(LocalUser* user, const std::string& message)
41         {
42                 user->WriteNotice("*** " + message + "; using your IP address (" + user->GetIPString() + ") instead.");
43
44                 bool display_is_real = user->GetDisplayedHost() == user->GetRealHost();
45                 user->ChangeRealHost(user->GetIPString(), display_is_real);
46
47                 dl->unset(user);
48         }
49
50  public:
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
57          */
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)
60                 , uuid(user->uuid)
61         {
62         }
63
64         /** Called on successful lookup
65          * if a previous result has already come back.
66          * @param r The finished query
67          */
68         void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
69         {
70                 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
71                 if (!bound_user)
72                 {
73                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
74                         return;
75                 }
76
77                 const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
78                 if (ans_record == NULL)
79                 {
80                         HandleError(bound_user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
81                         return;
82                 }
83
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)" : "");
88
89                 if (this->question.type == DNS::QUERY_PTR)
90                 {
91                         UserResolver* res_forward;
92                         if (bound_user->client_sa.family() == AF_INET6)
93                         {
94                                 /* IPV6 forward lookup */
95                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
96                         }
97                         else
98                         {
99                                 /* IPV4 lookup */
100                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
101                         }
102                         try
103                         {
104                                 this->manager->Process(res_forward);
105                         }
106                         catch (DNS::Exception& e)
107                         {
108                                 delete res_forward;
109                                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
110
111                                 HandleError(bound_user, "There was an internal error resolving your host");
112                         }
113                 }
114                 else if (this->question.type == DNS::QUERY_A || this->question.type == DNS::QUERY_AAAA)
115                 {
116                         /* Both lookups completed */
117
118                         irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
119                         bool rev_match = false;
120                         if (user_ip->family() == AF_INET6)
121                         {
122                                 struct in6_addr res_bin;
123                                 if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
124                                 {
125                                         rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
126                                 }
127                         }
128                         else
129                         {
130                                 struct in_addr res_bin;
131                                 if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
132                                 {
133                                         rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
134                                 }
135                         }
136
137                         if (rev_match)
138                         {
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);
143                         }
144                         else
145                         {
146                                 HandleError(bound_user, "Your hostname does not match up with your IP address");
147                         }
148                 }
149         }
150
151         /** Called on failed lookup
152          * @param query The errored query
153          */
154         void OnError(const DNS::Query* query) CXX11_OVERRIDE
155         {
156                 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
157                 if (bound_user)
158                         HandleError(bound_user, "Could not resolve your hostname: " + this->manager->GetErrorStr(query->error));
159         }
160 };
161
162 class ModuleHostnameLookup : public Module
163 {
164  private:
165         LocalIntExt dnsLookup;
166         dynamic_reference<DNS::Manager> DNS;
167
168  public:
169         ModuleHostnameLookup()
170                 : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
171                 , DNS(this, "DNS")
172         {
173                 dl = &dnsLookup;
174         }
175
176         void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
177         {
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)
181                         return;
182
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)
185                         return;
186
187                 user->WriteNotice("*** Looking up your hostname...");
188
189                 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
190                 try
191                 {
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()
194                          */
195                         this->dnsLookup.set(user, 1);
196                         this->DNS->Process(res_reverse);
197                 }
198                 catch (DNS::Exception& e)
199                 {
200                         this->dnsLookup.set(user, 0);
201                         delete res_reverse;
202                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
203                 }
204         }
205
206         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
207         {
208                 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
209         }
210
211         Version GetVersion() CXX11_OVERRIDE
212         {
213                 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
214         }
215 };
216
217 MODULE_INIT(ModuleHostnameLookup)