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