]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_ident.cpp
Converted more stuff to 'Implements' system
[user/henk/code/inspircd.git] / src / modules / m_ident.cpp
index cc25a484c5ce80a6219913225e99b8b1fad4f26d..c55c514998c5f3f1fd1c3ba467303f832a0ec212 100644 (file)
@@ -14,6 +14,8 @@
  * ---------------------------------------------------
  */
 
+using namespace std;
+
 #include <stdio.h>
 #include <string>
 #include <stdlib.h>
 
 Server *Srv;
 
+// State engine constants. We have three states,
+// connecting, waiting for data, and finished.
+
+#define IDENT_STATE_CONNECT    1
+#define IDENT_STATE_WAITDATA   2
+#define IDENT_STATE_DONE       3
+
+// Ident lookups are done by attaching an RFC1413 class to the
+// userrec record using the Extensible system.
+// The RFC1413 class is written especially for this module but
+// it should be relatively standalone for anyone else who wishes
+// to have a nonblocking ident lookup in a program :)
+// the class operates on a simple state engine, each state of the
+// connection incrementing a state counter, leading through to
+// a concluding state which terminates the lookup.
+
 class RFC1413
 {
  protected:
-       int fd;
-       userrec* u;
-       sockaddr_in addr;
-       in_addr addy;
-       int state;
-       char ibuf[MAXBUF];
-       sockaddr_in sock_us;
-       sockaddr_in sock_them;
-       socklen_t uslen;
-       socklen_t themlen;
-       int nrecv;
-       time_t timeout_end;
-       bool timeout;
+       int fd;                  // file descriptor
+       userrec* u;              // user record that the lookup is associated with
+       sockaddr_in addr;        // address we're connecting to
+       in_addr addy;            // binary ip address
+       int state;               // state (this class operates on a state engine)
+       char ibuf[MAXBUF];       // input buffer
+       sockaddr_in sock_us;     // our port number
+       sockaddr_in sock_them;   // their port number
+       socklen_t uslen;         // length of our port number
+       socklen_t themlen;       // length of their port number
+       int nrecv;               // how many bytes we've received
+       time_t timeout_end;      // how long until the operation times out
+       bool timeout;            // true if we've timed out and should bail
+       char ident_request[128]; // buffer used to make up the request string
  public:
+
+       // The destructor makes damn sure the socket is freed :)
+
+       ~RFC1413()
+       {
+               if (this->fd != -1)
+               {
+                       shutdown(this->fd,2);
+                       close(this->fd);
+                       this->fd = -1;
+               }
+       }
+
+       // establish an ident connection, maxtime is the time to spend trying
+       // returns true if successful, false if something was catastrophically wrong.
+       // note that failed connects are not reported here but detected in RFC1413::Poll()
+       // as the socket is nonblocking
+
        bool Connect(userrec* user, int maxtime)
        {
                timeout_end = time(NULL)+maxtime;
                timeout = false;
                if ((this->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
                {
+                       // theres been a boo-boo... no more fd's left for us, woe is me!
                        Srv->Log(DEBUG,"Ident: socket failed for: "+std::string(user->ip));
                        return false;
                }
@@ -72,34 +110,49 @@ class RFC1413
 
                if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)) == -1)
                {
+                       // theres been an error, but EINPROGRESS just means 'right, im on it, call me later'
                        if (errno != EINPROGRESS)
                        {
+                               // ... so that error isnt fatal, like the rest.
                                Srv->Log(DEBUG,"Ident: connect failed for: "+std::string(user->ip));
+                               shutdown(this->fd,2);
+                                close(this->fd);
+                               this->fd = -1;
                                return false;
                        }
                 }
                Srv->Log(DEBUG,"Ident: successful connect associated with user "+std::string(user->nick));
                this->u = user;
-               this->state = 1;
+               this->state = IDENT_STATE_CONNECT;
                return true;
        }
 
