]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/listensocket.cpp
Update all wiki links to point to the new wiki. This was done automatically with...
[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
21 /* Private static member data must be initialized in this manner */
22 unsigned int ListenSocketBase::socketcount = 0;
23 sockaddr* ListenSocketBase::sock_us = NULL;
24 sockaddr* ListenSocketBase::client = NULL;
25 sockaddr* ListenSocketBase::raddr = NULL;
26
27 ListenSocketBase::ListenSocketBase(InspIRCd* Instance, int port, const std::string &addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
28 {
29         this->SetFd(irc::sockets::OpenTCPSocket(addr.c_str()));
30         if (this->GetFd() > -1)
31         {
32                 if (!Instance->BindSocket(this->fd,port,addr.c_str()))
33                         this->fd = -1;
34 #ifdef IPV6
35                 if ((!*addr.c_str()) || (strchr(addr.c_str(),':')))
36                         this->family = AF_INET6;
37                 else
38 #endif
39                 this->family = AF_INET;
40                 Instance->SE->AddFd(this);
41         }
42         /* Saves needless allocations */
43         if (socketcount == 0)
44         {
45                 /* All instances of ListenSocket share these, so reference count it */
46                 ServerInstance->Logs->Log("SOCKET", DEBUG,"Allocate sockaddr structures");
47                 sock_us = new sockaddr[2];
48                 client = new sockaddr[2];
49                 raddr = new sockaddr[2];
50         }
51         socketcount++;
52 }
53
54 ListenSocketBase::~ListenSocketBase()
55 {
56         if (this->GetFd() > -1)
57         {
58                 ServerInstance->SE->DelFd(this);
59                 ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
60                 if (ServerInstance->SE->Shutdown(this, 2) || ServerInstance->SE->Close(this))
61                         ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
62                 this->fd = -1;
63         }
64         socketcount--;
65         if (socketcount == 0)
66         {
67                 delete[] sock_us;
68                 delete[] client;
69                 delete[] raddr;
70         }
71 }
72
73 /* Just seperated into another func for tidiness really.. */
74 void ListenSocketBase::AcceptInternal()
75 {
76         ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensoket");
77         socklen_t uslen, length;                // length of our port number
78         int incomingSockfd;
79
80 #ifdef IPV6
81         if (this->family == AF_INET6)
82         {
83                 uslen = sizeof(sockaddr_in6);
84                 length = sizeof(sockaddr_in6);
85         }
86         else
87 #endif
88         {
89                 uslen = sizeof(sockaddr_in);
90                 length = sizeof(sockaddr_in);
91         }
92
93         incomingSockfd = ServerInstance->SE->Accept(this, (sockaddr*)client, &length);
94
95         if (incomingSockfd < 0 ||
96                   ServerInstance->SE->GetSockName(this, sock_us, &uslen) == -1)
97         {
98                 ServerInstance->SE->Shutdown(incomingSockfd, 2);
99                 ServerInstance->SE->Close(incomingSockfd);
100                 ServerInstance->stats->statsRefused++;
101                 return;
102         }
103         /*
104          * XXX -
105          * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
106          * its a pretty big but for the moment valid assumption:
107          * file descriptors are handed out starting at 0, and are recycled as theyre freed.
108          * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
109          * irc server at once (or the irc server otherwise initiating this many connections, files etc)
110          * which for the time being is a physical impossibility (even the largest networks dont have more
111          * than about 10,000 users on ONE server!)
112          */
113         if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
114         {
115                 ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
116                 ServerInstance->SE->Shutdown(incomingSockfd, 2);
117                 ServerInstance->SE->Close(incomingSockfd);
118                 ServerInstance->stats->statsRefused++;
119                 return;
120         }
121
122         static char buf[MAXBUF];
123         static char target[MAXBUF];
124
125         *target = *buf = '\0';
126
127 #ifdef IPV6
128         if (this->family == AF_INET6)
129         {
130                 inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
131                 socklen_t raddrsz = sizeof(sockaddr_in6);
132                 if (getsockname(incomingSockfd, (sockaddr*) raddr, &raddrsz) == 0)
133                         inet_ntop(AF_INET6, &((const sockaddr_in6*)raddr)->sin6_addr, target, sizeof(target));
134                 else
135                         ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
136
137                 /*
138                  * This case is the be all and end all patch to catch and nuke 4in6
139                  * instead of special-casing shit all over the place and wreaking merry
140                  * havoc with crap, instead, we just recreate sockaddr and strip ::ffff: prefix
141                  * if it's a 4in6 IP.
142                  *
143                  * This is, of course, much improved over the older way of handling this
144                  * (pretend it doesn't exist + hack around it -- yes, both were done!)
145                  *
146                  * Big, big thanks to danieldg for his work on this.
147                  * -- w00t
148                  */
149                 static const unsigned char prefix4in6[12] = { 0,0,0,0,  0,0,0,0, 0,0,0xFF,0xFF };
150                 if (!memcmp(prefix4in6, &((const sockaddr_in6*)client)->sin6_addr, 12))
151                 {
152                         // strip leading ::ffff: from the IPs
153                         memmove(buf, buf+7, sizeof(buf)-7);
154                         memmove(target, target+7, sizeof(target)-7);
155
156                         // recreate as a sockaddr_in using the IPv4 IP
157                         uint16_t sport = ((const sockaddr_in6*)client)->sin6_port;
158                         struct sockaddr_in* clientv4 = (struct sockaddr_in*)client;
159                         clientv4->sin_family = AF_INET;
160                         clientv4->sin_port = sport;
161                         inet_pton(AF_INET, buf, &clientv4->sin_addr);
162                 }
163         }
164         else
165 #endif
166         {
167                 inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
168                 socklen_t raddrsz = sizeof(sockaddr_in);
169                 if (getsockname(incomingSockfd, (sockaddr*) raddr, &raddrsz) == 0)
170                         inet_ntop(AF_INET, &((const sockaddr_in*)raddr)->sin_addr, target, sizeof(target));
171                 else
172                         ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
173         }
174
175         ServerInstance->SE->NonBlocking(incomingSockfd);
176         ServerInstance->stats->statsAccept++;
177         this->OnAcceptReady(target, incomingSockfd, buf);
178 }
179
180 void ListenSocketBase::HandleEvent(EventType e, int err)
181 {
182         switch (e)
183         {
184                 case EVENT_ERROR:
185                         ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
186                         break;
187                 case EVENT_WRITE:
188                         ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
189                         break;
190                 case EVENT_READ:
191                         this->AcceptInternal();
192                         break;
193         }
194 }
195
196 void ClientListenSocket::OnAcceptReady(const std::string &ipconnectedto, int nfd, const std::string &incomingip)
197 {
198         ServerInstance->Users->AddUser(ServerInstance, nfd, bind_port, false, client, ipconnectedto);
199 }