Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members

servers.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
00006  *                       E-mail:
00007  *                <brain@chatspike.net>
00008  *                <Craig@chatspike.net>
00009  *     
00010  * Written by Craig Edwards, Craig McLure, and others.
00011  * This program is free but copyrighted software; see
00012  *            the file COPYING for details.
00013  *
00014  * ---------------------------------------------------
00015  */
00016 
00017 using namespace std;
00018 
00019 #include "inspircd_config.h" 
00020 #include "servers.h"
00021 #include "inspircd.h"
00022 #include <unistd.h>
00023 #include <fcntl.h>
00024 #include <poll.h>
00025 #include <sys/errno.h>
00026 #include <sys/ioctl.h>
00027 #include <sys/utsname.h>
00028 #include <vector>
00029 #include <string>
00030 #include <deque>
00031 #include <sstream>
00032 #include <map>
00033 #include "inspstring.h"
00034 #include "helperfuncs.h"
00035 #include "connection.h"
00036 
00037 extern time_t TIME;
00038 extern int MaxConn;
00039 
00040 extern serverrec* me[32];
00041 
00042 extern bool has_been_netsplit;
00043 
00044 std::deque<std::string> xsums;
00045 
00046 serverrec::serverrec()
00047 {
00048         strlcpy(name,"",256);
00049         pingtime = 0;
00050         lastping = TIME;
00051         usercount_i = usercount = opercount = version = 0;
00052         hops_away = 1;
00053         signon = TIME;
00054         jupiter = false;
00055         fd = 0;
00056         sync_soon = false;
00057         strlcpy(nickserv,"",NICKMAX);
00058         connectors.clear();
00059 }
00060 
00061  
00062 serverrec::~serverrec()
00063 {
00064 }
00065 
00066 serverrec::serverrec(char* n, long ver, bool jupe)
00067 {
00068         strlcpy(name,n,256);
00069         lastping = TIME;
00070         usercount_i = usercount = opercount = 0;
00071         version = ver;
00072         hops_away = 1;
00073         signon = TIME;
00074         jupiter = jupe;
00075         fd = 0;
00076         sync_soon = false;
00077         strlcpy(nickserv,"",NICKMAX);
00078         connectors.clear();
00079 }
00080 
00081 bool serverrec::CreateListener(char* newhost, int p)
00082 {
00083         sockaddr_in host_address;
00084         int flags;
00085         in_addr addy;
00086         int on = 0;
00087         struct linger linger = { 0 };
00088 
00089         this->port = p;
00090 
00091         fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00092         if (fd <= 0)
00093         {
00094                 return false;
00095         }
00096 
00097         setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
00098         linger.l_onoff = 1;
00099         linger.l_linger = 1;
00100         setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
00101 
00102         // attempt to increase socket sendq and recvq as high as its possible
00103         // to get them on linux.
00104         int sendbuf = 32768;
00105         int recvbuf = 32768;
00106         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
00107         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
00108 
00109         memset((void*)&host_address, 0, sizeof(host_address));
00110 
00111         host_address.sin_family = AF_INET;
00112 
00113         if (!strcmp(newhost,""))
00114         {
00115                 host_address.sin_addr.s_addr = htonl(INADDR_ANY);
00116         }
00117         else
00118         {
00119                 inet_aton(newhost,&addy);
00120                 host_address.sin_addr = addy;
00121         }
00122 
00123         host_address.sin_port = htons(p);
00124 
00125         if (bind(fd,(sockaddr*)&host_address,sizeof(host_address))<0)
00126         {
00127                 return false;
00128         }
00129 
00130         // make the socket non-blocking
00131         flags = fcntl(fd, F_GETFL, 0);
00132         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
00133 
00134         this->port = p;
00135 
00136         listen(this->fd, MaxConn);
00137 
00138         return true;
00139 }
00140 
00141 
00142 bool serverrec::BeginLink(char* targethost, int newport, char* password, char* servername, int myport)
00143 {
00144         char connect[MAXBUF];
00145 
00146         ircd_connector connector;
00147         ircd_connector *cn = this->FindHost(servername);
00148 
00149 
00150         if (cn)
00151         {
00152                 WriteOpers("CONNECT aborted: Server %s already exists",servername);
00153                 return false;
00154         }
00155 
00156 
00157         if (this->fd)
00158         {
00159                 if (connector.MakeOutboundConnection(targethost,newport))
00160                 {
00161                         // targethost has been turned into an ip...
00162                         // we dont want this as the server name.
00163                         connector.SetServerName(servername);
00164                         snprintf(connect,MAXBUF,"S %s %s %lu %s :%s",getservername().c_str(),password,(unsigned long)myport,GetRevision().c_str(),getserverdesc().c_str());
00165                         connector.SetState(STATE_NOAUTH_OUTBOUND);
00166                         connector.SetHostAndPort(targethost, newport);
00167                         this->connectors.push_back(connector);
00168                         // this packet isn't actually sent until the socket connects -- the STATE_NOAUTH_OUTBOUND state
00169                         // queues outbound data until the socket is polled as writeable (e.g. the connection is established)
00170                         return this->SendPacket(connect, servername);
00171                 }
00172                 else
00173                 {
00174                         connector.SetState(STATE_DISCONNECTED);
00175                         WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
00176                 }
00177         }
00178         return false;
00179 }
00180 
00181 
00182 bool serverrec::MeshCookie(char* targethost, int newport, unsigned long cookie, char* servername)
00183 {
00184         char connect[MAXBUF];
00185 
00186         ircd_connector connector;
00187 
00188         WriteOpers("Establishing meshed link to %s:%d",servername,newport);
00189 
00190         if (this->fd)
00191         {
00192                 if (connector.MakeOutboundConnection(targethost,newport))
00193                 {
00194                         // targethost has been turned into an ip...
00195                         // we dont want this as the server name.
00196                         connector.SetServerName(servername);
00197                         snprintf(connect,MAXBUF,"- %lu %s :%s",cookie,getservername().c_str(),getserverdesc().c_str());
00198                         connector.SetState(STATE_NOAUTH_OUTBOUND);
00199                         connector.SetHostAndPort(targethost, newport);
00200                         this->connectors.push_back(connector);
00201                         return this->SendPacket(connect, servername);
00202                 }
00203                 else
00204                 {
00205                         connector.SetState(STATE_DISCONNECTED);
00206                         WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
00207                 }
00208         }
00209         return false;
00210 }
00211 
00212 bool serverrec::AddIncoming(int newfd, char* targethost, int sourceport)
00213 {
00214         ircd_connector connector;
00215 
00216         // targethost has been turned into an ip...
00217         // we dont want this as the server name.
00218         connector.SetServerName(targethost);
00219         connector.SetDescriptor(newfd);
00220         connector.SetState(STATE_NOAUTH_INBOUND);
00221         int flags = fcntl(newfd, F_GETFL, 0);
00222         fcntl(newfd, F_SETFL, flags | O_NONBLOCK);
00223         int sendbuf = 32768;
00224         int recvbuf = 32768;
00225         setsockopt(newfd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
00226         setsockopt(newfd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
00227         connector.SetHostAndPort(targethost, sourceport);
00228         connector.SetState(STATE_NOAUTH_INBOUND);
00229         log(DEBUG,"serverrec::AddIncoming() Added connection: %s:%d",targethost,sourceport);
00230         this->connectors.push_back(connector);
00231         return true;
00232 }
00233 
00234 void serverrec::TerminateLink(char* targethost)
00235 {
00236         // this locates the targethost in the serverrec::connectors vector of the class,
00237        // and terminates it by sending it an SQUIT token and closing its descriptor.
00238         // TerminateLink with a null string causes a terminate of ALL links
00239 }
00240 
00241 // Returns a pointer to the connector for 'host'
00242 ircd_connector* serverrec::FindHost(std::string findhost)
00243 {
00244         for (int i = 0; i < this->connectors.size(); i++)
00245         {
00246                 if (this->connectors[i].GetServerName() == findhost)
00247                 {
00248                         return &this->connectors[i];
00249                 }
00250         }
00251         return NULL;
00252 }
00253 
00254 
00255 // Checks to see if we can still reach a server at all (e.g. is it in ANY routing table?)
00256 bool IsRoutable(std::string servername)
00257 {
00258         for (int x = 0; x < 32; x++) if (me[x])
00259         {
00260                 ircd_connector* cn = me[x]->FindHost(servername.c_str());
00261                 if (cn)
00262                 {
00263                         if (cn->GetState() == STATE_DISCONNECTED)
00264                         {
00265                                 for (int k = 0; k < me[x]->connectors.size(); k++)
00266                                 {
00267                                         for (int m = 0; m < me[x]->connectors[k].routes.size(); m++)
00268                                         {
00269                                                 if (!strcasecmp(me[x]->connectors[k].routes[m].c_str(),servername.c_str()))
00270                                                 {
00271                                                         return true;
00272                                                 }
00273                                         }
00274                                 }
00275                                 return false;
00276                         }
00277                         else return true;
00278                 }
00279         }
00280         return false;
00281 }
00282 
00283 
00284 void serverrec::FlushWriteBuffers()
00285 {
00286         for (int i = 0; i < this->connectors.size(); i++)
00287         {
00288                 // don't try and ping a NOAUTH_OUTBOUND state, its not authed yet!
00289                 if ((this->connectors[i].GetState() == STATE_NOAUTH_OUTBOUND) && (TIME > this->connectors[i].age+30))
00290                 {
00291                         // however if we reach this timer its connected timed out :)
00292                         WriteOpers("*** Connection to %s timed out",this->connectors[i].GetServerName().c_str());
00293                         DoSplit(this->connectors[i].GetServerName().c_str());
00294                         return;
00295                 }
00296                 else if ((this->connectors[i].GetState() == STATE_NOAUTH_INBOUND) && (TIME > this->connectors[i].age+30))
00297                 {
00298                         WriteOpers("*** Connection from %s timed out",this->connectors[i].GetServerName().c_str());
00299                         DoSplit(this->connectors[i].GetServerName().c_str());
00300                         return;
00301                 }
00302                 else if (this->connectors[i].GetState() != STATE_DISCONNECTED)
00303                 {
00304                         if (!this->connectors[i].CheckPing())
00305                         {
00306                                 WriteOpers("*** Lost single connection to %s: Ping timeout",this->connectors[i].GetServerName().c_str());
00307                                 this->connectors[i].CloseConnection();
00308                                 this->connectors[i].SetState(STATE_DISCONNECTED);
00309                                 if (!IsRoutable(this->connectors[i].GetServerName()))
00310                                 {
00311                                         WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
00312                                         DoSplit(this->connectors[i].GetServerName().c_str());
00313                                 }
00314                                 has_been_netsplit = true;
00315                         }
00316                 }
00317                 if (this->connectors[i].HasBufferedOutput())
00318                 {
00319                         if (!this->connectors[i].FlushWriteBuf())
00320                         {
00321                                 // if we're here the write() caused an error, we cannot proceed
00322                                 WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",this->connectors[i].GetServerName().c_str(),this->connectors[i].GetWriteError().c_str());
00323                                 this->connectors[i].CloseConnection();
00324                                 this->connectors[i].SetState(STATE_DISCONNECTED);
00325                                 if (!IsRoutable(this->connectors[i].GetServerName()))
00326                                 {
00327                                         WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
00328                                         DoSplit(this->connectors[i].GetServerName().c_str());
00329                                 }
00330                                 has_been_netsplit = true;
00331                         }
00332                 }
00333         }
00334 }
00335 
00336 bool serverrec::SendPacket(char *message, const char* sendhost)
00337 {
00338         if ((!message) || (!sendhost))
00339                 return true;
00340 
00341         ircd_connector* cn = this->FindHost(sendhost);
00342 
00343         if (!strchr(message,'\n'))
00344         {
00345                 strlcat(message,"\n",MAXBUF);
00346         }
00347 
00348         if (cn)
00349         {
00350                 log(DEBUG,"main: serverrec::SendPacket() sent '%s' to %s",message,cn->GetServerName().c_str());
00351 
00352                 if (cn->GetState() == STATE_DISCONNECTED)
00353                 {
00354                         // fix: can only route one hop to avoid a loop
00355                         if (strncmp(message,"R ",2))
00356                         {
00357                                 log(DEBUG,"Not a double reroute");
00358                                 // this route is down, we must re-route the packet through an available point in the mesh.
00359                                 for (int k = 0; k < this->connectors.size(); k++)
00360                                 {
00361                                         log(DEBUG,"Check connector %d: %s",k,this->connectors[k].GetServerName().c_str());
00362                                         // search for another point in the mesh which can 'reach' where we want to go
00363                                         for (int m = 0; m < this->connectors[k].routes.size(); m++)
00364                                         {
00365                                                 if (!strcasecmp(this->connectors[k].routes[m].c_str(),sendhost))
00366                                                 {
00367                                                         log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
00368                                                         char buffer[MAXBUF];
00369                                                         snprintf(buffer,MAXBUF,"R %s %s",sendhost,message);
00370                                                         this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
00371                                                         return true;
00372                                                 }
00373                                         }
00374                                 }
00375                         }
00376                         char buffer[MAXBUF];
00377                         snprintf(buffer,MAXBUF,"& %s",sendhost);
00378                         WriteOpers("*** All connections to %s lost.",sendhost);
00379                         NetSendToAllExcept(sendhost,buffer);
00380                         DoSplit(sendhost);
00381                         return false;
00382                 }
00383 
00384                 // returns false if the packet could not be sent (e.g. target host down)
00385                 if (!cn->AddWriteBuf(message))
00386                 {
00387                         // if we're here, there was an error pending, and the send cannot proceed
00388                         log(DEBUG,"cn->AddWriteBuf() failed for serverrec::SendPacket(): %s",cn->GetWriteError().c_str());
00389                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
00390                         cn->CloseConnection();
00391                         cn->SetState(STATE_DISCONNECTED);
00392                         WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",cn->GetServerName().c_str(),cn->GetWriteError().c_str());
00393                         // retry the packet along a new route so either arrival OR failure are gauranteed (bugfix)
00394                         return this->SendPacket(message,sendhost);
00395                 }
00396                 if (!cn->FlushWriteBuf())
00397                 {
00398                         // if we're here the write() caused an error, we cannot proceed
00399                         log(DEBUG,"cn->FlushWriteBuf() failed for serverrec::SendPacket(): %s",cn->GetWriteError().c_str());
00400                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
00401                         cn->CloseConnection();
00402                         cn->SetState(STATE_DISCONNECTED);
00403                         WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",cn->GetServerName().c_str(),cn->GetWriteError().c_str());
00404                         // retry the packet along a new route so either arrival OR failure are gauranteed
00405                         return this->SendPacket(message,sendhost);
00406                 }
00407                 return true;
00408         }
00409 }
00410 
00411 bool already_have_sum(std::string sum)
00412 {
00413         for (int i = 0; i < xsums.size(); i++)
00414         {
00415                 if (xsums[i] == sum)
00416                 {
00417                         return true;
00418                 }
00419         }
00420         if (xsums.size() >= 128)
00421         {
00422                 xsums.pop_front();
00423         }
00424         xsums.push_back(sum);
00425         return false;
00426 }
00427 
00428 // receives a packet from any where there is data waiting, first come, first served
00429 // fills the message and host values with the host where the data came from.
00430 
00431 bool serverrec::RecvPacket(std::deque<std::string> &messages, char* recvhost,std::deque<std::string> &sums)
00432 {
00433         char data[65536];
00434         memset(data, 0, 65536);
00435         for (int i = 0; i < this->connectors.size(); i++)
00436         {
00437                 if (this->connectors[i].GetState() != STATE_DISCONNECTED)
00438                 {
00439                         // returns false if the packet could not be sent (e.g. target host down)
00440                         int rcvsize = 0;
00441 
00442                         // check if theres any data on this socket
00443                         // if not, continue onwards to the next.
00444                         pollfd polls;
00445                         polls.fd = this->connectors[i].GetDescriptor();
00446                         polls.events = POLLIN;
00447                         int ret = poll(&polls,1,1);
00448                         if (ret <= 0) continue;
00449 
00450                         rcvsize = recv(this->connectors[i].GetDescriptor(),data,65000,0);
00451                         data[rcvsize] = '\0';
00452                         if (rcvsize == -1)
00453                         {
00454                                 if (errno != EAGAIN)
00455                                 {
00456                                         log(DEBUG,"recv() failed for serverrec::RecvPacket(): %s",strerror(errno));
00457                                         log(DEBUG,"Disabling connector: %s",this->connectors[i].GetServerName().c_str());
00458                                         this->connectors[i].CloseConnection();
00459                                         this->connectors[i].SetState(STATE_DISCONNECTED);
00460                                         if (!IsRoutable(this->connectors[i].GetServerName()))
00461                                         {
00462                                                 WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
00463                                                 DoSplit(this->connectors[i].GetServerName().c_str());
00464                                         }
00465                                         has_been_netsplit = true;
00466                                 }
00467                         }
00468                         int pushed = 0;
00469                         if (rcvsize > 0)
00470                         {
00471                                 if (!this->connectors[i].AddBuffer(data))
00472                                 {
00473                                         WriteOpers("*** Read buffer for %s exceeds maximum, closing connection!",this->connectors[i].GetServerName().c_str());
00474                                         this->connectors[i].CloseConnection();
00475                                         this->connectors[i].SetState(STATE_DISCONNECTED);
00476                                         if (!IsRoutable(this->connectors[i].GetServerName()))
00477                                         {
00478                                                 WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
00479                                                 DoSplit(this->connectors[i].GetServerName().c_str());
00480                                         }
00481                                         has_been_netsplit = true;
00482                                 }
00483                                 if (this->connectors[i].BufferIsComplete())
00484                                 {
00485                                         this->connectors[i].ResetPing();
00486                                         while (this->connectors[i].BufferIsComplete())
00487                                         {
00488                                                 std::string text = this->connectors[i].GetBuffer();
00489                                                 if (text != "")
00490                                                 {
00491                                                         if ((text[0] == ':') && (text.find(" ") != std::string::npos))
00492                                                         {
00493                                                                 std::string orig = text;
00494                                                                 log(DEBUG,"Original: %s",text.c_str());
00495                                                                 std::string sum = text.substr(1,text.find(" ")-1);
00496                                                                 text = text.substr(text.find(" ")+1,text.length());
00497                                                                 std::string possible_token = text.substr(1,text.find(" ")-1);
00498                                                                 if (possible_token.length() > 1)
00499                                                                 {
00500                                                                         sums.push_back("*");
00501                                                                         text = orig;
00502                                                                         log(DEBUG,"Non-mesh, non-tokenized string passed up the chain");
00503                                                                 }
00504                                                                 else
00505                                                                 {
00506                                                                         log(DEBUG,"Packet sum: '%s'",sum.c_str());
00507                                                                         if ((already_have_sum(sum)) && (sum != "*"))
00508                                                                         {
00509                                                                                 // we don't accept dupes
00510                                                                                 continue;
00511                                                                         }
00512                                                                         sums.push_back(sum.c_str());
00513                                                                 }
00514                                                         }
00515                                                         else sums.push_back("*");
00516                                                         messages.push_back(text.c_str());
00517                                                         strlcpy(recvhost,this->connectors[i].GetServerName().c_str(),160);
00518                                                         log(DEBUG,"serverrec::RecvPacket() %d:%s->%s",pushed++,recvhost,text.c_str());
00519                                                 }
00520                                         }
00521                                         return true;
00522                                 }
00523                         }
00524                 }
00525         }
00526         // nothing new yet -- message and host will be undefined
00527         return false;
00528 }
00529 

Generated on Wed May 25 22:02:05 2005 for InspIRCd by doxygen 1.3.3