]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspsocket.cpp
There is absolutely no need to cache connect timeout.
[user/henk/code/inspircd.git] / src / inspsocket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core */
15
16 #include "socket.h"
17 #include "inspstring.h"
18 #include "socketengine.h"
19 #include "inspircd.h"
20
21 bool BufferedSocket::Readable()
22 {
23         return (this->state != I_CONNECTING);
24 }
25
26 BufferedSocket::BufferedSocket(InspIRCd* SI)
27 {
28         this->Timeout = NULL;
29         this->state = I_DISCONNECTED;
30         this->fd = -1;
31         this->Instance = SI;
32 }
33
34 BufferedSocket::BufferedSocket(InspIRCd* SI, int newfd, const char* ip)
35 {
36         this->Timeout = NULL;
37         this->fd = newfd;
38         this->state = I_CONNECTED;
39         strlcpy(this->IP,ip,MAXBUF);
40         this->Instance = SI;
41         if (this->fd > -1)
42                 this->Instance->SE->AddFd(this);
43 }
44
45 BufferedSocket::BufferedSocket(InspIRCd* SI, const std::string &ipaddr, int aport, unsigned int maxtime, const std::string &connectbindip)
46 {
47         this->cbindip = connectbindip;
48         this->fd = -1;
49         this->Instance = SI;
50         strlcpy(host,ipaddr.c_str(),MAXBUF);
51         this->Timeout = NULL;
52
53         strlcpy(this->host,ipaddr.c_str(),MAXBUF);
54         this->port = aport;
55
56         bool ipvalid = true;
57 #ifdef IPV6
58         if (strchr(host,':'))
59         {
60                 in6_addr n;
61                 if (inet_pton(AF_INET6, host, &n) < 1)
62                         ipvalid = false;
63         }
64         else
65 #endif
66         {
67                 in_addr n;
68                 if (inet_aton(host,&n) < 1)
69                         ipvalid = false;
70         }
71         if (!ipvalid)
72         {
73                 this->Instance->Logs->Log("SOCKET", DEBUG,"BUG: Hostname passed to BufferedSocket, rather than an IP address!");
74                 this->OnError(I_ERR_CONNECT);
75                 this->Close();
76                 this->fd = -1;
77                 this->state = I_ERROR;
78                 return;
79         }
80         else
81         {
82                 strlcpy(this->IP,host,MAXBUF);
83                 if (!this->DoConnect(maxtime))
84                 {
85                         this->OnError(I_ERR_CONNECT);
86                         this->Close();
87                         this->fd = -1;
88                         this->state = I_ERROR;
89                         return;
90                 }
91         }
92 }
93
94 void BufferedSocket::WantWrite()
95 {
96         this->Instance->SE->WantWrite(this);
97 }
98
99 void BufferedSocket::SetQueues(int nfd)
100 {
101         // attempt to increase socket sendq and recvq as high as its possible
102         int sendbuf = 32768;
103         int recvbuf = 32768;
104         if(setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const char *)&sendbuf,sizeof(sendbuf)) || setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const char *)&recvbuf,sizeof(sendbuf)))
105         {
106                 //this->Instance->Log(DEFAULT, "Could not increase SO_SNDBUF/SO_RCVBUF for socket %u", GetFd());
107                 ; // do nothing. I'm a little sick of people trying to interpret this message as a result of why their incorrect setups don't work.
108         }
109 }
110
111 /* Most irc servers require you to specify the ip you want to bind to.
112  * If you dont specify an IP, they rather dumbly bind to the first IP
113  * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP
114  * addresses we've bound server ports to, and we try and bind our outbound
115  * connections to the first usable non-loopback and non-any IP we find.
116  * This is easier to configure when you have a lot of links and a lot
117  * of servers to configure.
118  */
119 bool BufferedSocket::BindAddr(const std::string &ip)
120 {
121         ConfigReader Conf(this->Instance);
122         socklen_t size = sizeof(sockaddr_in);
123 #ifdef IPV6
124         bool v6 = false;
125         /* Are we looking for a binding to fit an ipv6 host? */
126         if ((ip.empty()) || (ip.find(':') != std::string::npos))
127                 v6 = true;
128 #endif
129         int j = 0;
130         while (j < Conf.Enumerate("bind") || (!ip.empty()))
131         {
132                 std::string sIP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip;
133                 if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers")
134                 {
135                         if (!ip.empty() || ((sIP != "*") && (sIP != "127.0.0.1") && (!sIP.empty()) && (sIP != "::1")))
136                         {
137                                 /* The [2] is required because we may write a sockaddr_in6 here, and sockaddr_in6 is larger than sockaddr, where sockaddr_in4 is not. */
138                                 sockaddr* s = new sockaddr[2];
139 #ifdef IPV6
140                                 if (v6)
141                                 {
142                                         in6_addr n;
143                                         if (inet_pton(AF_INET6, sIP.c_str(), &n) > 0)
144                                         {
145                                                 memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(sockaddr_in6));
146                                                 ((sockaddr_in6*)s)->sin6_port = 0;
147                                                 ((sockaddr_in6*)s)->sin6_family = AF_INET6;
148                                                 size = sizeof(sockaddr_in6);
149                                         }
150                                         else
151                                         {
152                                                 delete[] s;
153                                                 j++;
154                                                 continue;
155                                         }
156                                 }
157                                 else
158 #endif
159                                 {
160                                         in_addr n;
161                                         if (inet_aton(sIP.c_str(), &n) > 0)
162                                         {
163                                                 ((sockaddr_in*)s)->sin_addr = n;
164                                                 ((sockaddr_in*)s)->sin_port = 0;
165                                                 ((sockaddr_in*)s)->sin_family = AF_INET;
166                                         }
167                                         else
168                                         {
169                                                 delete[] s;
170                                                 j++;
171                                                 continue;
172                                         }
173                                 }
174
175                                 if (Instance->SE->Bind(this->fd, s, size) < 0)
176                                 {
177                                         this->state = I_ERROR;
178                                         this->OnError(I_ERR_BIND);
179                                         this->fd = -1;
180                                         delete[] s;
181                                         return false;
182                                 }
183
184                                 delete[] s;
185                                 return true;
186                         }
187                 }
188                 j++;
189         }
190         Instance->Logs->Log("SOCKET", DEBUG,"nothing in the config to bind()!");
191         return true;
192 }
193
194 bool BufferedSocket::DoConnect(unsigned long maxtime)
195 {
196         /* The [2] is required because we may write a sockaddr_in6 here, and sockaddr_in6 is larger than sockaddr, where sockaddr_in4 is not. */
197         sockaddr* addr = new sockaddr[2];
198         socklen_t size = sizeof(sockaddr_in);
199 #ifdef IPV6
200         bool v6 = false;
201         if ((!*this->host) || strchr(this->host, ':'))
202                 v6 = true;
203
204         if (v6)
205         {
206                 this->fd = socket(AF_INET6, SOCK_STREAM, 0);
207                 if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP)))
208                 {
209                         if (!this->BindAddr(this->cbindip))
210                         {
211                                 delete[] addr;
212                                 return false;
213                         }
214                 }
215         }
216         else
217 #endif
218         {
219                 this->fd = socket(AF_INET, SOCK_STREAM, 0);
220                 if (this->fd > -1)
221                 {
222                         if (!this->BindAddr(this->cbindip))
223                         {
224                                 delete[] addr;
225                                 return false;
226                         }
227                 }
228         }
229
230         if (this->fd == -1)
231         {
232                 this->state = I_ERROR;
233                 this->OnError(I_ERR_SOCKET);
234                 delete[] addr;
235                 return false;
236         }
237
238 #ifdef IPV6
239         if (v6)
240         {
241                 in6_addr addy;
242                 if (inet_pton(AF_INET6, this->host, &addy) > 0)
243                 {
244                         ((sockaddr_in6*)addr)->sin6_family = AF_INET6;
245                         memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy));
246                         ((sockaddr_in6*)addr)->sin6_port = htons(this->port);
247                         size = sizeof(sockaddr_in6);
248                 }
249         }
250         else
251 #endif
252         {
253                 in_addr addy;
254                 if (inet_aton(this->host, &addy) > 0)
255                 {
256                         ((sockaddr_in*)addr)->sin_family = AF_INET;
257                         ((sockaddr_in*)addr)->sin_addr = addy;
258                         ((sockaddr_in*)addr)->sin_port = htons(this->port);
259                 }
260         }
261
262         Instance->SE->NonBlocking(this->fd);
263
264         if (Instance->SE->Connect(this, (sockaddr*)addr, size) == -1)
265         {
266                 if (errno != EINPROGRESS)
267                 {
268                         this->OnError(I_ERR_CONNECT);
269                         this->Close();
270                         this->state = I_ERROR;
271                         return false;
272                 }
273
274                 this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, maxtime, this->Instance->Time());
275                 this->Instance->Timers->AddTimer(this->Timeout);
276         }
277
278         this->state = I_CONNECTING;
279         if (this->fd > -1)
280         {
281                 if (!this->Instance->SE->AddFd(this))
282                 {
283                         this->OnError(I_ERR_NOMOREFDS);
284                         this->Close();
285                         this->state = I_ERROR;
286                         return false;
287                 }
288                 this->SetQueues(this->fd);
289         }
290
291         Instance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
292         return true;
293 }
294
295
296 void BufferedSocket::Close()
297 {
298         /* Save this, so we dont lose it,
299          * otherise on failure, error messages
300          * might be inaccurate.
301          */
302         int save = errno;
303         if (this->fd > -1)
304         {
305                 if (this->GetIOHook())
306                 {
307                         try
308                         {
309                                 this->GetIOHook()->OnRawSocketClose(this->fd);
310                         }
311                         catch (CoreException& modexcept)
312                         {
313                                 Instance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
314                         }
315                 }
316                 Instance->SE->Shutdown(this, 2);
317                 if (Instance->SE->Close(this) != -1)
318                         this->OnClose();
319
320                 if (Instance->SocketCull.find(this) == Instance->SocketCull.end())
321                         Instance->SocketCull[this] = this;
322         }
323         errno = save;
324 }
325
326 std::string BufferedSocket::GetIP()
327 {
328         return this->IP;
329 }
330
331 const char* BufferedSocket::Read()
332 {
333         if (!Instance->SE->BoundsCheckFd(this))
334                 return NULL;
335
336         int n = 0;
337         char* ReadBuffer = Instance->GetReadBuffer();
338
339         if (this->GetIOHook())
340         {
341                 int result2 = 0;
342                 int MOD_RESULT = 0;
343                 try
344                 {
345                         MOD_RESULT = this->GetIOHook()->OnRawSocketRead(this->fd, ReadBuffer, Instance->Config->NetBufferSize, result2);
346                 }
347                 catch (CoreException& modexcept)
348                 {
349                         Instance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
350                 }
351                 if (MOD_RESULT < 0)
352                 {
353                         n = -1;
354                         errno = EAGAIN;
355                 }
356                 else
357                 {
358                         n = result2;
359                 }
360         }
361         else
362         {
363                 n = recv(this->fd, ReadBuffer, Instance->Config->NetBufferSize, 0);
364         }
365
366         /*
367          * This used to do some silly bounds checking instead of just passing bufsize - 1 to recv.
368          * Not only does that make absolutely no sense, but it could potentially result in a read buffer's worth
369          * of data being thrown into the bit bucket for no good reason, which is just *stupid*.. do things correctly now.
370          * --w00t (july 2, 2008)
371          */
372         if (n > 0)
373         {
374                 ReadBuffer[n] = 0;
375                 return ReadBuffer;
376         }
377         else
378         {
379                 int err = errno;
380                 if (err == EAGAIN)
381                         return "";
382                 else
383                         return NULL;
384         }
385 }
386
387 /*
388  * This function formerly tried to flush write buffer each call.
389  * While admirable in attempting to get the data out to wherever
390  * it is going, on a full socket, it's just going to syscall write() and
391  * EAGAIN constantly, instead of waiting in the SE to know if it can write
392  * which will chew a bit of CPU.
393  *
394  * So, now this function returns void (take note) and just adds to the sendq.
395  *
396  * It'll get written at a determinate point when the socketengine tells us it can write.
397  *              -- w00t (april 1, 2008)
398  */
399 void BufferedSocket::Write(const std::string &data)
400 {
401         /* Append the data to the back of the queue ready for writing */
402         outbuffer.push_back(data);
403
404         /* Mark ourselves as wanting write */
405         this->Instance->SE->WantWrite(this);
406 }
407
408 bool BufferedSocket::FlushWriteBuffer()
409 {
410         errno = 0;
411         if ((this->fd > -1) && (this->state == I_CONNECTED))
412         {
413                 if (this->GetIOHook())
414                 {
415                         while (outbuffer.size() && (errno != EAGAIN))
416                         {
417                                 try
418                                 {
419                                         /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
420                                          * implement their own buffering mechanisms
421                                          */
422                                         this->GetIOHook()->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length());
423                                         outbuffer.pop_front();
424                                 }
425                                 catch (CoreException& modexcept)
426                                 {
427                                         Instance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
428                                         return true;
429                                 }
430                         }
431                 }
432                 else
433                 {
434                         /* If we have multiple lines, try to send them all,
435                          * not just the first one -- Brain
436                          */
437                         while (outbuffer.size() && (errno != EAGAIN))
438                         {
439                                 /* Send a line */
440                                 int result = Instance->SE->Send(this, outbuffer[0].c_str(), outbuffer[0].length(), 0);
441
442                                 if (result > 0)
443                                 {
444                                         if ((unsigned int)result >= outbuffer[0].length())
445                                         {
446                                                 /* The whole block was written (usually a line)
447                                                  * Pop the block off the front of the queue,
448                                                  * dont set errno, because we are clear of errors
449                                                  * and want to try and write the next block too.
450                                                  */
451                                                 outbuffer.pop_front();
452                                         }
453                                         else
454                                         {
455                                                 std::string temp = outbuffer[0].substr(result);
456                                                 outbuffer[0] = temp;
457                                                 /* We didnt get the whole line out. arses.
458                                                  * Try again next time, i guess. Set errno,
459                                                  * because we shouldnt be writing any more now,
460                                                  * until the socketengine says its safe to do so.
461                                                  */
462                                                 errno = EAGAIN;
463                                         }
464                                 }
465                                 else if (result == 0)
466                                 {
467                                         this->Instance->SE->DelFd(this);
468                                         this->Close();
469                                         return true;
470                                 }
471                                 else if ((result == -1) && (errno != EAGAIN))
472                                 {
473                                         this->OnError(I_ERR_WRITE);
474                                         this->state = I_ERROR;
475                                         this->Instance->SE->DelFd(this);
476                                         this->Close();
477                                         return true;
478                                 }
479                         }
480                 }
481         }
482
483         if ((errno == EAGAIN) && (fd > -1))
484         {
485                 this->Instance->SE->WantWrite(this);
486         }
487
488         return (fd < 0);
489 }
490
491 void SocketTimeout::Tick(time_t)
492 {
493         ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
494
495         if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
496                 return;
497
498         if (this->sock->state == I_CONNECTING)
499         {
500                 // for connecting sockets, the timeout can occur
501                 // which causes termination of the connection after
502                 // the given number of seconds without a successful
503                 // connection.
504                 this->sock->OnTimeout();
505                 this->sock->OnError(I_ERR_TIMEOUT);
506
507                 /* NOTE: We must set this AFTER DelFd, as we added
508                  * this socket whilst writeable. This means that we
509                  * must DELETE the socket whilst writeable too!
510                  */
511                 this->sock->state = I_ERROR;
512
513                 if (ServerInstance->SocketCull.find(this->sock) == ServerInstance->SocketCull.end())
514                         ServerInstance->SocketCull[this->sock] = this->sock;
515         }
516
517         this->sock->Timeout = NULL;
518 }
519
520 bool BufferedSocket::Poll()
521 {
522 #ifndef WINDOWS
523         if (!Instance->SE->BoundsCheckFd(this))
524                 return false;
525 #endif
526
527         if (Instance->SE->GetRef(this->fd) != this)
528                 return false;
529
530         switch (this->state)
531         {
532                 case I_CONNECTING:
533                         /* Our socket was in write-state, so delete it and re-add it
534                          * in read-state.
535                          */
536 #ifndef WINDOWS
537                         if (this->fd > -1)
538                         {
539                                 this->Instance->SE->DelFd(this);
540                                 if (!this->Instance->SE->AddFd(this))
541                                         return false;
542                         }
543 #endif
544                         this->SetState(I_CONNECTED);
545
546                         if (this->GetIOHook())
547                         {
548                                 Instance->Logs->Log("SOCKET",DEBUG,"Hook for raw connect");
549                                 try
550                                 {
551                                         this->GetIOHook()->OnRawSocketConnect(this->fd);
552                                 }
553                                 catch (CoreException& modexcept)
554                                 {
555                                         Instance->Logs->Log("SOCKET",DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
556                                 }
557                         }
558                         return this->OnConnected();
559                 break;
560                 case I_CONNECTED:
561                         /* Process the read event */
562                         return this->OnDataReady();
563                 break;
564                 default:
565                 break;
566         }
567         return true;
568 }
569
570 void BufferedSocket::SetState(BufferedSocketState s)
571 {
572         this->state = s;
573 }
574
575 BufferedSocketState BufferedSocket::GetState()
576 {
577         return this->state;
578 }
579
580 bool BufferedSocket::OnConnected() { return true; }
581 void BufferedSocket::OnError(BufferedSocketError) { return; }
582 int BufferedSocket::OnDisconnect() { return 0; }
583 bool BufferedSocket::OnDataReady() { return true; }
584 bool BufferedSocket::OnWriteReady()
585 {
586         // Default behaviour: just try write some.
587         return !this->FlushWriteBuffer();
588 }
589 void BufferedSocket::OnTimeout() { return; }
590 void BufferedSocket::OnClose() { return; }
591
592 BufferedSocket::~BufferedSocket()
593 {
594         this->Close();
595         if (Timeout)
596         {
597                 Instance->Timers->DelTimer(Timeout);
598                 Timeout = NULL;
599         }
600 }
601
602 void BufferedSocket::HandleEvent(EventType et, int errornum)
603 {
604         switch (et)
605         {
606                 case EVENT_ERROR:
607                         switch (errornum)
608                         {
609                                 case ETIMEDOUT:
610                                         this->OnError(I_ERR_TIMEOUT);
611                                 break;
612                                 case ECONNREFUSED:
613                                 case 0:
614                                         this->OnError(this->state == I_CONNECTING ? I_ERR_CONNECT : I_ERR_WRITE);
615                                 break;
616                                 case EADDRINUSE:
617                                         this->OnError(I_ERR_BIND);
618                                 break;
619                                 case EPIPE:
620                                 case EIO:
621                                         this->OnError(I_ERR_WRITE);
622                                 break;
623                         }
624                         if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
625                                 this->Instance->SocketCull[this] = this;
626                         return;
627                 break;
628                 case EVENT_READ:
629                         if (!this->Poll())
630                         {
631                                 if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
632                                         this->Instance->SocketCull[this] = this;
633                                 return;
634                         }
635                 break;
636                 case EVENT_WRITE:
637                         if (this->state == I_CONNECTING)
638                         {
639                                 /* This might look wrong as if we should be actually calling
640                                  * with EVENT_WRITE, but trust me it is correct. There are some
641                                  * writeability-state things in the read code, because of how
642                                  * BufferedSocket used to work regarding write buffering in previous
643                                  * versions of InspIRCd. - Brain
644                                  */
645                                 this->HandleEvent(EVENT_READ);
646                                 return;
647                         }
648                         else
649                         {
650                                 if (!this->OnWriteReady())
651                                 {
652                                         if (this->Instance->SocketCull.find(this) == this->Instance->SocketCull.end())
653                                                 this->Instance->SocketCull[this] = this;
654                                         return;
655                                 }
656                         }
657                 break;
658         }
659 }
660