]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ident.cpp
And some more tweaks to make sure it frees memory :P
[user/henk/code/inspircd.git] / src / modules / m_ident.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "users.h"
16 #include "channels.h"
17 #include "modules.h"
18
19 /* $ModDesc: Provides support for RFC1413 ident lookups */
20
21 class IdentRequestSocket : public InspSocket
22 {
23  private:
24         userrec *user;
25         int original_fd;
26  public:
27         IdentRequestSocket(InspIRCd *Server, userrec *user, int timeout, const std::string &bindip)
28                 : InspSocket(Server, user->GetIPString(), 113, false, timeout, bindip), user(user)
29         {
30                 original_fd = user->GetFd();
31                 Instance->Log(DEBUG, "Ident request against user with fd %d", original_fd);
32         }
33
34         virtual bool OnConnected()
35         {
36                 if (Instance->SE->GetRef(original_fd) == user)
37                 {
38                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
39                         return false;
40                 }
41
42                 /* Both sockaddr_in and sockaddr_in6 can be safely casted to sockaddr, especially since the
43                  * only members we use are in a part of the struct that should always be identical (at the
44                  * byte level). */
45
46                 Instance->Log(DEBUG, "Sending ident request to %s", user->GetIPString());
47                 
48                 #ifndef IPV6
49                 sockaddr_in laddr, raddr;
50                 #else
51                 sockaddr_in6 laddr, raddr;
52                 #endif
53                 
54                 socklen_t laddrsz = sizeof(laddr);
55                 socklen_t raddrsz = sizeof(raddr);
56                 
57                 if ((getsockname(user->GetFd(), (sockaddr*) &laddr, &laddrsz) != 0) || (getpeername(user->GetFd(), (sockaddr*) &raddr, &raddrsz) != 0))
58                 {
59                         // Error
60                         TidyUser();
61                         return false;
62                 }
63                 
64                 char req[32];
65                 
66                 #ifndef IPV6
67                 snprintf(req, sizeof(req), "%d,%d\r\n", ntohs(raddr.sin_port), ntohs(laddr.sin_port));
68                 #else
69                 snprintf(req, sizeof(req), "%d,%d\r\n", ntohs(raddr.sin6_port), ntohs(laddr.sin6_port));
70                 #endif
71                 
72                 this->Write(req);
73                 
74                 return true;
75         }
76
77         virtual void OnClose()
78         {
79                 /* There used to be a check against the fd table here, to make sure the user hadn't been
80                  * deleted but not yet had their socket closed or something along those lines, dated june
81                  * 2006. Since we added the global cull list and such, I don't *think* that is necessary
82                  * 
83                  * -- YES IT IS!!!! DO NOT REMOVE IT, THIS IS WHAT THE WARNING ABOVE THE OLD CODE SAID :P
84                  */
85                 if (Instance->SE->GetRef(original_fd) == user)
86                 {
87                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
88                         return;
89                 }
90
91                 if (*user->ident == '~' && user->GetExt("ident_socket"))
92                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead", user->ident);
93
94                 TidyUser();
95                 Instance->next_call = Instance->Time();
96         }
97         
98         virtual void OnError(InspSocketError e)
99         {
100                 if (Instance->SE->GetRef(original_fd) == user)
101                 {
102                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
103                         return;
104                 }
105
106                 // Quick check to make sure that this hasn't been sent ;)
107                 if (*user->ident == '~' && user->GetExt("ident_socket"))
108                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead", user->ident);
109                 
110                 TidyUser();
111                 Instance->next_call = Instance->Time();
112         }
113         
114         virtual bool OnDataReady()
115         {
116                 if (Instance->SE->GetRef(original_fd) == user)
117                 {
118                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
119                         return false;
120                 }
121
122                 char *ibuf = this->Read();
123                 if (!ibuf)
124                 {
125                         TidyUser();
126                         return false;
127                 }
128                 
129                 // We don't really need to buffer for incomplete replies here, since IDENT replies are
130                 // extremely short - there is *no* sane reason it'd be in more than one packet
131                 
132                 irc::sepstream sep(ibuf, ':');
133                 std::string token;
134                 for (int i = 0; sep.GetToken(token); i++)
135                 {
136                         // We only really care about the 4th portion
137                         if (i < 3)
138                                 continue;
139                         
140                         char ident[IDENTMAX + 2];
141                         
142                         // Truncate the ident at any characters we don't like, skip leading spaces
143                         int k = 0;
144                         for (const char *j = token.c_str(); *j && (k < IDENTMAX + 1); j++)
145                         {
146                                 if (*j == ' ')
147                                         continue;
148                                 
149                                 // Rules taken from InspIRCd::IsIdent
150                                 if (((*j >= 'A') && (*j <= '}')) || ((*j >= '0') && (*j <= '9')) || (*j == '-') || (*j == '.'))
151                                 {
152                                         ident[k++] = *j;
153                                         continue;
154                                 }
155                                 
156                                 break;
157                         }
158                         
159                         ident[k] = '\0';
160                         
161                         // Redundant check with IsIdent, in case that changes and this doesn't (paranoia!)
162                         if (*ident && Instance->IsIdent(ident))
163                         {
164                                 strlcpy(user->ident, ident, IDENTMAX);
165                                 user->WriteServ("NOTICE Auth :*** Found your ident: %s", user->ident);
166                                 Instance->next_call = Instance->Time();
167                         }
168                         
169                         break;
170                 }
171
172                 TidyUser();
173                 return false;
174         }
175
176         void TidyUser()
177         {
178                 user->Shrink("ident_socket");
179                 int* delfd;
180                 if (user->GetExt("ident_socket_fd", delfd))
181                 {
182                         delete delfd;
183                         user->Shrink("ident_socket_fd");
184                 }
185         }
186 };
187
188 class ModuleIdent : public Module
189 {
190  private:
191         int RequestTimeout;
192  public:
193         ModuleIdent(InspIRCd *Me)
194                 : Module(Me)
195         {
196                 OnRehash(NULL, "");
197         }
198         
199         virtual ~ModuleIdent()
200         {
201         }
202         
203         virtual Version GetVersion()
204         {
205                 return Version(1, 1, 1, 0, VF_VENDOR, API_VERSION);
206         }
207         
208         virtual void Implements(char *List)
209         {
210                 List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnCleanup] = List[I_OnUserDisconnect] = 1;
211         }
212         
213         virtual void OnRehash(userrec *user, const std::string &param)
214         {
215                 ConfigReader MyConf(ServerInstance);
216                 
217                 RequestTimeout = MyConf.ReadInteger("ident", "timeout", 0, true);
218                 if (!RequestTimeout)
219                         RequestTimeout = 5;
220         }
221         
222         virtual int OnUserRegister(userrec *user)
223         {
224                 /* userrec::ident is currently the username field from USER; with m_ident loaded, that
225                  * should be preceded by a ~. The field is actually IDENTMAX+2 characters wide. */
226                 memmove(user->ident + 1, user->ident, IDENTMAX);
227                 user->ident[0] = '~';
228                 // Ensure that it is null terminated
229                 user->ident[IDENTMAX + 1] = '\0';
230                 
231                 user->WriteServ("NOTICE Auth :*** Looking up your ident...");
232                 
233                 // Get the IP that the user is connected to, and bind to that for the outgoing connection
234                 #ifndef IPV6
235                 sockaddr_in laddr;
236                 #else
237                 sockaddr_in6 laddr;
238                 #endif
239                 socklen_t laddrsz = sizeof(laddr);
240                 
241                 if (getsockname(user->GetFd(), (sockaddr*) &laddr, &laddrsz) != 0)
242                 {
243                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident);
244                         return 0;
245                 }
246                 
247                 #ifndef IPV6
248                 const char *ip = inet_ntoa(laddr.sin_addr);
249                 #else
250                 char ip[INET6_ADDRSTRLEN + 1];
251                 inet_ntop(laddr.sin6_family, &laddr.sin6_addr, ip, INET6_ADDRSTRLEN);
252                 #endif
253                 
254                 IdentRequestSocket *isock = new IdentRequestSocket(ServerInstance, user, RequestTimeout, ip);
255                 if (isock->GetFd() > -1)
256                 {
257                         user->Extend("ident_socket_fd", new int(isock->GetFd()));
258                         user->Extend("ident_socket", isock);
259                 }
260                 else
261                         if (ServerInstance->SocketCull.find(isock) == ServerInstance->SocketCull.end())
262                                 ServerInstance->SocketCull[isock] = isock;
263                 return 0;
264         }
265         
266         virtual bool OnCheckReady(userrec *user)
267         {
268                 return (!user->GetExt("ident_socket"));
269         }
270         
271         virtual void OnCleanup(int target_type, void *item)
272         {
273                 if (target_type == TYPE_USER)
274                 {
275                         IdentRequestSocket *isock;
276                         userrec *user = (userrec*)item;
277                         if (user->GetExt("ident_socket", isock))
278                         {
279                                 int *fd;
280                                 if (user->GetExt("ident_socket_fd", fd) && (ServerInstance->SE->GetRef(*fd) == isock))
281                                 {
282                                         user->Shrink("ident_socket_fd");
283                                         delete fd;
284                                         isock->Close();
285                                 }
286                         }
287                 }
288         }
289         
290         virtual void OnUserDisconnect(userrec *user)
291         {
292                 IdentRequestSocket *isock;
293                 if (user->GetExt("ident_socket", isock))
294                 {
295                         int *fd;
296                         if (user->GetExt("ident_socket_fd", fd) && (ServerInstance->SE->GetRef(*fd) == isock))
297                         {
298                                 user->Shrink("ident_socket_fd");
299                                 delete fd;
300                                 isock->Close();
301                         }
302                 }
303         }
304 };
305
306 MODULE_INIT(ModuleIdent);
307