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