]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands/cmd_hostname_lookup.cpp
Clean up CoreException
[user/henk/code/inspircd.git] / src / commands / cmd_hostname_lookup.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013 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("RESOLVER", LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
65                         return;
66                 }
67
68                 const DNS::ResourceRecord& ans_record = r->answers[0];
69
70                 ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record.name.c_str(), ans_record.rdata.c_str());
71
72                 if (!fwd)
73                 {
74                         // first half of resolution is done. We now need to verify that the host matches.
75                         ph->set(bound_user, ans_record.rdata);
76
77                         UserResolver* res_forward;
78                         if (bound_user->client_sa.sa.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
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->sa.sa_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                                 std::string* hostname = ph->get(bound_user);
129
130                                 if (hostname == NULL)
131                                 {
132                                         ServerInstance->Logs->Log("RESOLVER", LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
133                                         bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
134                                         return;
135                                 }
136                                 else if (hostname->length() < 65)
137                                 {
138                                         /* Hostnames starting with : are not a good thing (tm) */
139                                         if ((*hostname)[0] == ':')
140                                                 hostname->insert(0, "0");
141
142                                         bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
143                                         bound_user->host.assign(*hostname, 0, 64);
144                                         bound_user->dhost = bound_user->host;
145
146                                         /* Invalidate cache */
147                                         bound_user->InvalidateCache();
148                                 }
149                                 else
150                                 {
151                                         bound_user->WriteNotice("*** Your hostname is longer than the maximum of 64 characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
152                                 }
153
154                                 ph->unset(bound_user);
155                         }
156                         else
157                         {
158                                 bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
159                         }
160                 }
161         }
162
163         /** Called on failed lookup
164          * @param query The errored query
165          */
166         void OnError(const DNS::Query* query)
167         {
168                 LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
169                 if (bound_user)
170                 {
171                         bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
172                         dl->set(bound_user, 0);
173                         ServerInstance->stats->statsDnsBad++;
174                 }
175         }
176 };
177
178 class ModuleHostnameLookup : public Module
179 {
180         LocalIntExt dnsLookup;
181         LocalStringExt ptrHosts;
182         dynamic_reference<DNS::Manager> DNS;
183
184  public:
185         ModuleHostnameLookup()
186                 : dnsLookup("dnsLookup", this)
187                 , ptrHosts("ptrHosts", this)
188                 , DNS(this, "DNS")
189         {
190                 dl = &dnsLookup;
191                 ph = &ptrHosts;
192         }
193
194         void OnUserInit(LocalUser *user)
195         {
196                 if (!DNS || !user->MyClass->resolvehostnames)
197                 {
198                         user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
199                         return;
200                 }
201
202                 user->WriteNotice("*** Looking up your hostname...");
203
204                 UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
205                 try
206                 {
207                         /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
208                          * before Process() completes, which is why dnsLookup.set() is here, before Process()
209                          */
210                         this->dnsLookup.set(user, 1);
211                         this->DNS->Process(res_reverse);
212                 }
213                 catch (DNS::Exception& e)
214                 {
215                         this->dnsLookup.set(user, 0);
216                         delete res_reverse;
217                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Error in resolver: " + e.GetReason());
218                         ServerInstance->stats->statsDnsBad++;
219                 }
220         }
221
222         ModResult OnCheckReady(LocalUser* user)
223         {
224                 return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
225         }
226
227         Version GetVersion()
228         {
229                 return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
230         }
231 };
232
233 MODULE_INIT(ModuleHostnameLookup)