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