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