]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/connection.cpp
Updated to keep lowermap const within hashcomp.cpp
[user/henk/code/inspircd.git] / src / connection.cpp
index ad9133031ce3e6ff71e1984b5d5a135deef958f8..384f626e2f2f59f0cbefa57ec3fa6d9bc5d29897 100644 (file)
@@ -1,29 +1,84 @@
+/*       +------------------------------------+
+ *       | Inspire Internet Relay Chat Daemon |
+ *       +------------------------------------+
+ *
+ *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
+ *                       E-mail:
+ *                <brain@chatspike.net>
+ *               <Craig@chatspike.net>
+ *     
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ *            the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
 #include <connection.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <poll.h>
 #include <sys/errno.h>
 #include <sys/ioctl.h>
 #include <sys/utsname.h>
 #include <vector>
 #include <string>
 #include <deque>
+#include <sstream>
 #include "inspircd.h"
 #include "modules.h"
+#include "inspstring.h"
+#include "helperfuncs.h"
 
 using namespace std;
 
+
 extern std::vector<Module*> modules;
 extern std::vector<ircd_module*> factory;
 
+std::deque<std::string> xsums;
+
 extern int MODCOUNT;
 
+extern time_t TIME;
+
+
+/**
+ * The InspIRCd mesh network is maintained by a tree of objects which reference *themselves*.
+ * Every local server has an array of 32 *serverrecs, known as me[]. Each of these represents
+ * a local listening port, and is not null if the user has opened a listening port on the server.
+ * It is assumed nobody will ever want to open more than 32 listening server ports at any one
+ * time (i mean come on, why would you want more, the ircd works fine with ONE).
+ * Each me[] entry has multiple classes within it of type ircd_connector. These are stored in a vector
+ * and each represents a server linked via this socket. If the connection was created outbound,
+ * the connection is bound to the default ip address by using me[defaultRoute] (defaultRoute being
+ * a global variable which indicates the default server to make connections on). If the connection
+ * was created inbound, it is attached to the port the connection came in on. There may be as many
+ * ircd_connector objects as needed in each me[] entry. Each ircd_connector implements the specifics
+ * of an ircd connection in the mesh, however each ircd may have multiple ircd_connector connections
+ * to it, to maintain the mesh link.
+ */
+
+char* xsumtable = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+// creates a random id for a line for detection of duplicate messages
+std::string CreateSum()
+{
+       char sum[9];
+       sum[0] = ':';
+       sum[8] = '\0';
+       for(int q = 1; q < 8; q++)
+               sum[q] = xsumtable[rand()%52];
+       return sum;
+}
+
 connection::connection()
 {
        fd = 0;
 }
 
 
-bool connection::CreateListener(char* host, int p)
+bool connection::CreateListener(char* newhost, int p)
 {
        sockaddr_in host_address;
        int flags;
@@ -39,17 +94,29 @@ bool connection::CreateListener(char* host, int p)
                return false;
        }
 
+       setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
+       linger.l_onoff = 1;
+       linger.l_linger = 1;
+       setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
+       
+       // attempt to increase socket sendq and recvq as high as its possible
+       // to get them on linux.
+       int sendbuf = 32768;
+       int recvbuf = 32768;
+       setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
+       setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
+
        memset((void*)&host_address, 0, sizeof(host_address));
 
        host_address.sin_family = AF_INET;
 
-       if (!strcmp(host,""))
+       if (!strcmp(newhost,""))
        {
                host_address.sin_addr.s_addr = htonl(INADDR_ANY);
        }
        else
        {
-               inet_aton(host,&addy);
+               inet_aton(newhost,&addy);
                host_address.sin_addr = addy;
        }
 
@@ -66,18 +133,6 @@ bool connection::CreateListener(char* host, int p)
 
        this->port = p;
 
-       setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
-       linger.l_onoff = 1;
-       linger.l_linger = 0;
-       setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
-       
-       // attempt to increase socket sendq and recvq as high as its possible
-       // to get them on linux.
-       int sendbuf = 32768;
-       int recvbuf = 32768;
-       setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
-       setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
-       
        listen(this->fd,5);
 
        return true;
@@ -93,17 +148,17 @@ int ircd_connector::GetServerPort()
        return this->port;
 }
 
-bool ircd_connector::SetHostAndPort(char* host, int port)
+bool ircd_connector::SetHostAndPort(char* newhost, int newport)
 {
-       strncpy(this->host,host,160);
-       this->port = port;
+       strncpy(this->host,newhost,160);
+       this->port = newport;
        return true;
 }
 
