]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ident.cpp
Lots of bugfixes, added timeouts, completely nonblocking!
[user/henk/code/inspircd.git] / src / modules / m_ident.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 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 #include <stdio.h>
18 #include <string>
19 #include <stdlib.h>
20 #include <time.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <poll.h>
29 #include "users.h"
30 #include "channels.h"
31 #include "modules.h"
32 #include "inspircd.h"
33
34 /* $ModDesc: Provides support for RFC 1413 ident lookups */
35
36 Server *Srv;
37
38 class RFC1413
39 {
40  protected:
41         int fd;
42         userrec* u;
43         sockaddr_in addr;
44         in_addr addy;
45         int state;
46         char ibuf[MAXBUF];
47         sockaddr_in sock_us;
48         sockaddr_in sock_them;
49         socklen_t uslen;
50         socklen_t themlen;
51         int nrecv;
52         time_t timeout_end;
53         bool timeout;
54  public:
55         bool Connect(userrec* user, int maxtime)
56         {
57                 timeout_end = time(NULL)+maxtime;
58                 timeout = false;
59                 if ((this->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
60                 {
61                         Srv->Log(DEBUG,"Ident: socket failed for: "+std::string(user->ip));
62                         return false;
63                 }
64                 inet_aton(user->ip,&addy);
65                 addr.sin_family = AF_INET;
66                 addr.sin_addr = addy;
67                 addr.sin_port = htons(113);
68
69                 int flags;
70                 flags = fcntl(this->fd, F_GETFL, 0);
71                 fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
72
73                 if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)) == -1)
74                 {
75                         if (errno != EINPROGRESS)
76                         {
77                                 Srv->Log(DEBUG,"Ident: connect failed for: "+std::string(user->ip));
78                                 return false;
79                         }
80                 }
81                 Srv->Log(DEBUG,"Ident: successful connect associated with user "+std::string(user->nick));
82                 this->u = user;
83                 this->state = 1;
84                 return true;
85         }
86
87         bool Poll()
88         {
89                 if (time(NULL) > timeout_end)
90                 {
91                         timeout = true;
92                         Srv->SendServ(u->fd,"NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead.");
93                         return false;
94                 }
95                 pollfd polls;
96                 polls.fd = this->fd;
97                 if (state == 1)
98                 {
99                         polls.events = POLLOUT;
100                 }
101                 else
102                 {
103                         polls.events = POLLIN;
104                 }
105                 int ret = poll(&polls,1,1);
106
107                 if (ret > 0)
108                 {
109                         switch (this->state)
110                         {
111                                 case 1:
112                                         Srv->Log(DEBUG,"*** IDENT IN STATE 1");
113                                         uslen = sizeof(sock_us);
114                                         themlen = sizeof(sock_them);
115                                         if ((getsockname(this->u->fd,(sockaddr*)&sock_us,&uslen) || getpeername(this->u->fd, (sockaddr*)&sock_them, &themlen)))
116                                         {
117                                                 Srv->Log(DEBUG,"Ident: failed to get socket names, bailing to state 3");
118                                                 state = 3;
119                                         }
120                                         else
121                                         {
122                                                 // send the request in the following format: theirsocket,oursocket
123                                                 Write(this->fd,"%d,%d",ntohs(sock_them.sin_port),ntohs(sock_us.sin_port));
124                                                 Srv->Log(DEBUG,"Sent ident request, moving to state 2");
125                                                 state = 2;
126                                         }
127                                 break;
128                                 case 2:
129                                         Srv->Log(DEBUG,"*** IDENT IN STATE 2");
130                                         nrecv = recv(this->fd,ibuf,sizeof(ibuf),0);
131                                         if (nrecv > 0)
132                                         {
133                                                 // we have the response line in the following format:
134                                                 // 6193, 23 : USERID : UNIX : stjohns
135                                                 // 6195, 23 : ERROR : NO-USER
136                                                 ibuf[nrecv] = '\0';
137                                                 Srv->Log(DEBUG,"Received ident response: "+std::string(ibuf));
138                                                 close(this->fd);
139                                                 shutdown(this->fd,2);
140                                                 char* savept;
141                                                 char* section = strtok_r(ibuf,":",&savept);
142                                                 while (section)
143                                                 {
144                                                         if (strstr(section,"USERID"))
145                                                         {
146                                                                 section = strtok_r(NULL,":",&savept);
147                                                                 if (section)
148                                                                 {
149                                                                         // ID type, usually UNIX or OTHER... we dont want it, so read the next token
150                                                                         section = strtok_r(NULL,":",&savept);
151                                                                         if (section)
152                                                                         {
153                                                                                 while ((*section == ' ') && (strlen(section)>0)) section++; // strip leading spaces
154                                                                                 if ((section[strlen(section)-1] == 13) || (section[strlen(section)-1] == 10))
155                                                                                         section[strlen(section)-1] = '\0'; // strip carriage returns
156                                                                                 if ((section[strlen(section)-1] == 13) || (section[strlen(section)-1] == 10))
157                                                                                         section[strlen(section)-1] = '\0'; // strip linefeeds
158                                                                                 while ((section[strlen(section)-1] == ' ') && (strlen(section)>0)) // strip trailing spaces
159                                                                                         section[strlen(section)-1] = '\0';
160                                                                                 if (strlen(section))
161                                                                                 {
162                                                                                         strlcpy(u->ident,section,IDENTMAX);
163                                                                                         Srv->Log(DEBUG,"IDENT SET: "+std::string(u->ident));
164                                                                                         Srv->SendServ(u->fd,"NOTICE "+std::string(u->nick)+" :*** Found your ident: "+std::string(u->ident));
165                                                                                 }
166                                                                                 break;
167                                                                         }
168                                                                 }
169                                                         }
170                                                         section = strtok_r(NULL,":",&savept);
171                                                 }
172                                                 state = 3;
173                                         }
174                                 break;
175                                 case 3:
176                                         Srv->Log(DEBUG,"Ident lookup is complete!");
177                                 break;
178                                 default:
179                                         Srv->Log(DEBUG,"Ident: invalid ident state!!!");
180                                 break;
181                         }
182                 }
183         }
184
185         bool Done()
186         {
187                 return ((state == 3) || (timeout == true));
188         }
189 };
190
191 class ModuleIdent : public Module
192 {
193
194         ConfigReader* Conf;
195         int IdentTimeout;
196
197  public:
198         void ReadSettings()
199         {
200                 Conf = new ConfigReader;
201                 IdentTimeout = Conf->ReadInteger("ident","timeout",0,true);
202                 delete Conf;
203         }
204
205         ModuleIdent()
206         {
207                 Srv = new Server;
208                 ReadSettings();
209         }
210
211         virtual void OnRehash()
212         {
213                 ReadSettings();
214         }
215
216         virtual void OnUserRegister(userrec* user)
217         {
218                 RFC1413* ident = new RFC1413;
219                 Srv->SendServ(user->fd,"NOTICE "+std::string(user->nick)+" :*** Looking up your ident...");
220                 if (ident->Connect(user,IdentTimeout))
221                 {
222                         user->Extend("ident_data",(char*)ident);
223                         ident->Poll();
224                 }
225                 else
226                 {
227                         Srv->SendServ(user->fd,"NOTICE "+std::string(user->nick)+" :*** Could not look up your ident.");
228                         delete ident;
229                 }
230         }
231
232         virtual bool OnCheckReady(userrec* user)
233         {
234                 RFC1413* ident = (RFC1413*)user->GetExt("ident_data");
235                 if (ident)
236                 {
237                         ident->Poll();
238                         if (ident->Done())
239                         {
240                                 Srv->Log(DEBUG,"Ident: removing ident gubbins");
241                                 user->Shrink("ident_data");
242                                 delete ident;
243                                 return true;
244                         }
245                         return false;
246                 }
247                 return true;
248         }
249         
250         virtual ~ModuleIdent()
251         {
252                 delete Srv;
253         }
254         
255         virtual Version GetVersion()
256         {
257                 return Version(1,0,0,1,VF_VENDOR);
258         }
259         
260 };
261
262 // stuff down here is the module-factory stuff. For basic modules you can ignore this.
263
264 class ModuleIdentFactory : public ModuleFactory
265 {
266  public:
267         ModuleIdentFactory()
268         {
269         }
270         
271         ~ModuleIdentFactory()
272         {
273         }
274         
275         virtual Module * CreateModule()
276         {
277                 return new ModuleIdent;
278         }
279         
280 };
281
282
283 extern "C" void * init_module( void )
284 {
285         return new ModuleIdentFactory;
286 }
287