+
+ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
+{
+ this->SetFd(OpenTCPSocket(addr));
+ if (this->GetFd() > -1)
+ {
+ if (!Instance->BindSocket(this->fd,port,addr))
+ this->fd = -1;
+#ifdef IPV6
+ if ((!*addr) || (strchr(addr,':')))
+ this->family = AF_INET6;
+ else
+#endif
+ this->family = AF_INET;
+ Instance->SE->AddFd(this);
+ }
+}
+
+ListenSocket::~ListenSocket()
+{
+ if (this->GetFd() > -1)
+ {
+ ServerInstance->SE->DelFd(this);
+ ServerInstance->Logs->Log("SOCKET", DEBUG,"Shut down listener on fd %d", this->fd);
+ if (ServerInstance->SE->Shutdown(this, 2) || ServerInstance->SE->Close(this))
+ ServerInstance->Logs->Log("SOCKET", DEBUG,"Failed to cancel listener: %s", strerror(errno));
+ this->fd = -1;
+ }
+}
+
+
+// XXX this is a bit of an untidy way to avoid reallocating this constantly. also, we leak it on shutdown.. but that's kinda minor - w
+static sockaddr *sock_us;
+static sockaddr *client;
+static bool setup_sock = false;
+
+void ListenSocket::HandleEvent(EventType, int)
+{
+ socklen_t uslen, length; // length of our port number
+ int incomingSockfd, in_port;
+
+ if (!setup_sock)
+ {
+ sock_us = new sockaddr[2];
+ client = new sockaddr[2];
+ setup_sock = true;
+ }
+
+#ifdef IPV6
+ if (this->family == AF_INET6)
+ {
+ uslen = sizeof(sockaddr_in6);
+ length = sizeof(sockaddr_in6);
+ }
+ else
+#endif
+ {
+ uslen = sizeof(sockaddr_in);
+ length = sizeof(sockaddr_in);
+ }
+
+ incomingSockfd = ServerInstance->SE->Accept(this, (sockaddr*)client, &length);
+
+ if ((incomingSockfd > -1) && (!ServerInstance->SE->GetSockName(this, sock_us, &uslen)))
+ {
+ char buf[MAXBUF];
+ char target[MAXBUF];
+
+ *target = *buf = '\0';
+
+ sockaddr* raddr = new sockaddr[2];
+
+#ifdef IPV6
+ if (this->family == AF_INET6)
+ {
+ in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port);
+ inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
+ socklen_t raddrsz = sizeof(sockaddr_in6);
+ if (getpeername(incomingSockfd, (sockaddr*) raddr, &raddrsz) == 0)
+ inet_ntop(AF_INET6, &((const sockaddr_in6*)raddr)->sin6_addr, target, sizeof(target));
+ else
+ ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
+ }
+ else
+#endif
+ {
+ inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
+ in_port = ntohs(((sockaddr_in*)sock_us)->sin_port);
+ socklen_t raddrsz = sizeof(sockaddr_in);
+ if (getpeername(incomingSockfd, (sockaddr*) raddr, &raddrsz) == 0)
+ inet_ntop(AF_INET, &((const sockaddr_in*)raddr)->sin_addr, target, sizeof(target));
+ else
+ ServerInstance->Logs->Log("SOCKET", DEBUG, "Can't get peername: %s", strerror(errno));
+
+ }
+
+ delete[] raddr;
+
+ ServerInstance->SE->NonBlocking(incomingSockfd);
+
+ ServerInstance->stats->statsAccept++;
+ ServerInstance->Users->AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client, target);
+ }
+ else
+ {
+ ServerInstance->SE->Shutdown(incomingSockfd, 2);
+ ServerInstance->SE->Close(incomingSockfd);
+ ServerInstance->stats->statsRefused++;
+ }
+}
+