]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_hostname_lookup.cpp
0dd077a8e6049343bcc9b93c6a6a823b7de2ef39
[user/henk/code/inspircd.git] / src / coremods / core_hostname_lookup.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
5  *   Copyright (C) 2013, 2017-2019 Sadie Powell <sadie@witchery.services>
6  *   Copyright (C) 2013, 2016 Adam <Adam@anope.org>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "modules/dns.h"
24
25 namespace
26 {
27         LocalIntExt* dl;
28 }
29
30 /** Derived from Resolver, and performs user forward/reverse lookups.
31  */
32 class UserResolver : public DNS::Request
33 {
34  private:
35         /** UUID we are looking up */
36         const std::string uuid;
37
38         /** Handles errors which happen during DNS resolution. */
39         static void HandleError(LocalUser* user, const std::string& message)
40         {
41                 user->WriteNotice("*** " + message + "; using your IP address (" + user->GetIPString() + ") instead.");
42
43                 bool display_is_real = user->GetDisplayedHost() == user->GetRealHost();
44                 user->ChangeRealHost(user->GetIPString(), display_is_real);
45
46                 dl->unset(user);
47         }
48
49  public:
50         /** Create a resolver.
51          * @param mgr DNS Manager
52          * @param me this module
53          * @param user The user to begin lookup on
54          * @param to_resolve The IP or host to resolve
55          * @param qt The query type
56          */
57         UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
58                 : DNS::Request(mgr, me, to_resolve, qt)
59                 , uuid(user->uuid)
60         {
61         }
62
63         /** Called on successful lookup
64          * if a previous result has already come back.
65          * @param r The finished query
66          */
67         void OnLookupComplete(const DNS::Query* r) CXX11_OVERRIDE
68         {
69                 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
70                 if (!bound_user)
71                 {
72                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
73                         return;
74                 }
75
76                 const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
77                 if (ans_record == NULL)
78                 {
79                         HandleError(bound_user, "Could not resolve your hostname: No " + this->manager->GetTypeStr(this->question.type) + " records found");
80                         return;
81                 }
82
83                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "DNS %s result for %s: '%s' -> '%s'%s",
84                         this->manager->GetTypeStr(question.type).c_str(), uuid.c_str(),
85                         ans_record->name.c_str(), ans_record->rdata.c_str(),
86                         r->cached ? " (cached)" : "");
87
88                 if (this->question.type == DNS::QUERY_PTR)
89                 {
90                         UserResolver* res_forward;
91                         if (bound_user->client_sa.family() == AF_INET6)
92                         {
93                                 /* IPV6 forward lookup */
94                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
95                         }
96                         else
97                         {
98                                 /* IPV4 lookup */
99                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
100                         }
101                         try
102                         {
103                                 this->manager->Process(res_forward);
104                         }
105                         catch (DNS::Exception& e)
106                         {
107                                 delete res_forward;
108                                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
109
110                                 HandleError(bound_user, "There was an internal error resolving your host");
111                         }
112                 }
113                 else if (this->question.type == DNS::QUERY_A || this->question.type == DNS::QUERY_AAAA)
114                 {
115                         /* Both lookups completed */
116
117                         irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
118                         bool rev_match = false;
119                         if (user_ip->family() == AF_INET6)
120                         {
121                                 struct in6_addr res_bin;
122                                 if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
123                                 {
124                                         rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
125                                 }
126                         }
127                         else
128                         {
129                                 struct in_addr res_bin;
130                                 if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
131                                 {
132                                         rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
133                                 }
134                         }
135
136                         if (rev_match)
137                         {
138                                 bound_user->WriteNotice("*** Found your hostname (" + this->question.name + (r->cached ? ") -- cached" : ")"));
139                                 bool display_is_real = bound_user->GetDisplayedHost() == bound_user->GetRealHost();
140                                 bound_user->ChangeRealHost(this->question.name, display_is_real);
141                                 dl->unset(bound_user);
142                         }
143                         else
144                         {
145                                 HandleError(bound_user, "Your hostname does not match up with your IP address");
146                         }
147                 }
148         }
149
150         /** Called on failed lookup
151          * @param query The errored query
152          */
153         void OnError(const DNS::Query* query) CXX11_OVERRIDE
154         {
155                 LocalUser* bound_user = IS_LOCAL(ServerInstance->FindUUID(uuid));
156                 if (bound_user)
157                         HandleError(bound_user, "Could not resolve your hostname: " + this->manager->GetErrorStr(query->error));
158         }
159 };
160
161 class ModuleHostnameLookup : public Module
162 {
163  private:
164         LocalIntExt dnsLookup;
165         dynamic_reference<DNS::Manager> DNS;
166
167  public:
168         ModuleHostnameLookup()
169                 : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
170                 , DNS(this, "DNS")
171         {
172                 dl = &dnsLookup;
173         }
174
175         void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
176         {
177                 // If core_dns is not loaded or hostname resolution is disabled for the user's
178                 // connect class then the logic in this function does not apply.
179                 if (!DNS || !user->MyClass->resolvehostnames)
180                         return;
181
182                 // Clients can't have a DNS hostname if they aren't connected via IPv4 or IPv6.
183                 if (user->client_sa.family() != AF_INET && user->client_sa.family() != AF_INET6)
184                         return;
185
186                 user->WriteNotice("*** Looking up your hostname...");
187
188                 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
189                 try
190                 {
191                         /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
192                          * before Process() completes, which is why dnsLookup.set() is here, before Process()
193                          */
194                         this->dnsLookup.set(user, 1);
195                         this->DNS->Process(res_reverse);
196                 }
197                 catch (DNS::Exception& e)
198                 {
199                         this->dnsLookup.set(user, 0);
200                         delete res_reverse;
201                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
202                 }
203         }
204
205         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
206         {
207                 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
208         }
209
210         Version GetVersion() CXX11_OVERRIDE
211         {
212                 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
213         }
214 };
215
216 MODULE_INIT(ModuleHostnameLookup)