]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_hostname_lookup.cpp
d150b85516623cab55ca27348dea23c0d6510323
[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         LocalStringExt* ph;
27 }
28
29 /** Derived from Resolver, and performs user forward/reverse lookups.
30  */
31 class UserResolver : public DNS::Request
32 {
33         /** UUID we are looking up */
34         const std::string uuid;
35
36         /** True if the lookup is forward, false if is a reverse lookup
37          */
38         const bool fwd;
39
40  public:
41         /** Create a resolver.
42          * @param mgr DNS Manager
43          * @param me this module
44          * @param user The user to begin lookup on
45          * @param to_resolve The IP or host to resolve
46          * @param qt The query type
47          */
48         UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
49                 : DNS::Request(mgr, me, to_resolve, qt)
50                 , uuid(user->uuid)
51                 , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
52         {
53         }
54
55         /** Called on successful lookup
56          * if a previous result has already come back.
57          * @param r The finished query
58          */
59         void OnLookupComplete(const DNS::Query* r)
60         {
61                 LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
62                 if (!bound_user)
63                 {
64                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
65                         return;
66                 }
67
68                 const DNS::ResourceRecord* ans_record = r->FindAnswerOfType(this->question.type);
69                 if (ans_record == NULL)
70                 {
71                         OnError(r);
72                         return;
73                 }
74
75                 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());
76
77                 if (!fwd)
78                 {
79                         // first half of resolution is done. We now need to verify that the host matches.
80                         ph->set(bound_user, ans_record->rdata);
81
82                         UserResolver* res_forward;
83                         if (bound_user->client_sa.sa.sa_family == AF_INET6)
84                         {
85                                 /* IPV6 forward lookup */
86                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_AAAA);
87                         }
88                         else
89                         {
90                                 /* IPV4 lookup */
91                                 res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record->rdata, DNS::QUERY_A);
92                         }
93                         try
94                         {
95                                 this->manager->Process(res_forward);
96                         }
97                         catch (DNS::Exception& e)
98                         {
99                                 delete res_forward;
100                                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
101
102                                 bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
103                                 dl->set(bound_user, 0);
104                         }
105                 }
106                 else
107                 {
108                         /* Both lookups completed */
109
110                         irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
111                         bool rev_match = false;
112                         if (user_ip->sa.sa_family == AF_INET6)
113                         {
114                                 struct in6_addr res_bin;
115                                 if (inet_pton(AF_INET6, ans_record->rdata.c_str(), &res_bin))
116                                 {
117                                         rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
118                                 }
119                         }
120                         else
121                         {
122                                 struct in_addr res_bin;
123                                 if (inet_pton(AF_INET, ans_record->rdata.c_str(), &res_bin))
124                                 {
125                                         rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
126                                 }
127                         }
128
129                         dl->set(bound_user, 0);
130
131                         if (rev_match)
132                         {
133                                 std::string* hostname = ph->get(bound_user);
134
135                                 if (hostname == NULL)
136                                 {
137                                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
138                                         bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
139                                         return;
140                                 }
141                                 else if (hostname->length() <= ServerInstance->Config->Limits.MaxHost)
142                                 {
143                                         /* Hostnames starting with : are not a good thing (tm) */
144                                         if ((*hostname)[0] == ':')
145                                                 hostname->insert(0, "0");
146
147                                         bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
148                                         bound_user->host.assign(*hostname, 0, ServerInstance->Config->Limits.MaxHost);
149                                         bound_user->dhost = bound_user->host;
150
151                                         /* Invalidate cache */
152                                         bound_user->InvalidateCache();
153                                 }
154                                 else
155                                 {
156                                         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.");
157                                 }
158
159                                 ph->unset(bound_user);
160                         }
161                         else
162                         {
163                                 bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
164                         }
165                 }
166         }
167
168         /** Called on failed lookup
169          * @param query The errored query
170          */
171         void OnError(const DNS::Query* query)
172         {
173                 LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
174                 if (bound_user)
175                 {
176                         bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
177                         dl->set(bound_user, 0);
178                 }
179         }
180 };
181
182 class ModuleHostnameLookup : public Module
183 {
184         LocalIntExt dnsLookup;
185         LocalStringExt ptrHosts;
186         dynamic_reference<DNS::Manager> DNS;
187
188  public:
189         ModuleHostnameLookup()
190                 : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
191                 , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
192                 , DNS(this, "DNS")
193         {
194                 dl = &dnsLookup;
195                 ph = &ptrHosts;
196         }
197
198         void OnUserInit(LocalUser *user) CXX11_OVERRIDE
199         {
200                 if (!DNS || !user->MyClass->resolvehostnames)
201                 {
202                         user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
203                         return;
204                 }
205
206                 user->WriteNotice("*** Looking up your hostname...");
207
208                 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
209                 try
210                 {
211                         /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
212                          * before Process() completes, which is why dnsLookup.set() is here, before Process()
213                          */
214                         this->dnsLookup.set(user, 1);
215                         this->DNS->Process(res_reverse);
216                 }
217                 catch (DNS::Exception& e)
218                 {
219                         this->dnsLookup.set(user, 0);
220                         delete res_reverse;
221                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
222                 }
223         }
224
225         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
226         {
227                 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
228         }
229
230         Version GetVersion() CXX11_OVERRIDE
231         {
232                 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
233         }
234 };
235
236 MODULE_INIT(ModuleHostnameLookup)