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