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