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