1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2009 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
18 #include "socketengine.h"
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;
27 ListenSocketBase::ListenSocketBase(InspIRCd* Instance, int port, const std::string &addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
29 this->SetFd(irc::sockets::OpenTCPSocket(addr.c_str()));
30 if (this->GetFd() > -1)
32 if (!Instance->BindSocket(this->fd,port,addr.c_str()))
35 if ((!*addr.c_str()) || (strchr(addr.c_str(),':')))
36 this->family = AF_INET6;
39 this->family = AF_INET;
40 Instance->SE->AddFd(this);
42 /* Saves needless allocations */
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];
54 ListenSocketBase::~ListenSocketBase()
56 if (this->GetFd() > -1)
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));
73 /* Just seperated into another func for tidiness really.. */
74 void ListenSocketBase::AcceptInternal()
76 ServerInstance->Logs->Log("SOCKET",DEBUG,"HandleEvent for Listensoket");
77 socklen_t uslen, length; // length of our port number
81 if (this->family == AF_INET6)
83 uslen = sizeof(sockaddr_in6);
84 length = sizeof(sockaddr_in6);
89 uslen = sizeof(sockaddr_in);
90 length = sizeof(sockaddr_in);
93 incomingSockfd = ServerInstance->SE->Accept(this, (sockaddr*)client, &length);
95 if (incomingSockfd < 0 ||
96 ServerInstance->SE->GetSockName(this, sock_us, &uslen) == -1)
98 ServerInstance->SE->Shutdown(incomingSockfd, 2);
99 ServerInstance->SE->Close(incomingSockfd);
100 ServerInstance->stats->statsRefused++;
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!)
113 if (incomingSockfd >= ServerInstance->SE->GetMaxFds())
115 ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full");
116 ServerInstance->SE->Shutdown(incomingSockfd, 2);
117 ServerInstance->SE->Close(incomingSockfd);
118 ServerInstance->stats->statsRefused++;
122 static char buf[MAXBUF];
123 static char target[MAXBUF];
125 *target = *buf = '\0';
128 if (this->family == AF_INET6)
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));
135 ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
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
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!)
146 * Big, big thanks to danieldg for his work on this.
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))
152 // strip leading ::ffff: from the IPs
153 memmove(buf, buf+7, sizeof(buf)-7);
154 memmove(target, target+7, sizeof(target)-7);
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);
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));
172 ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
175 ServerInstance->SE->NonBlocking(incomingSockfd);
176 ServerInstance->stats->statsAccept++;
177 this->OnAcceptReady(target, incomingSockfd, buf);
180 void ListenSocketBase::HandleEvent(EventType e, int err)
185 ServerInstance->Logs->Log("SOCKET",DEFAULT,"ListenSocket::HandleEvent() received a socket engine error event! well shit! '%s'", strerror(err));
188 ServerInstance->Logs->Log("SOCKET",DEBUG,"*** BUG *** ListenSocket::HandleEvent() got a WRITE event!!!");
191 this->AcceptInternal();
196 void ClientListenSocket::OnAcceptReady(const std::string &ipconnectedto, int nfd, const std::string &incomingip)
198 ServerInstance->Users->AddUser(ServerInstance, nfd, bind_port, false, client, ipconnectedto);