]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ident.cpp
This is better now.
[user/henk/code/inspircd.git] / src / modules / m_ident.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include <stdio.h>
20 #include <string>
21 #include "users.h"
22 #include "channels.h"
23 #include "modules.h"
24 #include "inspircd.h"
25
26 /* $ModDesc: Provides support for RFC 1413 ident lookups */
27
28 // Version 1.5.0.0 - Updated to use InspSocket, faster and neater.
29
30 /** Handles RFC1413 ident connections to users
31  */
32 class RFC1413 : public InspSocket
33 {
34  protected:
35                          // Server* class used for core communications
36         insp_sockaddr sock_us;   // our port number
37         insp_sockaddr sock_them; // their port number
38         socklen_t uslen;         // length of our port number
39         socklen_t themlen;       // length of their port number
40         char ident_request[128]; // buffer used to make up the request string
41  public:
42
43         userrec* u;              // user record that the lookup is associated with
44         int ufd;
45
46         RFC1413(InspIRCd* SI, userrec* user, int maxtime) : InspSocket(SI, user->GetIPString(), 113, false, maxtime), u(user)
47         {
48                 ufd = user->GetFd();
49         }
50
51         virtual void OnTimeout()
52         {
53                 // When we timeout, the connection failed within the allowed timeframe,
54                 // so we just display a notice, and tidy off the ident_data.
55                 if (u && (Instance->SE->GetRef(ufd) == u))
56                 {
57                         char newident[MAXBUF];
58                         u->Shrink("ident_data");
59                         u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using ~"+std::string(u->ident)+" instead.");
60                         strcpy(newident,"~");
61                         strlcat(newident,u->ident,IDENTMAX);
62                         strlcpy(u->ident,newident,IDENTMAX);
63                 }
64         }
65
66         virtual bool OnDataReady()
67         {
68                 char* ibuf = this->Read();
69                 if (ibuf)
70                 {
71                         char* savept;
72                         char* section = strtok_r(ibuf,":",&savept);
73                         while (section)
74                         {
75                                 if (strstr(section,"USERID"))
76                                 {
77                                         section = strtok_r(NULL,":",&savept);
78                                         if (section)
79                                         {
80                                                 // ID type, usually UNIX or OTHER... we dont want it, so read the next token
81                                                 section = strtok_r(NULL,":",&savept);
82                                                 if (section)
83                                                 {
84                                                         while (*section == ' ') section++; // strip leading spaces
85                                                         for (char* j = section; *j; j++)
86                                                         if ((*j < 33) || (*j > 126))
87                                                                 *j = '\0'; // truncate at invalid chars
88                                                         if (*section)
89                                                         {
90                                                                 if (u && (Instance->SE->GetRef(ufd) == u))
91                                                                 {
92                                                                         if (this->Instance->IsIdent(section))
93                                                                         {
94                                                                                 strlcpy(u->ident,section,IDENTMAX);
95                                                                                 Instance->Log(DEBUG,"IDENT SET: "+std::string(u->ident));
96                                                                                 u->WriteServ("NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident));
97                                                                         }
98                                                                 }
99                                                         }
100                                                         return false;
101                                                 }
102                                         }
103                                 }
104                                 section = strtok_r(NULL,":",&savept);
105                         }
106                 }
107                 return false;
108         }
109
110         virtual void OnClose()
111         {
112                 // tidy up after ourselves when the connection is done.
113                 // We receive this event straight after a timeout, too.
114                 //
115                 //
116                 // OK, now listen up. The weird looking check here is
117                 // REQUIRED. Don't try and optimize it away.
118                 //
119                 // When a socket is closed, it is not immediately removed
120                 // from the socket list, there can be a short delay
121                 // before it is culled from the list. This means that
122                 // without this check, there is a chance that a user
123                 // may not exist when we come to ::Shrink them, which
124                 // results in a segfault. The value of "u" may not
125                 // always be NULL at this point, so, what we do is
126                 // check against the fd_ref_table, to see if (1) the user
127                 // exists, and (2) its the SAME user, on the same file
128                 // descriptor that they were when the lookup began.
129                 //
130                 // Fixes issue reported by webs, 7 Jun 2006
131                 if (u && (Instance->SE->GetRef(ufd) == u))
132                 {
133                         u->Shrink("ident_data");
134                 }
135         }
136
137         virtual void OnError(InspSocketError e)
138         {
139                 if (u && (Instance->SE->GetRef(ufd) == u))
140                 {
141                         u->Shrink("ident_data");
142                 }
143         }
144
145         virtual bool OnConnected()
146         {
147                 Instance->Log(DEBUG,"Ident: connected");
148                 if (u && (Instance->SE->GetRef(ufd) == u))
149                 {
150                         uslen = sizeof(sock_us);
151                         themlen = sizeof(sock_them);
152                         if ((getsockname(this->u->GetFd(),(sockaddr*)&sock_us,&uslen) || getpeername(this->u->GetFd(), (sockaddr*)&sock_them, &themlen)))
153                         {
154                                 Instance->Log(DEBUG,"Ident: failed to get socket names, bailing");
155                                 return false;
156                         }
157                         else
158                         {
159                                 // send the request in the following format: theirsocket,oursocket
160 #ifdef IPV6
161                                 snprintf(ident_request,127,"%d,%d\r\n",ntohs(sock_them.sin6_port),ntohs(sock_us.sin6_port));
162 #else
163                                 snprintf(ident_request,127,"%d,%d\r\n",ntohs(sock_them.sin_port),ntohs(sock_us.sin_port));
164 #endif
165                                 this->Write(ident_request);
166                                 Instance->Log(DEBUG,"Sent ident request, waiting for reply");
167                                 return true;
168                         }
169                 }
170                 else
171                 {
172                         return true;
173                 }
174         }
175 };
176
177 class ModuleIdent : public Module
178 {
179
180         ConfigReader* Conf;
181         
182         int IdentTimeout;
183
184  public:
185         void ReadSettings()
186         {
187                 Conf = new ConfigReader(ServerInstance);
188                 IdentTimeout = Conf->ReadInteger("ident","timeout",0,true);
189                 if (!IdentTimeout)
190                         IdentTimeout = 1;
191                 DELETE(Conf);
192         }
193
194         ModuleIdent(InspIRCd* Me)
195                 : Module::Module(Me)
196         {
197                 
198                 ReadSettings();
199         }
200
201         void Implements(char* List)
202         {
203                 List[I_OnCleanup] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
204         }
205
206         virtual void OnRehash(const std::string &parameter)
207         {
208                 ReadSettings();
209         }
210
211         virtual int OnUserRegister(userrec* user)
212         {
213                 /*
214                  * when the new user connects, before they authenticate with USER/NICK/PASS, we do
215                  * their ident lookup. We do this by instantiating an object of type RFC1413, which
216                  * is derived from InspSocket, and inserting it into the socket engine using the
217                  * Server::AddSocket() call.
218                  */
219                 user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Looking up your ident...");
220                 RFC1413* ident = new RFC1413(ServerInstance, user, IdentTimeout);
221                 if ((ident->GetState() == I_CONNECTING) || (ident->GetState() == I_CONNECTED))
222                 {
223                         user->Extend("ident_data", (char*)ident);
224                 }
225                 else
226                 {
227                         char newident[MAXBUF];
228                         user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not find your ident, using ~"+std::string(user->ident)+" instead.");
229                         strcpy(newident,"~");
230                         strlcat(newident,user->ident,IDENTMAX);
231                         strlcpy(user->ident,newident,IDENTMAX);
232                         delete ident;
233                 }
234                 return 0;
235         }
236
237         virtual bool OnCheckReady(userrec* user)
238         {
239                 /*
240                  * The socket engine will clean up their ident request for us when it completes,
241                  * either due to timeout or due to closing, so, we just hold them until they dont
242                  * have an ident field any more.
243                  */
244                 RFC1413* ident;
245                 return (!user->GetExt("ident_data", ident));
246         }
247
248         virtual void OnCleanup(int target_type, void* item)
249         {
250                 if (target_type == TYPE_USER)
251                 {
252                         userrec* user = (userrec*)item;
253                         RFC1413* ident;
254                         if (user->GetExt("ident_data", ident))
255                         {
256                                 // FIX: If the user record is deleted, the socket wont be removed
257                                 // immediately so there is chance of the socket trying to write to
258                                 // a user which has now vanished! To prevent this, set ident::u
259                                 // to NULL and check it so that we dont write users who have gone away.
260                                 ident->u = NULL;
261                                 ServerInstance->SE->DelFd(ident);
262                                 delete ident;
263                         }
264                 }
265         }
266
267         virtual void OnUserDisconnect(userrec* user)
268         {
269                 /*
270                  * when the user quits tidy up any ident lookup they have pending to keep things tidy.
271                  * When we call RemoveSocket, the abstractions tied into the system evnetually work their
272                  * way to RFC1459::OnClose(), which shrinks off the ident_data for us, so we dont need
273                  * to do it here. If we don't tidy this up, there may still be lingering idents for users
274                  * who have quit, as class RFC1459 is only loosely bound to userrec* via a pair of pointers
275                  * and this would leave at least one of the invalid ;)
276                  */
277                 RFC1413* ident;
278                 if (user->GetExt("ident_data", ident))
279                 {
280                         ident->u = NULL;
281                         ServerInstance->SE->DelFd(ident);
282                         delete ident;
283                 }
284         }
285         
286         virtual ~ModuleIdent()
287         {
288         }
289         
290         virtual Version GetVersion()
291         {
292                 return Version(1,1,0,0,VF_VENDOR,API_VERSION);
293         }
294         
295 };
296
297 class ModuleIdentFactory : public ModuleFactory
298 {
299  public:
300         ModuleIdentFactory()
301         {
302         }
303         
304         ~ModuleIdentFactory()
305         {
306         }
307         
308         virtual Module * CreateModule(InspIRCd* Me)
309         {
310                 return new ModuleIdent(Me);
311         }
312         
313 };
314
315
316 extern "C" void * init_module( void )
317 {
318         return new ModuleIdentFactory;
319 }
320