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