1 /* +------------------------------------+
\r * | Inspire Internet Relay Chat Daemon |
\r * +------------------------------------+
\r *
\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team
\r * See: http://www.inspircd.org/wiki/index.php/Credits
\r *
\r * This program is free but copyrighted software; see
\r * the file COPYING for details.
\r *
\r * ---------------------------------------------------
\r */
\r\r#include "socketengine_iocp.h"
\r#include <mswsock.h>
\r\rIOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
\r{
\r /* Create completion port */
\r m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
\r\r /* Null variables out. */
\r CurrentSetSize = 0;
\r EngineHandle = 0;
\r memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
\r}
\r\rIOCPEngine::~IOCPEngine()
\r{
\r CloseHandle(m_completionPort);
\r}
\r\rbool IOCPEngine::AddFd(EventHandler* eh)
\r{
\r int fake_fd = GenerateFd(eh->GetFd());
\r int is_accept = 0;
\r int opt_len = sizeof(int);
\r if(fake_fd < 0)
\r return false;
\r\r /* are we a listen socket? */
\r getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
\r\r /* set up the read event so the socket can actually receive data :P */
\r eh->m_internalFd = fake_fd;
\r eh->m_writeEvent = 0;
\r eh->m_acceptEvent = 0;
\r\r unsigned long completion_key = (ULONG_PTR)eh->m_internalFd;
\r /* assign the socket to the completion port */
\r if(!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
\r return false;
\r\r /* set up binding, increase set size */
\r ref[fake_fd] = eh;
\r ++CurrentSetSize;
\r\r /* setup initial events */
\r if(is_accept)
\r PostAcceptEvent(eh);
\r else
\r PostReadEvent(eh);
\r\r /* log message */
\r ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh);
\r\r /* post a write event if there is data to be written */
\r if(eh->Writeable())
\r WantWrite(eh);
\r\r /* we're all good =) */
\r try
\r {
\r m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
\r }
\r catch (...)
\r {
\r /* Ohshi-, map::insert failed :/ */
\r return false;
\r }
\r\r return true;
\r}
\r\rbool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
\r{
\r int fake_fd = eh->m_internalFd;
\r int fd = eh->GetFd();
\r \r if(ref[fake_fd] == 0)
\r return false;
\r\r ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh);
\r\r /* Cancel pending i/o operations. */
\r if (CancelIo((HANDLE)fd) == FALSE)
\r return false;
\r\r /* Free the buffer, and delete the event. */
\r if(eh->m_readEvent != 0)
\r {
\r if(((Overlapped*)eh->m_readEvent)->m_params != 0)
\r delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params);
\r\r delete ((Overlapped*)eh->m_readEvent);
\r }
\r\r if(eh->m_writeEvent != 0)
\r delete ((Overlapped*)eh->m_writeEvent);
\r\r if(eh->m_acceptEvent != 0)
\r {
\r delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
\r delete ((Overlapped*)eh->m_acceptEvent);
\r }
\r\r /* Clear binding */
\r ref[fake_fd] = 0;
\r m_binding.erase(eh->GetFd());
\r\r /* decrement set size */
\r --CurrentSetSize;
\r \r /* success */
\r return true;
\r}
\r\rvoid IOCPEngine::WantWrite(EventHandler* eh)
\r{
\r /* Post event - write begin */
\r if(!eh->m_writeEvent)
\r {
\r ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
\r Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
\r eh->m_writeEvent = (void*)ov;
\r PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
\r }
\r}
\r\rbool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
\r{
\r Overlapped * ov = new Overlapped(type, param);
\r ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
\r return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
\r}
\r\rvoid IOCPEngine::PostReadEvent(EventHandler * eh)
\r{
\r Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
\r DWORD flags = 0;
\r DWORD r_length = 0;
\r WSABUF buf;
\r\r /* by passing a null buffer pointer, we can have this working in the same way as epoll..
\r * its slower, but it saves modifying all network code.
\r */
\r buf.buf = 0;
\r buf.len = 0;
\r\r /* determine socket type. */
\r DWORD sock_type;
\r int sock_len = sizeof(DWORD);
\r if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
\r {
\r /* wtfhax? */
\r PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
\r delete ov;
\r return;
\r }
\r switch(sock_type)
\r {
\r case SOCK_DGRAM: /* UDP Socket */
\r {
\r udp_overlap * uv = new udp_overlap;
\r uv->udp_sockaddr_len = sizeof(sockaddr);
\r buf.buf = (char*)uv->udp_buffer;
\r buf.len = sizeof(uv->udp_buffer);
\r ov->m_params = (unsigned long)uv;
\r if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
\r {
\r int err = WSAGetLastError();
\r if(err != WSA_IO_PENDING)
\r {
\r delete ov;
\r PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
\r return;
\r }
\r }
\r }
\r break;
\r\r case SOCK_STREAM: /* TCP Socket */
\r {
\r if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
\r {
\r if(WSAGetLastError() != WSA_IO_PENDING)
\r {
\r delete ov;
\r PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
\r return;
\r }
\r }
\r }
\r break;
\r\r default:
\r {
\r printf("unknwon socket type: %u\n", sock_type);
\r return;
\r }
\r break;
\r }
\r eh->m_readEvent = (void*)ov;
\r}
\r\rint IOCPEngine::DispatchEvents()
\r{
\r DWORD len;
\r LPOVERLAPPED overlap;
\r Overlapped * ov;
\r EventHandler * eh;
\r ULONG_PTR intfd;
\r int ret;
\r unsigned long bytes_recv;
\r\r while(GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
\r {
\r // woot, we got an event on a socket :P
\r eh = ref[intfd];
\r ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
\r if(eh == 0) continue;
\r switch(ov->m_event)
\r {
\r case SOCKET_IO_EVENT_WRITE_READY:
\r {
\r eh->m_writeEvent = 0;
\r eh->HandleEvent(EVENT_WRITE, 0);
\r }
\r break;
\r\r case SOCKET_IO_EVENT_READ_READY:
\r {
\r if(ov->m_params)
\r {
\r // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
\r udp_overlap * uv = (udp_overlap*)ov->m_params;
\r uv->udp_len = len;
\r this->udp_ov = uv;
\r eh->m_readEvent = 0;
\r eh->HandleEvent(EVENT_READ, 0);
\r this->udp_ov = 0;
\r delete uv;
\r PostReadEvent(eh);
\r }
\r else
\r {
\r ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
\r eh->m_readEvent = 0;
\r if(ret != 0 || bytes_recv == 0)
\r {
\r /* end of file */
\r PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
\r }
\r else
\r {
\r eh->HandleEvent(EVENT_READ, 0);
\r PostReadEvent(eh);
\r }
\r }
\r }
\r break;
\r \r case SOCKET_IO_EVENT_ACCEPT:
\r {
\r /* this is kinda messy.. :/ */
\r eh->HandleEvent(EVENT_READ, ov->m_params);
\r delete ((accept_overlap*)ov->m_params);
\r eh->m_acceptEvent = 0;
\r PostAcceptEvent(eh);
\r }
\r break;
\r\r case SOCKET_IO_EVENT_ERROR:
\r {
\r eh->HandleEvent(EVENT_ERROR, ov->m_params);
\r }
\r break;
\r }
\r \r delete ov;
\r }
\r\r return 0;
\r}
\r\rvoid IOCPEngine::PostAcceptEvent(EventHandler * eh)
\r{
\r int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
\r int len = sizeof(sockaddr_in) + 16;
\r DWORD dwBytes;
\r accept_overlap* ao = new accept_overlap;
\r memset(ao->buf, 0, 1024);
\r ao->socket = fd;
\r\r Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
\r eh->m_acceptEvent = (void*)ov;
\r\r if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
\r {
\r int err = WSAGetLastError();
\r if(err != WSA_IO_PENDING)
\r {
\r printf("PostAcceptEvent err: %d\n", err);
\r }
\r }
\r}
\r\r\rstd::string IOCPEngine::GetName()
\r{
\r return "iocp";
\r}
\r\rint __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
\r{
\r Overlapped* ovl = (Overlapped*)acceptevent;
\r accept_overlap* ov = (accept_overlap*)ovl->m_params;
\r\r sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
\r sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
\r\r memcpy(addr, client_address, sizeof(sockaddr_in));
\r *addrlen = sizeof(sockaddr_in);
\r\r return ov->socket;
\r}
\r\rint __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
\r{
\r Overlapped* ovl = (Overlapped*)acceptevent;
\r accept_overlap* ov = (accept_overlap*)ovl->m_params;
\r\r sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
\r sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
\r\r memcpy(name, server_address, sizeof(sockaddr_in));
\r *namelen = sizeof(sockaddr_in);
\r\r return 0;
\r}
\r\rint __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
\r{
\r memcpy(buf, ov->udp_buffer, ov->udp_len);
\r memcpy(from, ov->udp_sockaddr, *fromlen);
\r return ov->udp_len;
\r}
\r\rEventHandler * IOCPEngine::GetRef(int fd)
\r{
\r map<int, EventHandler*>::iterator itr = m_binding.find(fd);
\r return (itr == m_binding.end()) ? 0 : itr->second;
\r}
\r\rbool IOCPEngine::HasFd(int fd)
\r{
\r return (GetRef(fd) != 0);
\r}
\r\rEventHandler * IOCPEngine::GetIntRef(int fd)
\r{
\r if(fd < 0 || fd > MAX_DESCRIPTORS)
\r return 0;
\r return ref[fd];
\r}
\r\r