+       // Poll the socket to see if we have an ident result, and if we do apply it to the user.
+       // returns false if we cannot poll for some reason (e.g. timeout).
+
        bool Poll()
        {
                if (time(NULL) > timeout_end)
                {
                        timeout = true;
                        Srv->SendServ(u->fd,"NOTICE "+std::string(u->nick)+" :*** Could not find your ident, using "+std::string(u->ident)+" instead.");
+                       shutdown(this->fd,2);
+                       close(this->fd);
+                       this->fd = -1;
                        return false;
                }
                pollfd polls;
                polls.fd = this->fd;
-               if (state == 1)
+               if (state == IDENT_STATE_CONNECT)
                {
+                       // during state IDENT_STATE_CONNECT (leading up to the connect)
+                       // we're watching for writeability
                        polls.events = POLLOUT;
                }
                else
                {
+                       // the rest of the time we're waiting for data
+                       // back on the socket, or a socket close
                        polls.events = POLLIN;
                }
                int ret = poll(&polls,1,1);
@@ -108,25 +161,27 @@ class RFC1413
                {
                        switch (this->state)
                        {
-                               case 1:
-                                       Srv->Log(DEBUG,"*** IDENT IN STATE 1");
+                               case IDENT_STATE_CONNECT:
                                        uslen = sizeof(sock_us);
                                        themlen = sizeof(sock_them);
                                        if ((getsockname(this->u->fd,(sockaddr*)&sock_us,&uslen) || getpeername(this->u->fd, (sockaddr*)&sock_them, &themlen)))
                                        {
                                                Srv->Log(DEBUG,"Ident: failed to get socket names, bailing to state 3");
-                                               state = 3;
+                                               shutdown(this->fd,2);
+                                                close(this->fd);
+                                               this->fd = -1;
+                                               state = IDENT_STATE_DONE;
                                        }
                                        else
                                        {
                                                // send the request in the following format: theirsocket,oursocket
-                                               Write(this->fd,"%d,%d",ntohs(sock_them.sin_port),ntohs(sock_us.sin_port));
+                                               snprintf(ident_request,127,"%d,%d\r\n",ntohs(sock_them.sin_port),ntohs(sock_us.sin_port));
+                                               send(this->fd,ident_request,strlen(ident_request),0);
                                                Srv->Log(DEBUG,"Sent ident request, moving to state 2");
-                                               state = 2;
+                                               state = IDENT_STATE_WAITDATA;
                                        }
                                break;
-                               case 2:
-                                       Srv->Log(DEBUG,"*** IDENT IN STATE 2");
+                               case IDENT_STATE_WAITDATA:
                                        nrecv = recv(this->fd,ibuf,sizeof(ibuf),0);
                                        if (nrecv > 0)
                                        {
@@ -135,8 +190,9 @@ class RFC1413
                                                // 6195, 23 : ERROR : NO-USER
                                                ibuf[nrecv] = '\0';
                                                Srv->Log(DEBUG,"Received ident response: "+std::string(ibuf));
-                                               close(this->fd);
                                                shutdown(this->fd,2);
+                                               close(this->fd);
+                                               this->fd = -1;
                                                char* savept;
                                                char* section = strtok_r(ibuf,":",&savept);
                                                while (section)
@@ -151,12 +207,10 @@ class RFC1413
                                                                        if (section)
                                                                        {
                                                                                while ((*section == ' ') && (strlen(section)>0)) section++; // strip leading spaces
-                                                                               if ((section[strlen(section)-1] == 13) || (section[strlen(section)-1] == 10))
-                                                                                       section[strlen(section)-1] = '\0'; // strip carriage returns
-                                                                               if ((section[strlen(section)-1] == 13) || (section[strlen(section)-1] == 10))
-                                                                                       section[strlen(section)-1] = '\0'; // strip linefeeds
-                                                                               while ((section[strlen(section)-1] == ' ') && (strlen(section)>0)) // strip trailing spaces
-                                                                                       section[strlen(section)-1] = '\0';
+                                                                               int t = strlen(section);
+                                                                               for (int j = 0; j < t; j++)
+                                                                                       if ((section[j] < 33) || (section[j]>126))
+                                                                                               section[j] = '\0'; // truncate at invalid chars
                                                                                if (strlen(section))
                                                                                {
                                                                                        strlcpy(u->ident,section,IDENTMAX);
@@ -169,10 +223,13 @@ class RFC1413
                                                        }
                                                        section = strtok_r(NULL,":",&savept);
                                                }
-                                               state = 3;
+                                               state = IDENT_STATE_DONE;
                                        }
                                break;
-                               case 3:
+                               case IDENT_STATE_DONE:
+                                       shutdown(this->fd,2);
+                                       close(this->fd);
+                                       this->fd = -1;
                                        Srv->Log(DEBUG,"Ident lookup is complete!");
                                break;
                                default:
@@ -180,11 +237,15 @@ class RFC1413
                                break;
                        }
                }
+               return true;
        }
 