-bool ircd_connector::SetHostAddress(char* host, int port)
+bool ircd_connector::SetHostAddress(char* newhost, int newport)
 {
-       strncpy(this->host,host,160);
-       this->port = port;
+       strncpy(this->host,newhost,160);
+       this->port = newport;
        memset((void*)&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        inet_aton(host,&addr.sin_addr);
@@ -116,22 +171,68 @@ void ircd_connector::SetServerPort(int p)
        this->port = p;
 }
 
-bool ircd_connector::MakeOutboundConnection(char* host, int port)
+void ircd_connector::AddBuffer(std::string a)
+{
+       std::string b = "";
+       for (int i = 0; i < a.length(); i++)
+               if (a[i] != '\r')
+                       b = b + a[i];
+
+       std::stringstream stream(ircdbuffer);
+       stream << b;
+       log(DEBUG,"AddBuffer: %s",b.c_str());
+       ircdbuffer = stream.str();
+}
+
+bool ircd_connector::BufferIsComplete()
+{
+       for (int i = 0; i < ircdbuffer.length(); i++)
+               if (ircdbuffer[i] == '\n')
+                       return true;
+       return false;
+}
+
+void ircd_connector::ClearBuffer()
+{
+       ircdbuffer = "";
+}
+
+std::string ircd_connector::GetBuffer()
+{
+       // Fix by Brain 28th Apr 2005
+       // seems my stringstream code isnt liked by linux
+       // EVEN THOUGH IT IS CORRECT! Fixed by using a different
+       // (SLOWER) algorithm...
+        char* line = (char*)ircdbuffer.c_str();
+        std::string ret = "";
+        while ((*line != '\n') && (strlen(line)))
+        {
+                ret = ret + *line;
+                line++;
+        }
+        if ((*line == '\n') || (*line == '\r'))
+                line++;
+        ircdbuffer = line;
+        return ret;
+}
+
+bool ircd_connector::MakeOutboundConnection(char* newhost, int newport)
 {
-       log(DEBUG,"MakeOutboundConnection: Original param: %s",host);
-       hostent* hoste = gethostbyname(host);
+       log(DEBUG,"MakeOutboundConnection: Original param: %s",newhost);
+       ClearBuffer();
+       hostent* hoste = gethostbyname(newhost);
        if (!hoste)
        {
-               log(DEBUG,"MakeOutboundConnection: gethostbyname was NULL, setting %s",host);
-               this->SetHostAddress(host,port);
-               SetHostAndPort(host,port);
+               log(DEBUG,"MakeOutboundConnection: gethostbyname was NULL, setting %s",newhost);
+               this->SetHostAddress(newhost,newport);
+               SetHostAndPort(newhost,newport);
        }
        else
        {
                struct in_addr* ia = (in_addr*)hoste->h_addr;
                log(DEBUG,"MakeOutboundConnection: gethostbyname was valid, setting %s",inet_ntoa(*ia));
-               this->SetHostAddress(inet_ntoa(*ia),port);
-               SetHostAndPort(inet_ntoa(*ia),port);
+               this->SetHostAddress(inet_ntoa(*ia),newport);
+               SetHostAndPort(inet_ntoa(*ia),newport);
        }
 
        this->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -140,6 +241,7 @@ bool ircd_connector::MakeOutboundConnection(char* host, int port)
                if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)))
                {
                        WriteOpers("connect() failed for %s",host);
+                       RemoveServer(this->servername.c_str());
                        return false;
                }
                int flags = fcntl(this->fd, F_GETFL, 0);
@@ -153,58 +255,79 @@ bool ircd_connector::MakeOutboundConnection(char* host, int port)
        else
        {
                WriteOpers("socket() failed!");
+               RemoveServer(this->servername.c_str());
        }
 
        return false;
 }
 
 
