]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/connection.cpp
c6aad8c65e917eba2113c19bc751dc043d8d8619
[user/henk/code/inspircd.git] / src / connection.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd.h"
20 #include "connection.h"
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <poll.h>
24 #include <sys/errno.h>
25 #include <sys/ioctl.h>
26 #include <sys/utsname.h>
27 #include <vector>
28 #include <string>
29 #include <deque>
30 #include <sstream>
31 #include "modules.h"
32 #include "inspstring.h"
33 #include "helperfuncs.h"
34
35 extern bool has_been_netsplit;
36 extern std::vector<Module*> modules;
37 extern std::vector<ircd_module*> factory;
38
39 extern int MODCOUNT;
40
41 extern time_t TIME;
42
43
44 /**
45  * The InspIRCd mesh network is maintained by a tree of objects which reference *themselves*.
46  * Every local server has an array of 32 *serverrecs, known as me[]. Each of these represents
47  * a local listening port, and is not null if the user has opened a listening port on the server.
48  * It is assumed nobody will ever want to open more than 32 listening server ports at any one
49  * time (i mean come on, why would you want more, the ircd works fine with ONE).
50  * Each me[] entry has multiple classes within it of type ircd_connector. These are stored in a vector
51  * and each represents a server linked via this socket. If the connection was created outbound,
52  * the connection is bound to the default ip address by using me[defaultRoute] (defaultRoute being
53  * a global variable which indicates the default server to make connections on). If the connection
54  * was created inbound, it is attached to the port the connection came in on. There may be as many
55  * ircd_connector objects as needed in each me[] entry. Each ircd_connector implements the specifics
56  * of an ircd connection in the mesh, however each ircd may have multiple ircd_connector connections
57  * to it, to maintain the mesh link.
58  */
59
60 char* xsumtable = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
61
62 // creates a random id for a line for detection of duplicate messages
63 std::string CreateSum()
64 {
65         char sum[9];
66         sum[0] = ':';
67         sum[8] = '\0';
68         for(int q = 1; q < 8; q++)
69                 sum[q] = xsumtable[rand()%52];
70         return sum;
71 }
72
73 connection::connection()
74 {
75         fd = -1;
76 }
77
78
79 ircd_connector::ircd_connector()
80 {
81         fd = -1;
82         port = 0;
83         sendq = "";
84         WriteError = "";
85         nextping = TIME+120;
86         replied = false;
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* newhost, int newport)
100 {
101         strncpy(this->host,newhost,160);
102         this->port = newport;
103         return true;
104 }
105
106 bool ircd_connector::SetHostAddress(char* newhost, int newport)
107 {
108         strncpy(this->host,newhost,160);
109         this->port = newport;
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::AddBuffer(std::string a)
123 {
124         std::string b = "";
125         for (unsigned int i = 0; i < a.length(); i++)
126                 if (a[i] != '\r')
127                         b = b + a[i];
128
129         std::stringstream stream(ircdbuffer);
130         stream << b;
131         log(DEBUG,"AddBuffer: %s",b.c_str());
132         ircdbuffer = stream.str();
133         return (ircdbuffer.length() < 1048576);
134 }
135
136 bool ircd_connector::BufferIsComplete()
137 {
138         for (unsigned int i = 0; i < ircdbuffer.length(); i++)
139                 if (ircdbuffer[i] == '\n')
140                         return true;
141         return false;
142 }
143
144 void ircd_connector::ClearBuffer()
145 {
146         ircdbuffer = "";
147 }
148
149 std::string ircd_connector::GetBuffer()
150 {
151         // Fix by Brain 28th Apr 2005
152         // seems my stringstream code isnt liked by linux
153         // EVEN THOUGH IT IS CORRECT! Fixed by using a different
154         // (SLOWER) algorithm...
155         char* line = (char*)ircdbuffer.c_str();
156         std::string ret = "";
157         while ((*line != '\n') && (strlen(line)))
158         {
159                 ret = ret + *line;
160                 line++;
161         }
162         if ((*line == '\n') || (*line == '\r'))
163                 line++;
164         ircdbuffer = line;
165         return ret;
166 }
167
168 bool ircd_connector::AddWriteBuf(std::string data)
169 {
170         log(DEBUG,"connector::AddWriteBuf(%s)",data.c_str());
171         if (this->GetWriteError() != "")
172                 return false;
173         if (this->GetState() == STATE_DISCONNECTED)
174                 return false;
175         std::stringstream stream;
176         stream << sendq << data;
177         sendq = stream.str();
178         return (sendq.length() < 1048576);
179 }
180
181 bool ircd_connector::HasBufferedOutput()
182 {
183         return (sendq.length() > 0);
184 }
185
186 bool ircd_connector::CheckPing()
187 {
188         if (TIME > this->nextping)
189         {
190                 if (this->replied)
191                 {
192                         this->AddWriteBuf("?\n");
193                         this->nextping = TIME+120;
194                         this->replied = false;
195                         return true;
196                 }
197                 else
198                 {
199                         if (this->GetState() == STATE_CONNECTED)
200                         {
201                                 this->SetWriteError("Ping timeout");
202                                 this->CloseConnection();
203                                 this->SetState(STATE_DISCONNECTED);
204                                 WriteOpers("*** Ping timeout on link to %s (more routes may remain)",this->GetServerName().c_str());
205                                 has_been_netsplit = true;
206                                 return false;
207                         }
208                 }
209         }
210         return true;
211 }
212
213 void ircd_connector::ResetPing()
214 {
215         log(DEBUG,"Reset ping counter");
216         this->replied = true;
217         this->nextping = TIME+120;
218 }
219
220 // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
221 bool ircd_connector::FlushWriteBuf()
222 {
223         if ((this->GetState() == STATE_NOAUTH_OUTBOUND) || (this->GetState() == STATE_COOKIE_OUTBOUND))
224         {
225                 // if the outbound socket hasnt connected yet... return true and don't
226                 // actually do anything until it IS connected. This should probably
227                 // have a timeout somewhere, 10 secs should suffice. ;-)
228                 pollfd polls;
229                 polls.fd = this->fd;
230                 polls.events = POLLOUT;
231                 int ret = poll(&polls,1,1);
232                 if (ret < 1)
233                         return true;
234                 // this falls through and sends any waiting data, which can put it into the
235                 // connected state.
236                 if (this->GetState() == STATE_COOKIE_OUTBOUND)
237                 {
238                         log(DEBUG,"Moving cookie_outbound into STATE_CONNECTED state");
239                         this->SetState(STATE_CONNECTED);
240                         for (int t = 0; t < 32; t++) if (me[t]) for (unsigned int l = 0; l < me[t]->connectors.size(); l++)
241                         {
242                                 if (me[t]->connectors[l].GetDescription() != "")
243                                 {
244                                         snprintf(buffer,MAXBUF,"%s = %s :%s\r\n",CreateSum().c_str(),me[t]->connectors[l].GetServerName().c_str(),me[t]->connectors[l].GetDescription().c_str());
245                                         this->AddWriteBuf(buffer);
246                                 }
247                         }
248                 }
249                 snprintf(buffer,MAXBUF,"%s v %s %s\r\n",CreateSum().c_str(),ServerName,GetVersionString().c_str());
250                 this->AddWriteBuf(buffer);
251         }
252         if ((sendq.length()) && (this->GetState() != STATE_DISCONNECTED))
253         {
254                 char* tb = (char*)this->sendq.c_str();
255                 int n_sent = write(this->fd,tb,this->sendq.length());
256                 if (n_sent != 0)
257                 {
258                         if (n_sent == -1)
259                         {
260                                 this->SetWriteError(strerror(errno));
261                                 return false;
262                         }
263                         else
264                         {
265                                 log(DEBUG,"Wrote %d chars to socket",n_sent);
266                                 // advance the queue
267                                 tb += n_sent;
268                                 this->sendq = tb;
269                                 return true;
270                         }
271                 }
272         }
273         return true;
274 }
275
276 void ircd_connector::SetWriteError(std::string error)
277 {
278         if (this->WriteError == "")
279                 this->WriteError = error;
280 }
281
282 std::string ircd_connector::GetWriteError()
283 {
284         return this->WriteError;
285 }
286
287
288 bool ircd_connector::MakeOutboundConnection(char* newhost, int newport)
289 {
290         log(DEBUG,"MakeOutboundConnection: Original param: %s",newhost);
291         ClearBuffer();
292         hostent* hoste = gethostbyname(newhost);
293         if (!hoste)
294         {
295                 log(DEBUG,"MakeOutboundConnection: gethostbyname was NULL, setting %s",newhost);
296                 this->SetHostAddress(newhost,newport);
297                 SetHostAndPort(newhost,newport);
298         }
299         else
300         {
301                 struct in_addr* ia = (in_addr*)hoste->h_addr;
302                 log(DEBUG,"MakeOutboundConnection: gethostbyname was valid, setting %s",inet_ntoa(*ia));
303                 this->SetHostAddress(inet_ntoa(*ia),newport);
304                 SetHostAndPort(inet_ntoa(*ia),newport);
305         }
306
307         this->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
308         if (this->fd >= 0)
309         {
310                 int flags = fcntl(this->fd, F_GETFL, 0);
311                 fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
312                 if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)) == -1)
313                 {
314                         if (errno != EINPROGRESS)
315                         {
316                                 WriteOpers("connect() failed for %s",host);
317                                 RemoveServer(this->servername.c_str());
318                                 return false;
319                         }
320                 }
321                 int sendbuf = 32768;
322                 int recvbuf = 32768;
323                 setsockopt(this->fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
324                 setsockopt(this->fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
325                 return true;
326         }
327         else
328         {
329                 WriteOpers("socket() failed!");
330                 RemoveServer(this->servername.c_str());
331         }
332
333         return false;
334 }
335
336
337 void ircd_connector::SetVersionString(std::string newversion)
338 {
339         log(DEBUG,"Set version of %s to %s",this->servername.c_str(),newversion.c_str());
340         this->version = newversion;
341 }
342
343 std::string ircd_connector::GetVersionString()
344 {
345         return this->version;
346 }
347
348
349 std::string ircd_connector::GetServerName()
350 {
351         return this->servername;
352 }
353
354 std::string ircd_connector::GetDescription()
355 {
356         return this->description;
357 }
358
359 void ircd_connector::SetServerName(std::string serv)
360 {
361         this->servername = serv;
362 }
363
364 void ircd_connector::SetDescription(std::string desc)
365 {
366         this->description = desc;
367 }
368
369
370 int ircd_connector::GetDescriptor()
371 {
372         return this->fd;
373 }
374
375 int ircd_connector::GetState()
376 {
377         return this->state;
378 }
379
380
381 void ircd_connector::SetState(int newstate)
382 {
383         this->state = newstate;
384         if (state == STATE_DISCONNECTED)
385         {
386                 NetSendMyRoutingTable();
387         }
388 }
389
390 void ircd_connector::CloseConnection()
391 {
392         log(DEBUG,"Closing connection");
393         // flush the queues
394         this->sendq = "";
395         this->ircdbuffer = "";
396         shutdown(this->fd,2);
397         close(this->fd);
398 }
399
400 void ircd_connector::SetDescriptor(int newfd)
401 {
402         this->fd = newfd;
403 }