]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspsocket.cpp
Don't send SYNTAX to clients which havent registered fully yet. Nothing says we cant...
[user/henk/code/inspircd.git] / src / inspsocket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 #include "socket.h"
15 #include "configreader.h"
16 #include "inspstring.h"
17 #include "socketengine.h"
18 #include "inspircd.h"
19
20 using irc::sockets::OpenTCPSocket;
21
22 bool InspSocket::Readable()
23 {
24         return ((this->state != I_CONNECTING) && (this->WaitingForWriteEvent == false));
25 }
26
27 InspSocket::InspSocket(InspIRCd* SI)
28 {
29         this->Timeout = NULL;
30         this->state = I_DISCONNECTED;
31         this->fd = -1;
32         this->WaitingForWriteEvent = false;
33         this->Instance = SI;
34         this->IsIOHooked = false;
35 }
36
37 InspSocket::InspSocket(InspIRCd* SI, int newfd, const char* ip)
38 {
39         this->Timeout = NULL;
40         this->fd = newfd;
41         this->state = I_CONNECTED;
42         strlcpy(this->IP,ip,MAXBUF);
43         this->WaitingForWriteEvent = false;
44         this->Instance = SI;
45         this->IsIOHooked = false;
46         if (this->fd > -1)
47                 this->Instance->SE->AddFd(this);
48 }
49
50 InspSocket::InspSocket(InspIRCd* SI, const std::string &ipaddr, int aport, bool listening, unsigned long maxtime, const std::string &connectbindip)
51 {
52         this->cbindip = connectbindip;
53         this->fd = -1;
54         this->Instance = SI;
55         strlcpy(host,ipaddr.c_str(),MAXBUF);
56         this->WaitingForWriteEvent = false;
57         this->IsIOHooked = false;
58         this->Timeout = NULL;
59         if (listening)
60         {
61                 if ((this->fd = OpenTCPSocket(host)) == ERROR)
62                 {
63                         this->fd = -1;
64                         this->state = I_ERROR;
65                         this->OnError(I_ERR_SOCKET);
66                         return;
67                 }
68                 else
69                 {
70                         if (!SI->BindSocket(this->fd,aport,(char*)ipaddr.c_str()))
71                         {
72                                 this->Close();
73                                 this->fd = -1;
74                                 this->state = I_ERROR;
75                                 this->OnError(I_ERR_BIND);
76                                 this->ClosePending = true;
77                                 return;
78                         }
79                         else
80                         {
81                                 this->state = I_LISTENING;
82                                 if (this->fd > -1)
83                                 {
84                                         if (!this->Instance->SE->AddFd(this))
85                                         {
86                                                 this->Close();
87                                                 this->state = I_ERROR;
88                                                 this->OnError(I_ERR_NOMOREFDS);
89                                         }
90                                 }
91                                 return;
92                         }
93                 }
94         }
95         else
96         {
97                 strlcpy(this->host,ipaddr.c_str(),MAXBUF);
98                 this->port = aport;
99
100                 bool ipvalid = true;
101 #ifdef IPV6
102                 if (strchr(host,':'))
103                 {
104                         in6_addr n;
105                         if (inet_pton(AF_INET6, host, &n) < 1)
106                                 ipvalid = false;
107                 }
108                 else
109                 {
110                         in_addr n;
111                         if (inet_aton(host,&n) < 1)
112                                 ipvalid = false;
113                 }
114 #else
115                 in_addr n;
116                 if (inet_aton(host,&n) < 1)
117                         ipvalid = false;
118 #endif
119                 if (!ipvalid)
120                 {
121                         this->Instance->Log(DEBUG,"BUG: Hostname passed to InspSocket, rather than an IP address!");
122                         this->Close();
123                         this->fd = -1;
124                         this->state = I_ERROR;
125                         this->OnError(I_ERR_RESOLVE);
126                         return;
127                 }
128                 else
129                 {
130                         strlcpy(this->IP,host,MAXBUF);
131                         timeout_val = maxtime;
132                         if (!this->DoConnect())
133                         {
134                                 this->Close();
135                                 this->fd = -1;
136                                 this->state = I_ERROR;
137                                 this->OnError(I_ERR_CONNECT);
138                                 return;
139                         }
140                 }
141         }
142 }
143
144 void InspSocket::WantWrite()
145 {
146         this->Instance->SE->WantWrite(this);
147         this->WaitingForWriteEvent = true;
148 }
149
150 void InspSocket::SetQueues(int nfd)
151 {
152         // attempt to increase socket sendq and recvq as high as its possible
153         int sendbuf = 32768;
154         int recvbuf = 32768;
155         setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
156         setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
157 }
158
159 /* Most irc servers require you to specify the ip you want to bind to.
160  * If you dont specify an IP, they rather dumbly bind to the first IP
161  * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP
162  * addresses we've bound server ports to, and we try and bind our outbound
163  * connections to the first usable non-loopback and non-any IP we find.
164  * This is easier to configure when you have a lot of links and a lot
165  * of servers to configure.
166  */
167 bool InspSocket::BindAddr(const std::string &ip)
168 {
169         ConfigReader Conf(this->Instance);
170         socklen_t size = sizeof(sockaddr_in);
171 #ifdef IPV6
172         bool v6 = false;
173         /* Are we looking for a binding to fit an ipv6 host? */
174         if ((ip.empty()) || (ip.find(':') != std::string::npos))
175                 v6 = true;
176 #endif
177         int j = 0;
178         while (j < Conf.Enumerate("bind") || (!ip.empty()))
179         {
180                 std::string IP = ip.empty() ? Conf.ReadValue("bind","address",j) : ip;
181                 if (!ip.empty() || Conf.ReadValue("bind","type",j) == "servers")
182                 {
183                         if (!ip.empty() || ((IP != "*") && (IP != "127.0.0.1") && (IP != "") && (IP != "::1")))
184                         {
185                                 sockaddr* s = new sockaddr[2];
186 #ifdef IPV6
187                                 if (v6)
188                                 {
189                                         in6_addr n;
190                                         if (inet_pton(AF_INET6, IP.c_str(), &n) > 0)
191                                         {
192                                                 memcpy(&((sockaddr_in6*)s)->sin6_addr, &n, sizeof(n));
193                                                 ((sockaddr_in6*)s)->sin6_family = AF_INET6;
194                                                 size = sizeof(sockaddr_in6);
195                                         }
196                                         else
197                                         {
198                                                 delete[] s;
199                                                 j++;
200                                                 continue;
201                                         }
202                                 }
203                                 else
204                                 {
205                                         in_addr n;
206                                         if (inet_aton(IP.c_str(), &n) > 0)
207                                         {
208                                                 ((sockaddr_in*)s)->sin_addr = n;
209                                                 ((sockaddr_in*)s)->sin_family = AF_INET;
210                                         }
211                                         else
212                                         {
213                                                 delete[] s;
214                                                 j++;
215                                                 continue;
216                                         }
217                                 }
218 #else
219                                 in_addr n;
220                                 if (insp_aton(IP.c_str(), &n) > 0)
221                                 {
222                                         ((sockaddr_in*)s)->sin_addr = n;
223                                         ((sockaddr_in*)s)->sin_family = AF_INET;
224                                 }
225                                 else
226                                 {
227                                         delete[] s;
228                                         j++;
229                                         continue;
230                                 }
231 #endif
232
233                                 if (bind(this->fd, s, size) < 0)
234                                 {
235                                         this->state = I_ERROR;
236                                         this->OnError(I_ERR_BIND);
237                                         this->fd = -1;
238                                         delete[] s;
239                                         return false;
240                                 }
241
242                                 delete[] s;
243                                 return true;
244                         }
245                 }
246                 j++;
247         }
248         return true;
249 }
250
251 bool InspSocket::DoConnect()
252 {
253         sockaddr* addr = new sockaddr[2];
254         socklen_t size = sizeof(sockaddr_in);
255 #ifdef IPV6
256         bool v6 = false;
257         if ((!*this->host) || strchr(this->host, ':'))
258                 v6 = true;
259
260         if (v6)
261         {
262                 this->fd = socket(AF_INET6, SOCK_STREAM, 0);
263                 if ((this->fd > -1) && ((strstr(this->IP,"::ffff:") != (char*)&this->IP) && (strstr(this->IP,"::FFFF:") != (char*)&this->IP)))
264                 {
265                         if (!this->BindAddr(this->cbindip))
266                         {
267                                 delete[] addr;
268                                 return false;
269                         }
270                 }
271         }
272         else
273         {
274                 this->fd = socket(AF_INET, SOCK_STREAM, 0);
275                 if (this->fd > -1)
276                 {
277                         if (!this->BindAddr(this->cbindip))
278                         {
279                                 delete[] addr;
280                                 return false;
281                         }
282                 }
283         }
284 #else
285         this->fd = socket(AF_INET, SOCK_STREAM, 0);
286         if (this->fd > -1)
287         {
288                 if (!this->BindAddr(this->cbindip))
289                 {
290                         delete[] addr;
291                         return false;
292                 }
293         }
294 #endif
295
296         if (this->fd == -1)
297         {
298                 this->state = I_ERROR;
299                 this->OnError(I_ERR_SOCKET);
300                 delete[] addr;
301                 return false;
302         }
303
304 #ifdef IPV6
305         if (v6)
306         {
307                 in6_addr addy;
308                 if (inet_pton(AF_INET6, this->host, &addy) > 0)
309                 {
310                         ((sockaddr_in6*)addr)->sin6_family = AF_INET6;
311                         memcpy(&((sockaddr_in6*)addr)->sin6_addr, &addy, sizeof(addy));
312                         ((sockaddr_in6*)addr)->sin6_port = htons(this->port);
313                         size = sizeof(sockaddr_in6);
314                 }
315         }
316         else
317         {
318                 in_addr addy;
319                 if (inet_aton(this->host, &addy) > 0)
320                 {
321                         ((sockaddr_in*)addr)->sin_family = AF_INET;
322                         ((sockaddr_in*)addr)->sin_addr = addy;
323                         ((sockaddr_in*)addr)->sin_port = htons(this->port);
324                 }
325         }
326 #else
327         in_addr addy;
328         if (inet_aton(this->host, &addy) > 0)
329         {
330                 ((sockaddr_in*)addr)->sin_family = AF_INET;
331                 ((sockaddr_in*)addr)->sin_addr = addy;
332                 ((sockaddr_in*)addr)->sin_port = htons(this->port);
333         }
334 #endif
335
336         int flags = fcntl(this->fd, F_GETFL, 0);
337         fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
338
339         if (connect(this->fd, (sockaddr*)addr, size) == -1)
340         {
341                 if (errno != EINPROGRESS)
342                 {
343                         this->OnError(I_ERR_CONNECT);
344                         this->Close();
345                         this->state = I_ERROR;
346                         return false;
347                 }
348
349                 this->Timeout = new SocketTimeout(this->GetFd(), this->Instance, this, timeout_val, this->Instance->Time());
350                 this->Instance->Timers->AddTimer(this->Timeout);
351         }
352         this->state = I_CONNECTING;
353         if (this->fd > -1)
354         {
355                 if (!this->Instance->SE->AddFd(this))
356                 {
357                         this->OnError(I_ERR_NOMOREFDS);
358                         this->Close();
359                         this->state = I_ERROR;
360                         return false;
361                 }
362                 this->SetQueues(this->fd);
363         }
364         return true;
365 }
366
367
368 void InspSocket::Close()
369 {
370         /* Save this, so we dont lose it,
371          * otherise on failure, error messages
372          * might be inaccurate.
373          */
374         int save = errno;
375         if (this->fd > -1)
376         {
377                 if (this->IsIOHooked && Instance->Config->GetIOHook(this))
378                 {
379                         try
380                         {
381                                 Instance->Config->GetIOHook(this)->OnRawSocketClose(this->fd);
382                         }
383                         catch (CoreException& modexcept)
384                         {
385                                 Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
386                         }
387                 }
388                 this->OnClose();
389                 shutdown(this->fd,2);
390                 close(this->fd);
391         }
392         errno = save;
393 }
394
395 std::string InspSocket::GetIP()
396 {
397         return this->IP;
398 }
399
400 char* InspSocket::Read()
401 {
402         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
403                 return NULL;
404
405         int n = 0;
406
407         if (this->IsIOHooked)
408         {
409                 int result2 = 0;
410                 int MOD_RESULT = 0;
411                 try
412                 {
413                         MOD_RESULT = Instance->Config->GetIOHook(this)->OnRawSocketRead(this->fd,this->ibuf,sizeof(this->ibuf),result2);
414                 }
415                 catch (CoreException& modexcept)
416                 {
417                         Instance->Log(DEFAULT,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
418                 }
419                 if (MOD_RESULT < 0)
420                 {
421                         n = -1;
422                         errno = EAGAIN;
423                 }
424                 else
425                 {
426                         n = result2;
427                 }
428         }
429         else
430         {
431                 n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0);
432         }
433
434         if ((n > 0) && (n <= (int)sizeof(this->ibuf)))
435         {
436                 ibuf[n] = 0;
437                 return ibuf;
438         }
439         else
440         {
441                 int err = errno;
442                 if (err == EAGAIN)
443                         return "";
444                 else
445                         return NULL;
446         }
447 }
448
449 void InspSocket::MarkAsClosed()
450 {
451 }
452
453 // There are two possible outcomes to this function.
454 // It will either write all of the data, or an undefined amount.
455 // If an undefined amount is written the connection has failed
456 // and should be aborted.
457 int InspSocket::Write(const std::string &data)
458 {
459         /* Try and append the data to the back of the queue, and send it on its way
460          */
461         outbuffer.push_back(data);
462         this->Instance->SE->WantWrite(this);
463         return (!this->FlushWriteBuffer());
464 }
465
466 bool InspSocket::FlushWriteBuffer()
467 {
468         errno = 0;
469         if ((this->fd > -1) && (this->state == I_CONNECTED))
470         {
471                 if (this->IsIOHooked)
472                 {
473                         while (outbuffer.size() && (errno != EAGAIN))
474                         {
475                                 try
476                                 {
477                                         int result = Instance->Config->GetIOHook(this)->OnRawSocketWrite(this->fd, outbuffer[0].c_str(), outbuffer[0].length());
478                                         if (result > 0)
479                                         {
480                                                 if ((unsigned int)result >= outbuffer[0].length())
481                                                 {
482                                                         outbuffer.pop_front();
483                                                 }
484                                                 else
485                                                 {
486                                                         std::string temp = outbuffer[0].substr(result);
487                                                         outbuffer[0] = temp;
488                                                         errno = EAGAIN;
489                                                 }
490                                         }
491                                         else if (((result == -1) && (errno != EAGAIN)) || (result == 0))
492                                         {
493                                                 this->OnError(I_ERR_WRITE);
494                                                 this->state = I_ERROR;
495                                                 this->Instance->SE->DelFd(this);
496                                                 this->Close();
497                                                 return true;
498                                         }
499                                 }
500                                 catch (CoreException& modexcept)
501                                 {
502                                         Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
503                                         return true;
504                                 }
505                         }
506                 }
507                 else
508                 {
509                         /* If we have multiple lines, try to send them all,
510                          * not just the first one -- Brain
511                          */
512                         while (outbuffer.size() && (errno != EAGAIN))
513                         {
514                                 /* Send a line */
515                                 int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length());
516                                 if (result > 0)
517                                 {
518                                         if ((unsigned int)result >= outbuffer[0].length())
519                                         {
520                                                 /* The whole block was written (usually a line)
521                                                  * Pop the block off the front of the queue,
522                                                  * dont set errno, because we are clear of errors
523                                                  * and want to try and write the next block too.
524                                                  */
525                                                 outbuffer.pop_front();
526                                         }
527                                         else
528                                         {
529                                                 std::string temp = outbuffer[0].substr(result);
530                                                 outbuffer[0] = temp;
531                                                 /* We didnt get the whole line out. arses.
532                                                  * Try again next time, i guess. Set errno,
533                                                  * because we shouldnt be writing any more now,
534                                                  * until the socketengine says its safe to do so.
535                                                  */
536                                                 errno = EAGAIN;
537                                         }
538                                 }
539                                 else if ((result == -1) && (errno != EAGAIN))
540                                 {
541                                         this->OnError(I_ERR_WRITE);
542                                         this->state = I_ERROR;
543                                         this->Instance->SE->DelFd(this);
544                                         this->Close();
545                                         return true;
546                                 }
547                         }
548                 }
549         }
550
551         if ((errno == EAGAIN) && (fd > -1))
552         {
553                 this->Instance->SE->WantWrite(this);
554         }
555
556         return (fd < 0);
557 }
558
559 void SocketTimeout::Tick(time_t now)
560 {
561         if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
562                 return;
563
564         if (this->sock->state == I_CONNECTING)
565         {
566                 // for non-listening sockets, the timeout can occur
567                 // which causes termination of the connection after
568                 // the given number of seconds without a successful
569                 // connection.
570                 this->sock->OnTimeout();
571                 this->sock->OnError(I_ERR_TIMEOUT);
572                 this->sock->timeout = true;
573                 ServerInstance->SE->DelFd(this->sock);
574                 /* NOTE: We must set this AFTER DelFd, as we added
575                  * this socket whilst writeable. This means that we
576                  * must DELETE the socket whilst writeable too!
577                  */
578                 this->sock->state = I_ERROR;
579                 this->sock->Close();
580                 delete this->sock;
581                 return;
582         }
583         else
584         {
585                 this->sock->Timeout = NULL;
586         }
587 }
588
589 bool InspSocket::Poll()
590 {
591         if (this->Instance->SE->GetRef(this->fd) != this)
592                 return false;
593
594         int incoming = -1;
595
596         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
597                 return false;
598
599         switch (this->state)
600         {
601                 case I_CONNECTING:
602                         /* Our socket was in write-state, so delete it and re-add it
603                          * in read-state.
604                          */
605                         if (this->fd > -1)
606                         {
607                                 this->Instance->SE->DelFd(this);
608                                 this->SetState(I_CONNECTED);
609                                 if (!this->Instance->SE->AddFd(this))
610                                         return false;
611                         }
612                         if (Instance->Config->GetIOHook(this))
613                         {
614                                 try
615                                 {
616                                         Instance->Config->GetIOHook(this)->OnRawSocketConnect(this->fd);
617                                 }
618                                 catch (CoreException& modexcept)
619                                 {
620                                         Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
621                                 }
622                         }
623                         return this->OnConnected();
624                 break;
625                 case I_LISTENING:
626                 {
627                         sockaddr* client = new sockaddr[2];
628                         length = sizeof (sockaddr_in);
629                         std::string recvip;
630 #ifdef IPV6
631                         if ((!*this->host) || strchr(this->host, ':'))
632                                 length = sizeof(sockaddr_in6);
633 #endif
634                         incoming = accept (this->fd, client, &length);
635 #ifdef IPV6
636                         if ((!*this->host) || strchr(this->host, ':'))
637                         {
638                                 char buf[1024];
639                                 recvip = inet_ntop(AF_INET6, &((sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
640                         }
641                         else
642                         {
643                                 recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr);
644                         }
645 #else
646                         recvip = inet_ntoa(((sockaddr_in*)client)->sin_addr);
647 #endif
648                         this->OnIncomingConnection(incoming, (char*)recvip.c_str());
649
650                         if (this->IsIOHooked)
651                         {
652                                 try
653                                 {
654                                         Instance->Config->GetIOHook(this)->OnRawSocketAccept(incoming, recvip.c_str(), this->port);
655                                 }
656                                 catch (CoreException& modexcept)
657                                 {
658                                         Instance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
659                                 }
660                         }
661
662                         this->SetQueues(incoming);
663
664                         delete[] client;
665                         return true;
666                 }
667                 break;
668                 case I_CONNECTED:
669                         /* Process the read event */
670                         return this->OnDataReady();
671                 break;
672                 default:
673                 break;
674         }
675         return true;
676 }
677
678 void InspSocket::SetState(InspSocketState s)
679 {
680         this->state = s;
681 }
682
683 InspSocketState InspSocket::GetState()
684 {
685         return this->state;
686 }
687
688 int InspSocket::GetFd()
689 {
690         return this->fd;
691 }
692
693 bool InspSocket::OnConnected() { return true; }
694 void InspSocket::OnError(InspSocketError e) { return; }
695 int InspSocket::OnDisconnect() { return 0; }
696 int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
697 bool InspSocket::OnDataReady() { return true; }
698 bool InspSocket::OnWriteReady() { return true; }
699 void InspSocket::OnTimeout() { return; }
700 void InspSocket::OnClose() { return; }
701
702 InspSocket::~InspSocket()
703 {
704         this->Close();
705         if (Timeout)
706         {
707                 Instance->Timers->DelTimer(Timeout);
708                 Timeout = NULL;
709         }
710 }
711
712 void InspSocket::HandleEvent(EventType et, int errornum)
713 {
714         switch (et)
715         {
716                 case EVENT_ERROR:
717                         this->Instance->SE->DelFd(this);
718                         this->Close();
719                         delete this;
720                         return;
721                 break;
722                 case EVENT_READ:
723                         if (!this->Poll())
724                         {
725                                 this->Instance->SE->DelFd(this);
726                                 this->Close();
727                                 delete this;
728                                 return;
729                         }
730                 break;
731                 case EVENT_WRITE:
732                         if (this->WaitingForWriteEvent)
733                         {
734                                 this->WaitingForWriteEvent = false;
735                                 if (!this->OnWriteReady())
736                                 {
737                                         this->Instance->SE->DelFd(this);
738                                         this->Close();
739                                         delete this;
740                                         return;
741                                 }
742                         }
743                         if (this->state == I_CONNECTING)
744                         {
745                                 /* This might look wrong as if we should be actually calling
746                                  * with EVENT_WRITE, but trust me it is correct. There are some
747                                  * writeability-state things in the read code, because of how
748                                  * InspSocket used to work regarding write buffering in previous
749                                  * versions of InspIRCd. - Brain
750                                  */
751                                 this->HandleEvent(EVENT_READ);
752                                 return;
753                         }
754                         else
755                         {
756                                 if (this->FlushWriteBuffer())
757                                 {
758                                         this->Instance->SE->DelFd(this);
759                                         this->Close();
760                                         delete this;
761                                         return;
762                                 }
763                         }
764                 break;
765         }
766 }
767