-bool connection::BeginLink(char* targethost, int port, char* password, char* servername, int myport)
+bool connection::BeginLink(char* targethost, int newport, char* password, char* servername, int myport)
 {
        char connect[MAXBUF];
        
        ircd_connector connector;
+       ircd_connector *cn = this->FindHost(servername);
+
+
+       if (cn)
+       {
+               WriteOpers("CONNECT aborted: Server %s already exists",servername);
+               return false;
+       }
+
        
        if (this->fd)
        {
-               if (connector.MakeOutboundConnection(targethost,port))
+               if (connector.MakeOutboundConnection(targethost,newport))
                {
                        // targethost has been turned into an ip...
                        // we dont want this as the server name.
                        connector.SetServerName(servername);
-                       sprintf(connect,"S %s %s %d %d :%s",getservername().c_str(),password,myport,GetRevision(),getserverdesc().c_str());
+                       snprintf(connect,MAXBUF,"S %s %s %lu %lu :%s",getservername().c_str(),password,(unsigned long)myport,(unsigned long)GetRevision(),getserverdesc().c_str());
                        connector.SetState(STATE_NOAUTH_OUTBOUND);
-                       connector.SetHostAndPort(targethost, port);
+                       connector.SetHostAndPort(targethost, newport);
                        this->connectors.push_back(connector);
                        return this->SendPacket(connect, servername);
                }
                else
                {
                        connector.SetState(STATE_DISCONNECTED);
-                       WriteOpers("Could not create outbound connection to %s:%d",targethost,port);
+                       WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
                }
        }
        return false;
 }
 
