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