]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/servers.cpp
Attempt to fix recursive loop
[user/henk/code/inspircd.git] / src / servers.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 using namespace std;
18
19 #include "inspircd_config.h" 
20 #include "servers.h"
21 #include "inspircd.h"
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include <sys/errno.h>
26 #include <sys/ioctl.h>
27 #include <sys/utsname.h>
28 #include <vector>
29 #include <string>
30 #include <deque>
31 #include <sstream>
32 #include <map>
33 #include "inspstring.h"
34 #include "helperfuncs.h"
35 #include "connection.h"
36
37 extern time_t TIME;
38 extern int MaxConn;
39
40 extern serverrec* me[32];
41
42 extern bool has_been_netsplit;
43
44 std::deque<std::string> xsums;
45
46 serverrec::serverrec()
47 {
48         strlcpy(name,"",256);
49         pingtime = 0;
50         lastping = TIME;
51         usercount_i = usercount = opercount = version = 0;
52         hops_away = 1;
53         signon = TIME;
54         jupiter = false;
55         fd = 0;
56         sync_soon = false;
57         strlcpy(nickserv,"",NICKMAX);
58         connectors.clear();
59 }
60
61  
62 serverrec::~serverrec()
63 {
64 }
65
66 serverrec::serverrec(char* n, long ver, bool jupe)
67 {
68         strlcpy(name,n,256);
69         lastping = TIME;
70         usercount_i = usercount = opercount = 0;
71         version = ver;
72         hops_away = 1;
73         signon = TIME;
74         jupiter = jupe;
75         fd = 0;
76         sync_soon = false;
77         strlcpy(nickserv,"",NICKMAX);
78         connectors.clear();
79 }
80
81 bool serverrec::CreateListener(char* newhost, int p)
82 {
83         sockaddr_in host_address;
84         int flags;
85         in_addr addy;
86         int on = 0;
87         struct linger linger = { 0 };
88
89         this->port = p;
90
91         fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
92         if (fd <= 0)
93         {
94                 return false;
95         }
96
97         setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
98         linger.l_onoff = 1;
99         linger.l_linger = 1;
100         setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
101
102         // attempt to increase socket sendq and recvq as high as its possible
103         // to get them on linux.
104         int sendbuf = 32768;
105         int recvbuf = 32768;
106         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
107         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
108
109         memset((void*)&host_address, 0, sizeof(host_address));
110
111         host_address.sin_family = AF_INET;
112
113         if (!strcmp(newhost,""))
114         {
115                 host_address.sin_addr.s_addr = htonl(INADDR_ANY);
116         }
117         else
118         {
119                 inet_aton(newhost,&addy);
120                 host_address.sin_addr = addy;
121         }
122
123         host_address.sin_port = htons(p);
124
125         if (bind(fd,(sockaddr*)&host_address,sizeof(host_address))<0)
126         {
127                 return false;
128         }
129
130         // make the socket non-blocking
131         flags = fcntl(fd, F_GETFL, 0);
132         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
133
134         this->port = p;
135
136         listen(this->fd, MaxConn);
137
138         return true;
139 }
140
141
142 bool serverrec::BeginLink(char* targethost, int newport, char* password, char* servername, int myport)
143 {
144         char connect[MAXBUF];
145
146         ircd_connector connector;
147         ircd_connector *cn = this->FindHost(servername);
148
149
150         if (cn)
151         {
152                 WriteOpers("CONNECT aborted: Server %s already exists",servername);
153                 return false;
154         }
155
156
157         if (this->fd)
158         {
159                 if (connector.MakeOutboundConnection(targethost,newport))
160                 {
161                         // targethost has been turned into an ip...
162                         // we dont want this as the server name.
163                         connector.SetServerName(servername);
164                         snprintf(connect,MAXBUF,"S %s %s %lu %s :%s",getservername().c_str(),password,(unsigned long)myport,GetRevision().c_str(),getserverdesc().c_str());
165                         connector.SetState(STATE_NOAUTH_OUTBOUND);
166                         connector.SetHostAndPort(targethost, newport);
167                         this->connectors.push_back(connector);
168                         // this packet isn't actually sent until the socket connects -- the STATE_NOAUTH_OUTBOUND state
169                         // queues outbound data until the socket is polled as writeable (e.g. the connection is established)
170                         return this->SendPacket(connect, servername);
171                 }
172                 else
173                 {
174                         connector.SetState(STATE_DISCONNECTED);
175                         WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
176                         return false;
177                 }
178         }
179         return false;
180 }
181
182
183 bool serverrec::MeshCookie(char* targethost, int newport, unsigned long cookie, char* servername)
184 {
185         char connect[MAXBUF];
186
187         ircd_connector connector;
188
189         WriteOpers("Establishing meshed link to %s:%d",servername,newport);
190
191         if (this->fd)
192         {
193                 if (connector.MakeOutboundConnection(targethost,newport))
194                 {
195                         // targethost has been turned into an ip...
196                         // we dont want this as the server name.
197                         connector.SetServerName(servername);
198                         snprintf(connect,MAXBUF,"- %lu %s :%s",cookie,getservername().c_str(),getserverdesc().c_str());
199                         connector.SetState(STATE_COOKIE_OUTBOUND);
200                         connector.SetHostAndPort(targethost, newport);
201                         this->connectors.push_back(connector);
202                         return this->SendPacket(connect, servername);
203                 }
204                 else
205                 {
206                         connector.SetState(STATE_DISCONNECTED);
207                         WriteOpers("Could not create outbound connection to %s:%d",targethost,newport);
208                 }
209         }
210         return false;
211 }
212
213 bool serverrec::AddIncoming(int newfd, char* targethost, int sourceport)
214 {
215         ircd_connector connector;
216
217         // targethost has been turned into an ip...
218         // we dont want this as the server name.
219         connector.SetServerName(targethost);
220         connector.SetDescriptor(newfd);
221         connector.SetState(STATE_NOAUTH_INBOUND);
222         int flags = fcntl(newfd, F_GETFL, 0);
223         fcntl(newfd, F_SETFL, flags | O_NONBLOCK);
224         int sendbuf = 32768;
225         int recvbuf = 32768;
226         setsockopt(newfd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
227         setsockopt(newfd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
228         connector.SetHostAndPort(targethost, sourceport);
229         connector.SetState(STATE_NOAUTH_INBOUND);
230         log(DEBUG,"serverrec::AddIncoming() Added connection: %s:%d",targethost,sourceport);
231         this->connectors.push_back(connector);
232         return true;
233 }
234
235 void serverrec::TerminateLink(char* targethost)
236 {
237         // this locates the targethost in the serverrec::connectors vector of the class,
238        // and terminates it by sending it an SQUIT token and closing its descriptor.
239         // TerminateLink with a null string causes a terminate of ALL links
240 }
241
242 // Returns a pointer to the connector for 'host'
243 ircd_connector* serverrec::FindHost(std::string findhost)
244 {
245         for (unsigned int i = 0; i < this->connectors.size(); i++)
246         {
247                 if (this->connectors[i].GetServerName() == findhost)
248                 {
249                         return &this->connectors[i];
250                 }
251         }
252         return NULL;
253 }
254
255
256 // Checks to see if we can still reach a server at all (e.g. is it in ANY routing table?)
257 bool IsRoutable(std::string servername)
258 {
259         int c = 0;
260         for (int x = 0; x < 32; x++)
261         if (me[x])
262         {
263                 for (unsigned int i = 0; i < me[x]->connectors.size(); i++)
264                 {
265                         if ((me[x]->connectors[i].GetServerName() == servername) && (me[x]->connectors[i].GetState() != STATE_DISCONNECTED))
266                         {
267                                 c++;
268                         }
269                 }
270         }
271         return (c != 0);
272 }
273
274
275 void serverrec::FlushWriteBuffers()
276 {
277         char buffer[MAXBUF];
278         for (unsigned int i = 0; i < this->connectors.size(); i++)
279         {
280                 // don't try and ping a NOAUTH_OUTBOUND state, its not authed yet!
281                 if ((this->connectors[i].GetState() == STATE_NOAUTH_OUTBOUND) && (TIME > this->connectors[i].age+30))
282                 {
283                         // however if we reach this timer its connected timed out :)
284                         WriteOpers("*** Connection to %s timed out",this->connectors[i].GetServerName().c_str());
285                         snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
286                         NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
287                         DoSplit(this->connectors[i].GetServerName().c_str());
288                         return;
289                 }
290                 if ((this->connectors[i].GetState() == STATE_NOAUTH_INBOUND) && (TIME > this->connectors[i].age+30))
291                 {
292                         WriteOpers("*** Connection from %s timed out",this->connectors[i].GetServerName().c_str());
293                         snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
294                         NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
295                         DoSplit(this->connectors[i].GetServerName().c_str());
296                         return;
297                 }
298                 if (this->connectors[i].GetState() != STATE_DISCONNECTED)
299                 {
300                         if (!this->connectors[i].CheckPing())
301                         {
302                                 WriteOpers("*** Lost single connection to %s: Ping timeout",this->connectors[i].GetServerName().c_str());
303                                 this->connectors[i].CloseConnection();
304                                 this->connectors[i].SetState(STATE_DISCONNECTED);
305                                 if (!IsRoutable(this->connectors[i].GetServerName()))
306                                 {
307                                         WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
308                                         snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
309                                         NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
310                                         DoSplit(this->connectors[i].GetServerName().c_str());
311                                 }
312                                 has_been_netsplit = true;
313                         }
314                 }
315                 if ((this->connectors[i].GetWriteError() !="") && (this->connectors[i].GetState() != STATE_DISCONNECTED))
316                 {
317                         // if we're here the write() caused an error, we cannot proceed
318                         WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",this->connectors[i].GetServerName().c_str(),this->connectors[i].GetWriteError().c_str());
319                         this->connectors[i].CloseConnection();
320                         this->connectors[i].SetState(STATE_DISCONNECTED);
321                         if (!IsRoutable(this->connectors[i].GetServerName()))
322                         {
323                                 WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
324                                 snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
325                                 NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
326                                 DoSplit(this->connectors[i].GetServerName().c_str());
327                         }
328                         has_been_netsplit = true;
329                 }
330                 if ((this->connectors[i].HasBufferedOutput()) && (this->connectors[i].GetState() != STATE_DISCONNECTED))
331                 {
332                         if (!this->connectors[i].FlushWriteBuf())
333                         {
334                                 // if we're here the write() caused an error, we cannot proceed
335                                 WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",this->connectors[i].GetServerName().c_str(),this->connectors[i].GetWriteError().c_str());
336                                 this->connectors[i].CloseConnection();
337                                 this->connectors[i].SetState(STATE_DISCONNECTED);
338                                 if (!IsRoutable(this->connectors[i].GetServerName()))
339                                 {
340                                         WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
341                                         snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
342                                         NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
343                                         DoSplit(this->connectors[i].GetServerName().c_str());
344                                 }
345                                 has_been_netsplit = true;
346                         }
347                 }
348         }
349 }
350
351 bool serverrec::SendPacket(char *message, const char* sendhost)
352 {
353         if ((!message) || (!sendhost))
354                 return true;
355
356         ircd_connector* cn = this->FindHost(sendhost);
357
358         if (!strchr(message,'\n'))
359         {
360                 strlcat(message,"\n",MAXBUF);
361         }
362
363         if (cn)
364         {
365                 log(DEBUG,"main: serverrec::SendPacket() sent '%s' to %s",message,cn->GetServerName().c_str());
366
367                 if (cn->GetState() == STATE_DISCONNECTED)
368                 {
369                         // fix: can only route one hop to avoid a loop
370                         if (strncmp(message,"R ",2))
371                         {
372                                 log(DEBUG,"Not a double reroute");
373                                 // this route is down, we must re-route the packet through an available point in the mesh.
374                                 for (unsigned int k = 0; k < this->connectors.size(); k++)
375                                 {
376                                         log(DEBUG,"Check connector %d: %s",k,this->connectors[k].GetServerName().c_str());
377                                         // search for another point in the mesh which can 'reach' where we want to go
378                                         for (unsigned int m = 0; m < this->connectors[k].routes.size(); m++)
379                                         {
380                                                 if (!strcasecmp(this->connectors[k].routes[m].c_str(),sendhost))
381                                                 {
382                                                         log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
383                                                         if (this->connectors[k].GetState() != STATE_DISCONNECTED)
384                                                         {
385                                                                 char buffer[MAXBUF];
386                                                                 snprintf(buffer,MAXBUF,"R %s %s",sendhost,message);
387                                                                 this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
388                                                                 return true;
389                                                         }
390                                                         else
391                                                         {
392                                                                 log(DEBUG,"Nope, this route is down...");
393                                                                 return false;
394                                                         }
395                                                 }
396                                         }
397                                 }
398                         }
399                         char buffer[MAXBUF];
400                         snprintf(buffer,MAXBUF,"& %s",sendhost);
401                         WriteOpers("*** All connections to %s lost.",sendhost);
402                         NetSendToAllExcept(sendhost,buffer);
403                         DoSplit(sendhost);
404                         return false;
405                 }
406
407                 // returns false if the packet could not be sent (e.g. target host down)
408                 if (!cn->AddWriteBuf(message))
409                 {
410                         // if we're here, there was an error pending, and the send cannot proceed
411                         log(DEBUG,"cn->AddWriteBuf() failed for serverrec::SendPacket(): %s",cn->GetWriteError().c_str());
412                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
413                         cn->CloseConnection();
414                         cn->SetState(STATE_DISCONNECTED);
415                         WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",cn->GetServerName().c_str(),cn->GetWriteError().c_str());
416                         // retry the packet along a new route so either arrival OR failure are gauranteed (bugfix)
417                         return this->SendPacket(message,sendhost);
418                 }
419                 if (!cn->FlushWriteBuf())
420                 {
421                         // if we're here the write() caused an error, we cannot proceed
422                         log(DEBUG,"cn->FlushWriteBuf() failed for serverrec::SendPacket(): %s",cn->GetWriteError().c_str());
423                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
424                         cn->CloseConnection();
425                         cn->SetState(STATE_DISCONNECTED);
426                         WriteOpers("*** Lost single connection to %s, link inactive and retrying: %s",cn->GetServerName().c_str(),cn->GetWriteError().c_str());
427                         // retry the packet along a new route so either arrival OR failure are gauranteed
428                         return this->SendPacket(message,sendhost);
429                 }
430                 return true;
431         }
432         return false;
433 }
434
435 bool already_have_sum(std::string sum)
436 {
437         for (unsigned int i = 0; i < xsums.size(); i++)
438         {
439                 if (xsums[i] == sum)
440                 {
441                         return true;
442                 }
443         }
444         if (xsums.size() >= 128)
445         {
446                 xsums.pop_front();
447         }
448         xsums.push_back(sum);
449         return false;
450 }
451
452 // receives a packet from any where there is data waiting, first come, first served
453 // fills the message and host values with the host where the data came from.
454
455 bool serverrec::RecvPacket(std::deque<std::string> &messages, char* recvhost,std::deque<std::string> &sums)
456 {
457         char data[65536],buffer[MAXBUF];
458         memset(data, 0, 65536);
459         for (unsigned int i = 0; i < this->connectors.size(); i++)
460         {
461                 if (this->connectors[i].GetState() != STATE_DISCONNECTED)
462                 {
463                         // returns false if the packet could not be sent (e.g. target host down)
464                         int rcvsize = 0;
465
466                         // check if theres any data on this socket
467                         // if not, continue onwards to the next.
468                         pollfd polls;
469                         polls.fd = this->connectors[i].GetDescriptor();
470                         polls.events = POLLIN;
471                         int ret = poll(&polls,1,1);
472                         if (ret <= 0) continue;
473
474                         rcvsize = recv(this->connectors[i].GetDescriptor(),data,65000,0);
475                         data[rcvsize] = '\0';
476                         if (rcvsize == 0)
477                         {
478                                 log(DEBUG,"recv() failed for serverrec::RecvPacket(): EOF");
479                                 log(DEBUG,"Disabling connector: %s",this->connectors[i].GetServerName().c_str());
480                                 this->connectors[i].CloseConnection();
481                                 this->connectors[i].SetState(STATE_DISCONNECTED);
482                                 if (!IsRoutable(this->connectors[i].GetServerName()))
483                                 {
484                                         WriteOpers("*** Server %s is no longer routable, disconnecting (EOF)",this->connectors[i].GetServerName().c_str());
485                                         snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
486                                         NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
487                                         DoSplit(this->connectors[i].GetServerName().c_str());
488                                 }
489                                 has_been_netsplit = true;
490                         }
491                         if (rcvsize == -1)
492                         {
493                                 if (errno != EAGAIN)
494                                 {
495                                         log(DEBUG,"recv() failed for serverrec::RecvPacket(): %s",strerror(errno));
496                                         log(DEBUG,"Disabling connector: %s",this->connectors[i].GetServerName().c_str());
497                                         this->connectors[i].CloseConnection();
498                                         this->connectors[i].SetState(STATE_DISCONNECTED);
499                                         if (!IsRoutable(this->connectors[i].GetServerName()))
500                                         {
501                                                 WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
502                                                 snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
503                                                 NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
504                                                 DoSplit(this->connectors[i].GetServerName().c_str());
505                                         }
506                                         has_been_netsplit = true;
507                                 }
508                         }
509                         int pushed = 0;
510                         if (rcvsize > 0)
511                         {
512                                 if (!this->connectors[i].AddBuffer(data))
513                                 {
514                                         WriteOpers("*** Read buffer for %s exceeds maximum, closing connection!",this->connectors[i].GetServerName().c_str());
515                                         this->connectors[i].CloseConnection();
516                                         this->connectors[i].SetState(STATE_DISCONNECTED);
517                                         if (!IsRoutable(this->connectors[i].GetServerName()))
518                                         {
519                                                 WriteOpers("*** Server %s is no longer routable, disconnecting.",this->connectors[i].GetServerName().c_str());
520                                                 snprintf(buffer,MAXBUF,"& %s",this->connectors[i].GetServerName().c_str());
521                                                 NetSendToAllExcept(this->connectors[i].GetServerName().c_str(),buffer);
522                                                 DoSplit(this->connectors[i].GetServerName().c_str());
523                                         }
524                                         has_been_netsplit = true;
525                                 }
526                                 if (this->connectors[i].BufferIsComplete())
527                                 {
528                                         this->connectors[i].ResetPing();
529                                         while (this->connectors[i].BufferIsComplete())
530                                         {
531                                                 std::string text = this->connectors[i].GetBuffer();
532                                                 if (text != "")
533                                                 {
534                                                         if ((text[0] == ':') && (text.find(" ") != std::string::npos))
535                                                         {
536                                                                 std::string orig = text;
537                                                                 log(DEBUG,"Original: %s",text.c_str());
538                                                                 std::string sum = text.substr(1,text.find(" ")-1);
539                                                                 text = text.substr(text.find(" ")+1,text.length());
540                                                                 std::string possible_token = text.substr(1,text.find(" ")-1);
541                                                                 if (possible_token.length() > 1)
542                                                                 {
543                                                                         sums.push_back("*");
544                                                                         text = orig;
545                                                                         log(DEBUG,"Non-mesh, non-tokenized string passed up the chain");
546                                                                 }
547                                                                 else
548                                                                 {
549                                                                         log(DEBUG,"Packet sum: '%s'",sum.c_str());
550                                                                         if ((already_have_sum(sum)) && (sum != "*"))
551                                                                         {
552                                                                                 // we don't accept dupes
553                                                                                 continue;
554                                                                         }
555                                                                         sums.push_back(sum.c_str());
556                                                                 }
557                                                         }
558                                                         else sums.push_back("*");
559                                                         messages.push_back(text.c_str());
560                                                         strlcpy(recvhost,this->connectors[i].GetServerName().c_str(),160);
561                                                         log(DEBUG,"serverrec::RecvPacket() %d:%s->%s",pushed++,recvhost,text.c_str());
562                                                 }
563                                         }
564                                         return true;
565                                 }
566                         }
567                 }
568         }
569         // nothing new yet -- message and host will be undefined
570         return false;
571 }
572