]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/connection.cpp
Moved to B3 official
[user/henk/code/inspircd.git] / src / connection.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 #include <connection.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/errno.h>
21 #include <sys/ioctl.h>
22 #include <sys/utsname.h>
23 #include <vector>
24 #include <string>
25 #include <deque>
26 #include "inspircd.h"
27 #include "modules.h"
28 #include "inspstring.h"
29
30 using namespace std;
31
32
33 extern std::vector<Module*> modules;
34 extern std::vector<ircd_module*> factory;
35
36 extern int MODCOUNT;
37
38 extern time_t TIME;
39
40 connection::connection()
41 {
42         fd = 0;
43 }
44
45
46 bool connection::CreateListener(char* host, int p)
47 {
48         sockaddr_in host_address;
49         int flags;
50         in_addr addy;
51         int on = 0;
52         struct linger linger = { 0 };
53         
54         this->port = p;
55         
56         fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
57         if (fd <= 0)
58         {
59                 return false;
60         }
61
62         setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
63         linger.l_onoff = 1;
64         linger.l_linger = 1;
65         setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
66         
67         // attempt to increase socket sendq and recvq as high as its possible
68         // to get them on linux.
69         int sendbuf = 32768;
70         int recvbuf = 32768;
71         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
72         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
73
74         memset((void*)&host_address, 0, sizeof(host_address));
75
76         host_address.sin_family = AF_INET;
77
78         if (!strcmp(host,""))
79         {
80                 host_address.sin_addr.s_addr = htonl(INADDR_ANY);
81         }
82         else
83         {
84                 inet_aton(host,&addy);
85                 host_address.sin_addr = addy;
86         }
87
88         host_address.sin_port = htons(p);
89
90         if (bind(fd,(sockaddr*)&host_address,sizeof(host_address))<0)
91         {
92                 return false;
93         }
94
95         // make the socket non-blocking
96         flags = fcntl(fd, F_GETFL, 0);
97         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
98
99         this->port = p;
100
101         listen(this->fd,5);
102
103         return true;
104 }
105
106 char* ircd_connector::GetServerIP()
107 {
108         return this->host;
109 }
110
111 int ircd_connector::GetServerPort()
112 {
113         return this->port;
114 }
115
116 bool ircd_connector::SetHostAndPort(char* host, int port)
117 {
118         strncpy(this->host,host,160);
119         this->port = port;
120         return true;
121 }
122
123 bool ircd_connector::SetHostAddress(char* host, int port)
124 {
125         strncpy(this->host,host,160);
126         this->port = port;
127         memset((void*)&addr, 0, sizeof(addr));
128         addr.sin_family = AF_INET;
129         inet_aton(host,&addr.sin_addr);
130         addr.sin_port = htons(port);
131         return true;
132 }
133
134 void ircd_connector::SetServerPort(int p)
135 {
136         this->port = p;
137 }
138
139 bool ircd_connector::MakeOutboundConnection(char* host, int port)
140 {
141         log(DEBUG,"MakeOutboundConnection: Original param: %s",host);
142         hostent* hoste = gethostbyname(host);
143         if (!hoste)
144         {
145                 log(DEBUG,"MakeOutboundConnection: gethostbyname was NULL, setting %s",host);
146                 this->SetHostAddress(host,port);
147                 SetHostAndPort(host,port);
148         }
149         else
150         {
151                 struct in_addr* ia = (in_addr*)hoste->h_addr;
152                 log(DEBUG,"MakeOutboundConnection: gethostbyname was valid, setting %s",inet_ntoa(*ia));
153                 this->SetHostAddress(inet_ntoa(*ia),port);
154                 SetHostAndPort(inet_ntoa(*ia),port);
155         }
156
157         this->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
158         if (this->fd >= 0)
159         {
160                 if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)))
161                 {
162                         WriteOpers("connect() failed for %s",host);
163                         RemoveServer(this->servername.c_str());
164                         return false;
165                 }
166                 int flags = fcntl(this->fd, F_GETFL, 0);
167                 fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
168                 int sendbuf = 32768;
169                 int recvbuf = 32768;
170                 setsockopt(this->fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
171                 setsockopt(this->fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
172                 return true;
173         }
174         else
175         {
176                 WriteOpers("socket() failed!");
177                 RemoveServer(this->servername.c_str());
178         }
179
180         return false;
181 }
182
183
184 bool connection::BeginLink(char* targethost, int port, char* password, char* servername, int myport)
185 {
186         char connect[MAXBUF];
187         
188         ircd_connector connector;
189         ircd_connector *cn = this->FindHost(servername);
190
191
192         if (cn)
193         {
194                 WriteOpers("CONNECT aborted: Server %s already exists",servername);
195                 return false;
196         }
197
198         
199         if (this->fd)
200         {
201                 if (connector.MakeOutboundConnection(targethost,port))
202                 {
203                         // targethost has been turned into an ip...
204                         // we dont want this as the server name.
205                         connector.SetServerName(servername);
206                         snprintf(connect,MAXBUF,"S %s %s %d %d :%s",getservername().c_str(),password,myport,GetRevision(),getserverdesc().c_str());
207                         connector.SetState(STATE_NOAUTH_OUTBOUND);
208                         connector.SetHostAndPort(targethost, port);
209                         this->connectors.push_back(connector);
210                         return this->SendPacket(connect, servername);
211                 }
212                 else
213                 {
214                         connector.SetState(STATE_DISCONNECTED);
215                         WriteOpers("Could not create outbound connection to %s:%d",targethost,port);
216                 }
217         }
218         return false;
219 }
220
221 bool connection::MeshCookie(char* targethost, int port, long cookie, char* servername)
222 {
223         char connect[MAXBUF];
224         
225         ircd_connector connector;
226         
227         WriteOpers("Establishing meshed link to %s:%d",servername,port);
228
229         if (this->fd)
230         {
231                 if (connector.MakeOutboundConnection(targethost,port))
232                 {
233                         // targethost has been turned into an ip...
234                         // we dont want this as the server name.
235                         connector.SetServerName(servername);
236                         snprintf(connect,MAXBUF,"- %d %s :%s",cookie,getservername().c_str(),getserverdesc().c_str());
237                         connector.SetState(STATE_NOAUTH_OUTBOUND);
238                         connector.SetHostAndPort(targethost, port);
239                         connector.SetState(STATE_CONNECTED);
240                         this->connectors.push_back(connector);
241                         return this->SendPacket(connect, servername);
242                 }
243                 else
244                 {
245                         connector.SetState(STATE_DISCONNECTED);
246                         WriteOpers("Could not create outbound connection to %s:%d",targethost,port);
247                 }
248         }
249         return false;
250 }
251
252 bool connection::AddIncoming(int fd, char* targethost, int sourceport)
253 {
254         char connect[MAXBUF];
255         
256         ircd_connector connector;
257         
258         // targethost has been turned into an ip...
259         // we dont want this as the server name.
260         connector.SetServerName(targethost);
261         connector.SetDescriptor(fd);
262         connector.SetState(STATE_NOAUTH_INBOUND);
263         int flags = fcntl(fd, F_GETFL, 0);
264         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
265         int sendbuf = 32768;
266         int recvbuf = 32768;
267         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
268         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
269         connector.SetHostAndPort(targethost, sourceport);
270         connector.SetState(STATE_NOAUTH_INBOUND);
271         log(DEBUG,"connection::AddIncoming() Added connection: %s:%d",targethost,sourceport);
272         this->connectors.push_back(connector);
273         return true;
274 }
275
276 void connection::TerminateLink(char* targethost)
277 {
278         // this locates the targethost in the connection::connectors vector of the class,
279         // and terminates it by sending it an SQUIT token and closing its descriptor.
280         // TerminateLink with a null string causes a terminate of ALL links
281 }
282
283
284 // Returns a pointer to the connector for 'host'
285 ircd_connector* connection::FindHost(std::string host)
286 {
287         for (int i = 0; i < this->connectors.size(); i++)
288         {
289                 if (this->connectors[i].GetServerName() == host)
290                 {
291                         return &this->connectors[i];
292                 }
293         }
294         return NULL;
295 }
296
297 std::string ircd_connector::GetServerName()
298 {
299         return this->servername;
300 }
301
302 std::string ircd_connector::GetDescription()
303 {
304         return this->description;
305 }
306
307 void ircd_connector::SetServerName(std::string serv)
308 {
309         this->servername = serv;
310 }
311
312 void ircd_connector::SetDescription(std::string desc)
313 {
314         this->description = desc;
315 }
316
317
318 int ircd_connector::GetDescriptor()
319 {
320         return this->fd;
321 }
322
323 int ircd_connector::GetState()
324 {
325         return this->state;
326 }
327
328
329 void ircd_connector::SetState(int state)
330 {
331         this->state = state;
332         if (state == STATE_DISCONNECTED)
333         {
334                 NetSendMyRoutingTable();
335         }
336 }
337
338 void ircd_connector::CloseConnection()
339 {
340         int flags = fcntl(this->fd, F_GETFL, 0);
341         fcntl(this->fd, F_SETFL, flags ^ O_NONBLOCK);
342         close(this->fd);
343         flags = fcntl(this->fd, F_GETFL, 0);
344         fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
345 }
346
347 void ircd_connector::SetDescriptor(int fd)
348 {
349         this->fd = fd;
350 }
351
352 bool connection::SendPacket(char *message, const char* host)
353 {
354         if ((!message) || (!host))
355                 return true;
356
357         ircd_connector* cn = this->FindHost(host);
358         
359         if (!strchr(message,'\n'))
360         {
361                 strlcat(message,"\n",MAXBUF);
362         }
363
364         if (cn)
365         {
366                 log(DEBUG,"main: Connection::SendPacket() sent '%s' to %s",message,cn->GetServerName().c_str());
367                 
368                 if (cn->GetState() == STATE_DISCONNECTED)
369                 {
370                         log(DEBUG,"Main route to %s is down, seeking alternative",host);
371                         // fix: can only route one hop to avoid a loop
372                         if (strlcat(message,"R ",2))
373                         {
374                                 // this route is down, we must re-route the packet through an available point in the mesh.
375                                 for (int k = 0; k < this->connectors.size(); k++)
376                                 {
377                                         // search for another point in the mesh which can 'reach' where we want to go
378                                         for (int m = 0; m < this->connectors[k].routes.size(); m++)
379                                         {
380                                                 if (!strcasecmp(this->connectors[k].routes[m].c_str(),host))
381                                                 {
382                                                         log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
383                                                         char buffer[MAXBUF];
384                                                         snprintf(buffer,MAXBUF,"R %s %s",host,message);
385                                                         this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
386                                                         return true;
387                                                 }
388                                         }
389                                 }
390                         }
391                         char buffer[MAXBUF];
392                         snprintf(buffer,MAXBUF,"& %s",host);
393                         NetSendToAllExcept(host,buffer);
394                         log(DEBUG,"There are no routes to %s, we're gonna boot the server off!",host);
395                         DoSplit(host);
396                         return false;
397                 }
398
399                 // returns false if the packet could not be sent (e.g. target host down)
400                 if (send(cn->GetDescriptor(),message,strlen(message),0)<0)
401                 {
402                         log(DEBUG,"send() failed for Connection::SendPacket(): %s",strerror(errno));
403                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
404                         cn->CloseConnection();
405                         cn->SetState(STATE_DISCONNECTED);
406                         // retry the packet along a new route so either arrival OR failure are gauranteed (bugfix)
407                         return this->SendPacket(message,host);
408                 }
409                 return true;
410         }
411 }
412
413 // receives a packet from any where there is data waiting, first come, first served
414 // fills the message and host values with the host where the data came from.
415
416 bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
417 {
418         char data[32767];
419         memset(data, 0, 32767);
420         for (int i = 0; i < this->connectors.size(); i++)
421         {
422                 if (this->connectors[i].GetState() != STATE_DISCONNECTED)
423                 {
424                         // returns false if the packet could not be sent (e.g. target host down)
425                         int rcvsize = 0;
426                         rcvsize = recv(this->connectors[i].GetDescriptor(),data,32767,0);
427                         if (rcvsize == -1)
428                         {
429                                 if (errno != EAGAIN)
430                                 {
431                                         log(DEBUG,"recv() failed for Connection::RecvPacket(): %s",strerror(errno));
432                                         log(DEBUG,"Disabling connector: %s",this->connectors[i].GetServerName().c_str());
433                                         this->connectors[i].CloseConnection();
434                                         this->connectors[i].SetState(STATE_DISCONNECTED);
435                                 }
436                         }
437                         if (rcvsize > 0)
438                         {
439                                 char* l = strtok(data,"\n");
440                                 while (l)
441                                 {
442                                         char sanitized[32767];
443                                         memset(sanitized, 0, 32767);
444                                         int ptt = 0;
445                                         for (int pt = 0; pt < strlen(l); pt++)
446                                         {
447                                                 if (l[pt] != '\r')
448                                                 {
449                                                         sanitized[ptt++] = l[pt];
450                                                 }
451                                         }
452                                         sanitized[ptt] = '\0';
453                                         if (strlen(sanitized))
454                                         {
455                                                 messages.push_back(sanitized);
456                                                 strlcpy(host,this->connectors[i].GetServerName().c_str(),160);
457                                                 log(DEBUG,"main: Connection::RecvPacket() got '%s' from %s",sanitized,host);
458                                                 
459                                         }
460                                         l = strtok(NULL,"\n");
461                                 }
462                                 return true;
463                         }
464                 }
465         }
466         // nothing new yet -- message and host will be undefined
467         return false;
468 }
469
470 long connection::GenKey()
471 {
472         return (random()*time(NULL));
473 }
474