]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listensocket.cpp
Get rid of ModePair
[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 ListenSocket::ListenSocket(ConfigTag* tag, const std::string& addr, int port)
21         : bind_tag(tag)
22 {
23         irc::sockets::sockaddrs bind_to;
24
25         // canonicalize address if it is defined
26         if (!irc::sockets::aptosa(addr, port, bind_to))
27         {
28                 fd = -1;
29                 return;
30         }
31         irc::sockets::satoap(bind_to, bind_addr, bind_port);
32         bind_desc = irc::sockets::satouser(bind_to);
33
34         fd = irc::sockets::OpenTCPSocket(bind_addr);
35
36         if (this->fd > -1)
37         {
38                 int rv = ServerInstance->SE->Bind(this->fd, &bind_to.sa, sizeof(bind_to));
39                 if (rv >= 0)
40                         rv = ServerInstance->SE->Listen(this->fd, ServerInstance->Config->MaxConn);
41
42                 if (rv < 0)
43                 {
44                         ServerInstance->SE->Shutdown(this, 2);
45                         ServerInstance->SE->Close(this);
46                         this->fd = -1;
47                 }
48                 else
49                 {
50                         ServerInstance->SE->NonBlocking(this->fd);
51                         ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
52                 }
53         }
54 }
55
56 ListenSocket::~ListenSocket()
57 {
58         if (this->GetFd() > -1)
59         {
60                 ServerInstance->SE->DelFd(this);
61                 ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
62                 if (ServerInstance->SE->Shutdown(this, 2) || ServerInstance->SE->Close(this))
63                         ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
64                 this->fd = -1;
65         }
66 }
67
68 /* Just seperated into another func for tidiness really.. */
69 void ListenSocket::AcceptInternal()
70 {
71         irc::sockets::sockaddrs client;
72         irc::sockets::sockaddrs server;
73
74         socklen_t length = sizeof(client);
75         int incomingSockfd = ServerInstance->SE->Accept(this, &client.sa, &length);
76
77         ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensoket %s nfd=%d", bind_desc.c_str(), incomingSockfd);
78         if (incomingSockfd < 0)
79         {
80                 ServerInstance->stats->statsRefused++;
81                 return;
82         }
83
84         socklen_t sz = sizeof(server);
85         if (getsockname(incomingSockfd, &server.sa, &sz))
86                 ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
87
88         /*
89          * XXX -
90          * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
91          * its a pretty big but for the moment valid assumption:
92          * file descriptors are handed out starting at 0, and are recycled as theyre freed.
93          * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
94          * irc server at once (or the irc server otherwise initiating this many connections, files etc)
95          * which for the time being is a physical impossibility (even the largest networks dont have more
96          * than about 10,000 users on ONE server!)
97          */
98         if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
99         {
100                 ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
101                 ServerInstance->SE->Shutdown(incomingSockfd, 2);
102                 ServerInstance->SE->Close(incomingSockfd);
103                 ServerInstance->stats->statsRefused++;
104                 return;
105         }
106
107         if (client.sa.sa_family == AF_INET6)
108         {
109                 /*
110                  * This case is the be all and end all patch to catch and nuke 4in6
111                  * instead of special-casing shit all over the place and wreaking merry
112                  * havoc with crap, instead, we just recreate sockaddr and strip ::ffff: prefix
113                  * if it's a 4in6 IP.
114                  *
115                  * This is, of course, much improved over the older way of handling this
116                  * (pretend it doesn't exist + hack around it -- yes, both were done!)
117                  *
118                  * Big, big thanks to danieldg for his work on this.
119                  * -- w00t
120                  */
121                 static const unsigned char prefix4in6[12] = { 0,0,0,0, 0,0,0,0, 0,0,0xFF,0xFF };
122                 if (!memcmp(prefix4in6, &client.in6.sin6_addr, 12))
123                 {
124                         // recreate as a sockaddr_in using the IPv4 IP
125                         uint16_t sport = client.in6.sin6_port;
126                         uint32_t addr = *reinterpret_cast<uint32_t*>(client.in6.sin6_addr.s6_addr + 12);
127                         client.in4.sin_family = AF_INET;
128                         client.in4.sin_port = sport;
129                         client.in4.sin_addr.s_addr = addr;
130
131                         sport = server.in6.sin6_port;
132                         addr = *reinterpret_cast<uint32_t*>(server.in6.sin6_addr.s6_addr + 12);
133                         server.in4.sin_family = AF_INET;
134                         server.in4.sin_port = sport;
135                         server.in4.sin_addr.s_addr = addr;
136                 }
137         }
138
139         ServerInstance->SE->NonBlocking(incomingSockfd);
140
141         ModResult res;
142         FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server));
143         if (res == MOD_RES_PASSTHRU)
144         {
145                 std::string type = bind_tag->getString("type", "clients");
146                 if (type == "clients")
147                 {
148                         ServerInstance->Users->AddUser(incomingSockfd, this, &client, &server);
149                         res = MOD_RES_ALLOW;
150                 }
151         }
152         if (res == MOD_RES_ALLOW)
153         {
154                 ServerInstance->stats->statsAccept++;
155         }
156         else
157         {
158                 ServerInstance->stats->statsRefused++;
159                 ServerInstance->Logs->Log("SOCKET",DEFAULT,"Refusing connection on %s - %s",
160                         bind_desc.c_str(), res == MOD_RES_DENY ? "Connection refused by module" : "Module for this port not found");
161                 ServerInstance->SE->Close(incomingSockfd);
162         }
163 }
164
165 void ListenSocket::HandleEvent(EventType e, int err)
166 {
167         switch (e)
168         {
169                 case EVENT_ERROR:
170                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
171                         break;
172                 case EVENT_WRITE:
173                         ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
174                         break;
175                 case EVENT_READ:
176                         this->AcceptInternal();
177                         break;
178         }
179 }