]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/connection.cpp
ConfigReader fixes to cope with tab characters (why didnt we notice this before?)
[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         ircd_connector* cn = this->FindHost(host);
338         
339         if (!strchr(message,'\n'))
340         {
341                 strncat(message,"\n",MAXBUF);
342         }
343
344         if (cn)
345         {
346                 log(DEBUG,"main: Connection::SendPacket() sent '%s' to %s",message,cn->GetServerName().c_str());
347                 
348                 if (cn->GetState() == STATE_DISCONNECTED)
349                 {
350                         log(DEBUG,"Main route to %s is down, seeking alternative",host);
351                         // fix: can only route one hop to avoid a loop
352                         if (strncat(message,"R ",2))
353                         {
354                                 // this route is down, we must re-route the packet through an available point in the mesh.
355                                 for (int k = 0; k < this->connectors.size(); k++)
356                                 {
357                                         // search for another point in the mesh which can 'reach' where we want to go
358                                         for (int m = 0; m < this->connectors[k].routes.size(); m++)
359                                         {
360                                                 if (!strcasecmp(this->connectors[k].routes[m].c_str(),host))
361                                                 {
362                                                         log(DEBUG,"Found alternative route for packet: %s",this->connectors[k].GetServerName().c_str());
363                                                         char buffer[MAXBUF];
364                                                         snprintf(buffer,MAXBUF,"R %s %s",host,message);
365                                                         this->SendPacket(buffer,this->connectors[k].GetServerName().c_str());
366                                                         return true;
367                                                 }
368                                         }
369                                 }
370                         }
371                         char buffer[MAXBUF];
372                         snprintf(buffer,MAXBUF,"& %s",host);
373                         NetSendToAllExcept(host,buffer);
374                         log(DEBUG,"There are no routes to %s, we're gonna boot the server off!",host);
375                         DoSplit(host);
376                         return false;
377                 }
378
379                 // returns false if the packet could not be sent (e.g. target host down)
380                 if (send(cn->GetDescriptor(),message,strlen(message),0)<0)
381                 {
382                         log(DEBUG,"send() failed for Connection::SendPacket(): %s",strerror(errno));
383                         log(DEBUG,"Disabling connector: %s",cn->GetServerName().c_str());
384                         cn->CloseConnection();
385                         cn->SetState(STATE_DISCONNECTED);
386                         // retry the packet along a new route so either arrival OR failure are gauranteed (bugfix)
387                         return this->SendPacket(message,host);
388                 }
389                 return true;
390         }
391 }
392
393 // receives a packet from any where there is data waiting, first come, first served
394 // fills the message and host values with the host where the data came from.
395
396 bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
397 {
398         char data[32767];
399         memset(data, 0, 32767);
400         for (int i = 0; i < this->connectors.size(); i++)
401         {
402                 if (this->connectors[i].GetState() != STATE_DISCONNECTED)
403                 {
404                         // returns false if the packet could not be sent (e.g. target host down)
405                         int rcvsize = 0;
406                         rcvsize = recv(this->connectors[i].GetDescriptor(),data,32767,0);
407                         if (rcvsize == -1)
408                         {
409                                 if (errno != EAGAIN)
410                                 {
411                                         log(DEBUG,"recv() failed for Connection::RecvPacket(): %s",strerror(errno));
412                                         log(DEBUG,"Disabling connector: %s",this->connectors[i].GetServerName().c_str());
413                                         this->connectors[i].CloseConnection();
414                                         this->connectors[i].SetState(STATE_DISCONNECTED);
415                                 }
416                         }
417                         if (rcvsize > 0)
418                         {
419                                 char* l = strtok(data,"\n");
420                                 while (l)
421                                 {
422                                         char sanitized[32767];
423                                         memset(sanitized, 0, 32767);
424                                         int ptt = 0;
425                                         for (int pt = 0; pt < strlen(l); pt++)
426                                         {
427                                                 if (l[pt] != '\r')
428                                                 {
429                                                         sanitized[ptt++] = l[pt];
430                                                 }
431                                         }
432                                         sanitized[ptt] = '\0';
433                                         if (strlen(sanitized))
434                                         {
435                                                 messages.push_back(sanitized);
436                                                 strncpy(host,this->connectors[i].GetServerName().c_str(),160);
437                                                 log(DEBUG,"main: Connection::RecvPacket() got '%s' from %s",sanitized,host);
438                                                 
439                                         }
440                                         l = strtok(NULL,"\n");
441                                 }
442                                 return true;
443                         }
444                 }
445         }
446         // nothing new yet -- message and host will be undefined
447         return false;
448 }
449
450 long connection::GenKey()
451 {
452         srand(time(NULL));
453         return (random()*time(NULL));
454 }
455