-bool connection::MeshCookie(char* targethost, int port, long cookie, char* servername)
+void ircd_connector::SetVersionString(std::string newversion)
+{
+       log(DEBUG,"Set version of %s to %s",this->servername.c_str(),newversion.c_str());
+       this->version = newversion;
+}
+
+std::string ircd_connector::GetVersionString()
+{
+       return this->version;
+}
+
+bool connection::MeshCookie(char* targethost, int newport, unsigned long cookie, char* servername)
 {
        char connect[MAXBUF];
        
        ircd_connector connector;
        
-       WriteOpers("Establishing meshed link to %s:%d",servername,port);
+       WriteOpers("Establishing meshed link to %s:%d",servername,newport);
 
        if (this->fd)
        {
-               if (connector.MakeOutboundConnection(targethost,port))
+               if (connector.MakeOutboundConnection(targethost,newport))
                {
                        // targethost has been turned into an ip...
                        // we dont want this as the server name.
                        connector.SetServerName(servername);
-                       sprintf(connect,"- %d %s :%s",cookie,getservername().c_str(),getserverdesc().c_str());
+                       snprintf(connect,MAXBUF,"- %lu %s :%s",cookie,getservername().c_str(),getserverdesc().c_str());
                        connector.SetState(STATE_NOAUTH_OUTBOUND);
-                       connector.SetHostAndPort(targethost, port);
+                       connector.SetHostAndPort(targethost, newport);
                        connector.SetState(STATE_CONNECTED);
                        this->connectors.push_back(connector);
                        return this->SendPacket(connect, servername);
@@ -212,29 +335,27 @@ bool connection::MeshCookie(char* targethost, int port, long cookie, char* serve
                else
                {
                        connector.SetState(STATE_DISCONNECTED);
-                       WriteOpers("Could not create outbound connection to %s:%d",targethost,port);
+                       WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
                }
        }
        return false;
 }
 
-bool connection::AddIncoming(int fd, char* targethost, int sourceport)
+bool connection::AddIncoming(int newfd, char* targethost, int sourceport)
 {
-       char connect[MAXBUF];
-       
        ircd_connector connector;
        
        // targethost has been turned into an ip...
        // we dont want this as the server name.
        connector.SetServerName(targethost);
-       connector.SetDescriptor(fd);
+       connector.SetDescriptor(newfd);
        connector.SetState(STATE_NOAUTH_INBOUND);
-       int flags = fcntl(fd, F_GETFL, 0);
-       fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+       int flags = fcntl(newfd, F_GETFL, 0);
+       fcntl(newfd, F_SETFL, flags | O_NONBLOCK);
        int sendbuf = 32768;
        int recvbuf = 32768;
-       setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
-       setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
+       setsockopt(newfd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
+       setsockopt(newfd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
        connector.SetHostAndPort(targethost, sourceport);
        connector.SetState(STATE_NOAUTH_INBOUND);
        log(DEBUG,"connection::AddIncoming() Added connection: %s:%d",targethost,sourceport);
@@ -251,11 +372,11 @@ void connection::TerminateLink(char* targethost)
 
 
 // Returns a pointer to the connector for 'host'
-ircd_connector* connection::FindHost(std::string host)
+ircd_connector* connection::FindHost(std::string findhost)
 {
        for (int i = 0; i < this->connectors.size(); i++)
        {
-               if (this->connectors[i].GetServerName() == host)
+               if (this->connectors[i].GetServerName() == findhost)
                {
                        return &this->connectors[i];
                }
@@ -295,9 +416,9 @@ int ircd_connector::GetState()
 }
 
 
-void ircd_connector::SetState(int state)
+void ircd_connector::SetState(int newstate)
 {
-       this->state = state;
+       this->state = newstate;
        if (state == STATE_DISCONNECTED)
        {
                NetSendMyRoutingTable();
@@ -313,18 +434,21 @@ void ircd_connector::CloseConnection()
        fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
 }
 
-void ircd_connector::SetDescriptor(int fd)
+void ircd_connector::SetDescriptor(int newfd)
 {
-       this->fd = fd;
+       this->fd = newfd;
 }
 
-bool connection::SendPacket(char *message, const char* host)
+bool connection::SendPacket(char *message, const char* sendhost)
 {
-       ircd_connector* cn = this->FindHost(host);
+       if ((!message) || (!sendhost))
+               return true;
+
+       ircd_connector* cn = this->FindHost(sendhost);
        
        if (!strchr(message,'\n'))
        {
-               strncat(message,"\n",MAXBUF);
+               strlcat(message,"\n",MAXBUF);
        }
 
        if (cn)
@@ -333,28 +457,35 @@ bool connection::SendPacket(char *message, const char* host)
                
                if (cn->GetState() == STATE_DISCONNECTED)
                {
-                       log(DEBUG,"Main route to %s is down, seeking alternative",host);
-                       // this route is down, we must re-route the packet through an available point in the mesh.
-                       for (int k = 0; k < this->connectors.size(); k++)
+                       log(DEBUG,"\n\n\n\nMain route to %s is down, seeking alternative\n\n\n\n",sendhost);
+                       // fix: can only route one hop to avoid a loop
+                       if (strncmp(message,"R ",2))
                        {
-                               // search for another point in the mesh which can 'reach' where we want to go
-                               for (int m = 0; m < this->connectors[k].routes.size(); m++)
+                               log(DEBUG,"Not a double reroute");
+                               // this route is down, we must re-route the packet through an available point in the mesh.
+                               for (int k = 0; k < this->connectors.size(); k++)
                                {
-                                       if (!strcasecmp(this->connectors[k].routes[m].c_str(),host))
+                                       log(DEBUG,"Check connector %d: %s",k,this->connectors[k].GetServerName().c_str());
+                                       // search for another point in the mesh which can 'reach' where we want to go
+                                       for (int m = 0; m < this->connectors[k].routes.size(); m++)
                                        {
-                                               log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
-                                               char buffer[MAXBUF];
-                                               snprintf(buffer,MAXBUF,"R %s %s",host,message);
-                                               this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
-                                               return true;
+                                               log(DEBUG,"Check connector %d: %s route %s",k,this->connectors[k].GetServerName().c_str(),this->connectors[k].routes[m].c_str());
+                                               if (!strcasecmp(this->connectors[k].routes[m].c_str(),sendhost))
+                                               {
+                                                       log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
+                                                       char buffer[MAXBUF];
+                                                       snprintf(buffer,MAXBUF,"R %s %s",sendhost,message);
+                                                       this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
+                                                       return true;
+                                               }
                                        }
                                }
                        }
                        char buffer[MAXBUF];
-                       snprintf(buffer,MAXBUF,"& %s",host);
-                       NetSendToAllExcept(host,buffer);
-                       log(DEBUG,"There are no routes to %s, we're gonna boot the server off!",host);
-                       DoSplit(host);
+                       snprintf(buffer,MAXBUF,"& %s",sendhost);
+                       NetSendToAllExcept(sendhost,buffer);
+                       log(DEBUG,"\n\nThere are no routes to %s, we're gonna boot the server off!\n\n",sendhost);
+                       DoSplit(sendhost);
                        return false;
                }
 
@@ -366,27 +497,53 @@ bool connection::SendPacket(char *message, const char* host)
                        cn->CloseConnection();
                        cn->SetState(STATE_DISCONNECTED);
                        // retry the packet along a new route so either arrival OR failure are gauranteed (bugfix)
-                       this->SendPacket(message,host);
-                       return false;
+                       return this->SendPacket(message,sendhost);
                }
                return true;
        }
 }
 
+bool already_have_sum(std::string sum)
+{
+       for (int i = 0; i < xsums.size(); i++)
+       {
+               if (xsums[i] == sum)
+               {
+                       return true;
+               }
+       }
+       if (xsums.size() >= 128)
+       {
+               xsums.pop_front();
+       }
+       xsums.push_back(sum);
+       return false;
+}
+
 // receives a packet from any where there is data waiting, first come, first served
 // fills the message and host values with the host where the data came from.
 
-bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
+bool connection::RecvPacket(std::deque<std::string> &messages, char* recvhost,std::deque<std::string> &sums)
 {
-       char data[32767];
-       memset(data, 0, 32767);
+       char data[65536];
+       memset(data, 0, 65536);
        for (int i = 0; i < this->connectors.size(); i++)
        {
                if (this->connectors[i].GetState() != STATE_DISCONNECTED)
                {
                        // returns false if the packet could not be sent (e.g. target host down)
                        int rcvsize = 0;
-                       rcvsize = recv(this->connectors[i].GetDescriptor(),data,32767,0);
+
+                       // check if theres any data on this socket
+                       // if not, continue onwards to the next.
+                       pollfd polls;
+                       polls.fd = this->connectors[i].GetDescriptor();
+                       polls.events = POLLIN;
+                       int ret = poll(&polls,1,1);
+                       if (ret <= 0) continue;
+
+                       rcvsize = recv(this->connectors[i].GetDescriptor(),data,65000,0);
+                       data[rcvsize] = '\0';
                        if (rcvsize == -1)
                        {
                                if (errno != EAGAIN)
@@ -397,32 +554,50 @@ bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
                                        this->connectors[i].SetState(STATE_DISCONNECTED);
                                }
                        }
+                       int pushed = 0;
                        if (rcvsize > 0)
                        {
-                               char* l = strtok(data,"\n");
-                               while (l)
+                               this->connectors[i].AddBuffer(data);
+                               if (this->connectors[i].BufferIsComplete())
                                {
-                                       char sanitized[32767];
-                                       memset(sanitized, 0, 32767);
-                                       int ptt = 0;
-                                       for (int pt = 0; pt < strlen(l); pt++)
+                                       while (this->connectors[i].BufferIsComplete())
                                        {
-                                               if (l[pt] != '\r')
+                                               std::string text = this->connectors[i].GetBuffer();
+                                               if (text != "")
                                                {
-                                                       sanitized[ptt++] = l[pt];
+                                                       if ((text[0] == ':') && (text.find(" ") != std::string::npos))
+                                                       {
+                                                               std::string orig = text;
+                                                               log(DEBUG,"Original: %s",text.c_str());
+                                                               std::string sum = text.substr(1,text.find(" ")-1);
+                                                               text = text.substr(text.find(" ")+1,text.length());
+                                                               std::string possible_token = text.substr(1,text.find(" ")-1);
+                                                               if (possible_token.length() > 1)
+                                                               {
+                                                                       sums.push_back("*");
+                                                                       text = orig;
+                                                                       log(DEBUG,"Non-mesh, non-tokenized string passed up the chain");
+                                                               }
+                                                               else
+                                                               {
+                                                                       log(DEBUG,"Packet sum: '%s'",sum.c_str());
+                                                                       if ((already_have_sum(sum)) && (sum != "*"))
+                                                                       {
+                                                                               // we don't accept dupes
+                                                                               log(DEBUG,"Duplicate packet sum %s from server %s dropped",sum.c_str(),this->connectors[i].GetServerName().c_str());
+                                                                               continue;
+                                                                       }
+                                                                       sums.push_back(sum.c_str());
+                                                               }
+                                                       }
+                                                       else sums.push_back("*");
+                                                       messages.push_back(text.c_str());
+                                                       strlcpy(recvhost,this->connectors[i].GetServerName().c_str(),160);
+                                                       log(DEBUG,"Connection::RecvPacket() %d:%s->%s",pushed++,recvhost,text.c_str()); 
                                                }
                                        }
-                                       sanitized[ptt] = '\0';
-                                       if (strlen(sanitized))
-                                       {
-                                               messages.push_back(sanitized);
-                                               strncpy(host,this->connectors[i].GetServerName().c_str(),160);
-                                               log(DEBUG,"main: Connection::RecvPacket() got '%s' from %s",sanitized,host);
-                                               
-                                       }
-                                       l = strtok(NULL,"\n");
+                                       return true;
                                }
-                               return true;
                        }
                }
        }
@@ -430,9 +605,3 @@ bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
        return false;
 }
 
-long connection::GenKey()
-{
-       srand(time(NULL));
-       return (random()*time(NULL));
-}
-