]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ident.cpp
IMPORTANT checks in here that special took out thinking they were no longer required...
[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                         return false;
61                 }
62                 
63                 char req[32];
64                 
65                 #ifndef IPV6
66                 snprintf(req, sizeof(req), "%d,%d\r\n", ntohs(raddr.sin_port), ntohs(laddr.sin_port));
67                 #else
68                 snprintf(req, sizeof(req), "%d,%d\r\n", ntohs(raddr.sin6_port), ntohs(laddr.sin6_port));
69                 #endif
70                 
71                 this->Write(req);
72                 
73                 return true;
74         }
75
76         virtual void OnClose()
77         {
78                 /* There used to be a check against the fd table here, to make sure the user hadn't been
79                  * deleted but not yet had their socket closed or something along those lines, dated june
80                  * 2006. Since we added the global cull list and such, I don't *think* that is necessary
81                  * 
82                  * -- YES IT IS!!!! DO NOT REMOVE IT, THIS IS WHAT THE WARNING ABOVE THE OLD CODE SAID :P
83                  */
84                 if (Instance->SE->GetRef(original_fd) == user)
85                 {
86                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
87                         return;
88                 }
89
90                 if (*user->ident == '~' && user->GetExt("ident_socket"))
91                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead", user->ident);
92
93                 user->Shrink("ident_socket");
94                 Instance->next_call = Instance->Time();
95         }
96         
97         virtual void OnError(InspSocketError e)
98         {
99                 if (Instance->SE->GetRef(original_fd) == user)
100                 {
101                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
102                         return;
103                 }
104
105                 // Quick check to make sure that this hasn't been sent ;)
106                 if (*user->ident == '~' && user->GetExt("ident_socket"))
107                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead", user->ident);
108                 
109                 user->Shrink("ident_socket");
110                 Instance->next_call = Instance->Time();
111         }
112         
113         virtual bool OnDataReady()
114         {
115                 if (Instance->SE->GetRef(original_fd) == user)
116                 {
117                         Instance->Log(DEBUG,"Oh dear, our user has gone AWOL on fd %d", original_fd);
118                         return false;
119                 }
120
121                 char *ibuf = this->Read();
122                 if (!ibuf)
123                         return false;
124                 
125                 // We don't really need to buffer for incomplete replies here, since IDENT replies are
126                 // extremely short - there is *no* sane reason it'd be in more than one packet
127                 
128                 irc::sepstream sep(ibuf, ':');
129                 std::string token;
130                 for (int i = 0; sep.GetToken(token); i++)
131                 {
132                         // We only really care about the 4th portion
133                         if (i < 3)
134                                 continue;
135                         
136                         char ident[IDENTMAX + 2];
137                         
138                         // Truncate the ident at any characters we don't like, skip leading spaces
139                         int k = 0;
140                         for (const char *j = token.c_str(); *j && (k < IDENTMAX + 1); j++)
141                         {
142                                 if (*j == ' ')
143                                         continue;
144                                 
145                                 // Rules taken from InspIRCd::IsIdent
146                                 if (((*j >= 'A') && (*j <= '}')) || ((*j >= '0') && (*j <= '9')) || (*j == '-') || (*j == '.'))
147                                 {
148                                         ident[k++] = *j;
149                                         continue;
150                                 }
151                                 
152                                 break;
153                         }
154                         
155                         ident[k] = '\0';
156                         
157                         // Redundant check with IsIdent, in case that changes and this doesn't (paranoia!)
158                         if (*ident && Instance->IsIdent(ident))
159                         {
160                                 strlcpy(user->ident, ident, IDENTMAX);
161                                 user->WriteServ("NOTICE Auth :*** Found your ident: %s", user->ident);
162                                 Instance->next_call = Instance->Time();
163                         }
164                         
165                         break;
166                 }
167                 
168                 return false;
169         }
170 };
171
172 class ModuleIdent : public Module
173 {
174  private:
175         int RequestTimeout;
176  public:
177         ModuleIdent(InspIRCd *Me)
178                 : Module(Me)
179         {
180                 OnRehash(NULL, "");
181         }
182         
183         virtual ~ModuleIdent()
184         {
185         }
186         
187         virtual Version GetVersion()
188         {
189                 return Version(1, 1, 1, 0, VF_VENDOR, API_VERSION);
190         }
191         
192         virtual void Implements(char *List)
193         {
194                 List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnCleanup] = List[I_OnUserDisconnect] = 1;
195         }
196         
197         virtual void OnRehash(userrec *user, const std::string &param)
198         {
199                 ConfigReader MyConf(ServerInstance);
200                 
201                 RequestTimeout = MyConf.ReadInteger("ident", "timeout", 0, true);
202                 if (!RequestTimeout)
203                         RequestTimeout = 5;
204         }
205         
206         virtual int OnUserRegister(userrec *user)
207         {
208                 /* userrec::ident is currently the username field from USER; with m_ident loaded, that
209                  * should be preceded by a ~. The field is actually IDENTMAX+2 characters wide. */
210                 memmove(user->ident + 1, user->ident, IDENTMAX);
211                 user->ident[0] = '~';
212                 // Ensure that it is null terminated
213                 user->ident[IDENTMAX + 1] = '\0';
214                 
215                 user->WriteServ("NOTICE Auth :*** Looking up your ident...");
216                 
217                 // Get the IP that the user is connected to, and bind to that for the outgoing connection
218                 #ifndef IPV6
219                 sockaddr_in laddr;
220                 #else
221                 sockaddr_in6 laddr;
222                 #endif
223                 socklen_t laddrsz = sizeof(laddr);
224                 
225                 if (getsockname(user->GetFd(), (sockaddr*) &laddr, &laddrsz) != 0)
226                 {
227                         user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident);
228                         return 0;
229                 }
230                 
231                 #ifndef IPV6
232                 const char *ip = inet_ntoa(laddr.sin_addr);
233                 #else
234                 char ip[INET6_ADDRSTRLEN + 1];
235                 inet_ntop(laddr.sin6_family, &laddr.sin6_addr, ip, INET6_ADDRSTRLEN);
236                 #endif
237                 
238                 IdentRequestSocket *isock = new IdentRequestSocket(ServerInstance, user, RequestTimeout, ip);
239                 if (isock->GetFd() > -1)
240                         user->Extend("ident_socket", isock);
241                 else
242                         if (ServerInstance->SocketCull.find(isock) == ServerInstance->SocketCull.end())
243                                 ServerInstance->SocketCull[isock] = isock;
244                 return 0;
245         }
246         
247         virtual bool OnCheckReady(userrec *user)
248         {
249                 return (!user->GetExt("ident_socket"));
250         }
251         
252         virtual void OnCleanup(int target_type, void *item)
253         {
254                 if (target_type == TYPE_USER)
255                 {
256                         IdentRequestSocket *isock;
257                         userrec *user = (userrec*)item;
258                         if (user->GetExt("ident_socket", isock))
259                                 isock->Close();
260                 }
261         }
262         
263         virtual void OnUserDisconnect(userrec *user)
264         {
265                 IdentRequestSocket *isock;
266                 if (user->GetExt("ident_socket", isock))
267                         isock->Close();
268         }
269 };
270
271 MODULE_INIT(ModuleIdent);