+       // returns true if the operation is completed,
+       // either due to complete request, or a timeout
+
        bool Done()
        {
-               return ((state == 3) || (timeout == true));
+               return ((state == IDENT_STATE_DONE) || (timeout == true));
        }
 };
 
@@ -202,28 +263,36 @@ class ModuleIdent : public Module
                delete Conf;
        }
 
-       ModuleIdent()
+       ModuleIdent(Server* Me)
+               : Module::Module(Me)
        {
-               Srv = new Server;
+               Srv = Me;
                ReadSettings();
        }
 
-       virtual void OnRehash()
+       virtual void OnRehash(std::string parameter)
        {
                ReadSettings();
        }
 
        virtual void OnUserRegister(userrec* user)
        {
+               // when the new user connects, before they authenticate with USER/NICK/PASS, we do
+               // their ident lookup.
+
                RFC1413* ident = new RFC1413;
                Srv->SendServ(user->fd,"NOTICE "+std::string(user->nick)+" :*** Looking up your ident...");
                if (ident->Connect(user,IdentTimeout))
                {
+                       // attach the object to the user record
                        user->Extend("ident_data",(char*)ident);
+                       // start it off polling (always good to have a head start)
+                       // because usually connect has completed by now
                        ident->Poll();
                }
                else
                {
+                       // something went wrong, call an irc-ambulance!
                        Srv->SendServ(user->fd,"NOTICE "+std::string(user->nick)+" :*** Could not look up your ident.");
                        delete ident;
                }
@@ -234,22 +303,38 @@ class ModuleIdent : public Module
                RFC1413* ident = (RFC1413*)user->GetExt("ident_data");
                if (ident)
                {
+                       // this user has a pending ident lookup, poll it
                        ident->Poll();
+                       // is it done?
                        if (ident->Done())
                        {
+                               // their ident is done, zap the structures
                                Srv->Log(DEBUG,"Ident: removing ident gubbins");
                                user->Shrink("ident_data");
                                delete ident;
+                               // ...and send them on their way
                                return true;
                        }
+                       // nope, we hold them in this state, they dont go anywhere
                        return false;
                }
                return true;
        }
+
+        virtual void OnUserDisconnect(userrec* user)
+        {
+                // when the user quits tidy up any ident lookup they have pending to keep things tidy
+                // and to prevent a memory and FD leaks
+               RFC1413* ident = (RFC1413*)user->GetExt("ident_data");
+                if (ident)
+                {
+                        delete ident;
+                        user->Shrink("ident_data");
+                }
+        }
        
        virtual ~ModuleIdent()
        {
-               delete Srv;
        }
        
        virtual Version GetVersion()
@@ -259,8 +344,6 @@ class ModuleIdent : public Module
        
 };
 
-// stuff down here is the module-factory stuff. For basic modules you can ignore this.
-
 class ModuleIdentFactory : public ModuleFactory
 {
  public:
@@ -272,9 +355,9 @@ class ModuleIdentFactory : public ModuleFactory
        {
        }
        
-       virtual Module * CreateModule()
+       virtual Module * CreateModule(Server* Me)
        {
-               return new ModuleIdent;
+               return new ModuleIdent(Me);
        }
        
 };