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