]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listensocket.cpp
Change OnHookUserIO to OnHookIO, making it usable for more than User* and less picky...
[user/henk/code/inspircd.git] / src / listensocket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/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 "inspircd.h"
17 #include "socket.h"
18 #include "socketengine.h"
19
20 /* Private static member data must be declared in this manner */
21 irc::sockets::sockaddrs ListenSocketBase::client;
22 irc::sockets::sockaddrs ListenSocketBase::server;
23
24 ListenSocketBase::ListenSocketBase(InspIRCd* Instance, int port, const std::string &addr) : ServerInstance(Instance), desc("plaintext")
25 {
26         irc::sockets::sockaddrs bind_to;
27
28         bind_addr = addr;
29         bind_port = port;
30
31         // canonicalize address if it is defined
32         if (!addr.empty() && irc::sockets::aptosa(addr.c_str(), port, &bind_to))
33                 irc::sockets::satoap(&bind_to, bind_addr, bind_port);
34
35         this->SetFd(irc::sockets::OpenTCPSocket(bind_addr.c_str()));
36         if (this->GetFd() > -1)
37         {
38                 if (!Instance->BindSocket(this->fd,port,bind_addr.c_str()))
39                         this->fd = -1;
40                 Instance->SE->AddFd(this);
41         }
42 }
43
44 ListenSocketBase::~ListenSocketBase()
45 {
46         if (this->GetFd() > -1)
47         {
48                 ServerInstance->SE->DelFd(this);
49                 ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
50                 if (ServerInstance->SE->Shutdown(this, 2) || ServerInstance->SE->Close(this))
51                         ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
52                 this->fd = -1;
53         }
54 }
55
56 /* Just seperated into another func for tidiness really.. */
57 void ListenSocketBase::AcceptInternal()
58 {
59         ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensoket");
60         int incomingSockfd;
61
62         socklen_t length = sizeof(client);
63         incomingSockfd = ServerInstance->SE->Accept(this, &client.sa, &length);
64
65         if (incomingSockfd < 0)
66         {
67                 ServerInstance->SE->Shutdown(incomingSockfd, 2);
68                 ServerInstance->SE->Close(incomingSockfd);
69                 ServerInstance->stats->statsRefused++;
70                 return;
71         }
72
73         socklen_t sz = sizeof(server);
74         if (getsockname(incomingSockfd, &server.sa, &sz))
75                 ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
76
77         /*
78          * XXX -
79          * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
80          * its a pretty big but for the moment valid assumption:
81          * file descriptors are handed out starting at 0, and are recycled as theyre freed.
82          * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
83          * irc server at once (or the irc server otherwise initiating this many connections, files etc)
84          * which for the time being is a physical impossibility (even the largest networks dont have more
85          * than about 10,000 users on ONE server!)
86          */
87         if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
88         {
89                 ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
90                 ServerInstance->SE->Shutdown(incomingSockfd, 2);
91                 ServerInstance->SE->Close(incomingSockfd);
92                 ServerInstance->stats->statsRefused++;
93                 return;
94         }
95
96         if (client.sa.sa_family == AF_INET6)
97         {
98                 /*
99                  * This case is the be all and end all patch to catch and nuke 4in6
100                  * instead of special-casing shit all over the place and wreaking merry
101                  * havoc with crap, instead, we just recreate sockaddr and strip ::ffff: prefix
102                  * if it's a 4in6 IP.
103                  *
104                  * This is, of course, much improved over the older way of handling this
105                  * (pretend it doesn't exist + hack around it -- yes, both were done!)
106                  *
107                  * Big, big thanks to danieldg for his work on this.
108                  * -- w00t
109                  */
110                 static const unsigned char prefix4in6[12] = { 0,0,0,0, 0,0,0,0, 0,0,0xFF,0xFF };
111                 if (!memcmp(prefix4in6, &client.in6.sin6_addr, 12))
112                 {
113                         // recreate as a sockaddr_in using the IPv4 IP
114                         uint16_t sport = client.in6.sin6_port;
115                         uint32_t addr = *reinterpret_cast<uint32_t*>(client.in6.sin6_addr.s6_addr + 12);
116                         client.in4.sin_family = AF_INET;
117                         client.in4.sin_port = sport;
118                         client.in4.sin_addr.s_addr = addr;
119
120                         sport = server.in6.sin6_port;
121                         addr = *reinterpret_cast<uint32_t*>(server.in6.sin6_addr.s6_addr + 12);
122                         server.in4.sin_family = AF_INET;
123                         server.in4.sin_port = sport;
124                         server.in4.sin_addr.s_addr = addr;
125                 }
126         }
127
128         ServerInstance->SE->NonBlocking(incomingSockfd);
129         ServerInstance->stats->statsAccept++;
130         this->OnAcceptReady(incomingSockfd);
131 }
132
133 void ListenSocketBase::HandleEvent(EventType e, int err)
134 {
135         switch (e)
136         {
137                 case EVENT_ERROR:
138                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
139                         break;
140                 case EVENT_WRITE:
141                         ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
142                         break;
143                 case EVENT_READ:
144                         this->AcceptInternal();
145                         break;
146         }
147 }
148
149 void ClientListenSocket::OnAcceptReady(int nfd)
150 {
151         ServerInstance->Users->AddUser(ServerInstance, nfd, this, &client, &server);
152 }