]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_iocp.cpp
* Sockets will now always be nonblocking on win32 version of InspIRCd. There were...
[user/henk/code/inspircd.git] / src / socketengine_iocp.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "socketengine_iocp.h"
15 #include "exitcodes.h"
16 #include <mswsock.h>
17
18 IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
19 {
20         /* Create completion port */
21         m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
22
23         if (!m_completionPort)
24         {
25                 ServerInstance->Log(SPARSE,"ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
26                 ServerInstance->Log(SPARSE,"ERROR: this is a fatal error, exiting now.");
27                 printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
28                 printf("ERROR: this is a fatal error, exiting now.");
29                 InspIRCd::Exit(EXIT_STATUS_SOCKETENGINE);
30         }
31
32         /* Null variables out. */
33         CurrentSetSize = 0;
34         EngineHandle = 0;
35         memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
36 }
37
38 IOCPEngine::~IOCPEngine()
39 {
40         CloseHandle(m_completionPort);
41 }
42
43 bool IOCPEngine::AddFd(EventHandler* eh)
44 {
45         /* Does it at least look valid? */
46         if (!eh)
47                 return false;
48
49         int fake_fd = GenerateFd(eh->GetFd());
50         int is_accept = 0;
51         int opt_len = sizeof(int);
52
53         /* In range? */
54         if ((fake_fd < 0) || (fake_fd > MAX_DESCRIPTORS))
55                 return false;
56
57         /* Already an entry here */
58         if (ref[fake_fd])
59                 return false;
60
61         /* are we a listen socket? */
62         getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
63
64         /* set up the read event so the socket can actually receive data :P */
65         eh->m_internalFd = fake_fd;
66         eh->m_writeEvent = 0;
67         eh->m_acceptEvent = 0;
68
69         unsigned long completion_key = (ULONG_PTR)eh->m_internalFd;
70         /* assign the socket to the completion port */
71         if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
72                 return false;
73
74         /* set up binding, increase set size */
75         ref[fake_fd] = eh;
76         ++CurrentSetSize;
77
78         /* setup initial events */
79         if(is_accept)
80                 PostAcceptEvent(eh);
81         else
82                 PostReadEvent(eh);
83
84         /* log message */
85         ServerInstance->Log(DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", fake_fd, eh->GetFd(), eh);
86
87         /* post a write event if there is data to be written */
88         if(eh->Writeable())
89                 WantWrite(eh);
90
91         /* we're all good =) */
92         try
93         {
94                 m_binding.insert( map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
95         }
96         catch (...)
97         {
98                 /* Ohshi-, map::insert failed :/ */
99                 return false;
100         }
101
102         return true;
103 }
104
105 bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
106 {
107         if (!eh)
108                 return false;
109
110         int fake_fd = eh->m_internalFd;
111         int fd = eh->GetFd();
112         
113         if (!ref[fake_fd])
114                 return false;
115
116         ServerInstance->Log(DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", fake_fd, eh->GetFd(), eh);
117
118         /* Cancel pending i/o operations. */
119         if (CancelIo((HANDLE)fd) == FALSE)
120                 return false;
121
122         /* Free the buffer, and delete the event. */
123         if (eh->m_readEvent != 0)
124         {
125                 if(((Overlapped*)eh->m_readEvent)->m_params != 0)
126                         delete ((udp_overlap*)((Overlapped*)eh->m_readEvent)->m_params);
127
128                 delete ((Overlapped*)eh->m_readEvent);
129         }
130
131         if(eh->m_writeEvent != 0)
132                 delete ((Overlapped*)eh->m_writeEvent);
133
134         if(eh->m_acceptEvent != 0)
135         {
136                 delete ((accept_overlap*)((Overlapped*)eh->m_acceptEvent)->m_params);
137                 delete ((Overlapped*)eh->m_acceptEvent);
138         }
139
140         /* Clear binding */
141         ref[fake_fd] = 0;
142         m_binding.erase(eh->GetFd());
143
144         /* decrement set size */
145         --CurrentSetSize;
146         
147         /* success */
148         return true;
149 }
150
151 void IOCPEngine::WantWrite(EventHandler* eh)
152 {
153         if (!eh)
154                 return;
155
156         /* Post event - write begin */
157         if(!eh->m_writeEvent)
158         {
159                 ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
160                 Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
161                 eh->m_writeEvent = (void*)ov;
162                 PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
163         }
164 }
165
166 bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
167 {
168         if (!eh)
169                 return false;
170
171         Overlapped * ov = new Overlapped(type, param);
172         ULONG_PTR completion_key = (ULONG_PTR)eh->m_internalFd;
173         return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
174 }
175
176 void IOCPEngine::PostReadEvent(EventHandler * eh)
177 {
178         if (!eh)
179                 return;
180
181         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
182         DWORD flags = 0;
183         DWORD r_length = 0;
184         WSABUF buf;
185
186         /* by passing a null buffer pointer, we can have this working in the same way as epoll..
187          * its slower, but it saves modifying all network code.
188          */
189         buf.buf = 0;
190         buf.len = 0;
191
192         /* determine socket type. */
193         DWORD sock_type;
194         int sock_len = sizeof(DWORD);
195         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
196         {
197                 /* wtfhax? */
198                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
199                 delete ov;
200                 return;
201         }
202         switch(sock_type)
203         {
204                 case SOCK_DGRAM:                        /* UDP Socket */
205                 {
206                         udp_overlap * uv = new udp_overlap;
207                         uv->udp_sockaddr_len = sizeof(sockaddr);
208                         buf.buf = (char*)uv->udp_buffer;
209                         buf.len = sizeof(uv->udp_buffer);
210                         ov->m_params = (unsigned long)uv;
211                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
212                         {
213                                 int err = WSAGetLastError();
214                                 if(err != WSA_IO_PENDING)
215                                 {
216                                         delete ov;
217                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
218                                         return;
219                                 }
220                         }
221                 }
222                 break;
223
224                 case SOCK_STREAM:                       /* TCP Socket */
225                 {
226                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
227                         {
228                                 if(WSAGetLastError() != WSA_IO_PENDING)
229                                 {
230                                         delete ov;
231                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
232                                         return;
233                                 }
234                         }
235                 }
236                 break;
237
238                 default:
239                 {
240                         printf("unknwon socket type: %u\n", sock_type);
241                         return;
242                 }
243                 break;
244         }
245         eh->m_readEvent = (void*)ov;
246 }
247
248 int IOCPEngine::DispatchEvents()
249 {
250         DWORD len;
251         LPOVERLAPPED overlap;
252         Overlapped * ov;
253         EventHandler * eh;
254         ULONG_PTR intfd;
255         int ret;
256         unsigned long bytes_recv;
257
258         while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
259         {
260                 // woot, we got an event on a socket :P
261                 eh = ref[intfd];
262                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
263
264                 if (eh == 0)
265                         continue;
266
267                 switch(ov->m_event)
268                 {
269                         case SOCKET_IO_EVENT_WRITE_READY:
270                         {
271                                 eh->m_writeEvent = 0;
272                                 eh->HandleEvent(EVENT_WRITE, 0);
273                         }
274                         break;
275
276                         case SOCKET_IO_EVENT_READ_READY:
277                         {
278                                 if(ov->m_params)
279                                 {
280                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
281                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
282                                         uv->udp_len = len;
283                                         this->udp_ov = uv;
284                                         eh->m_readEvent = 0;
285                                         eh->HandleEvent(EVENT_READ, 0);
286                                         this->udp_ov = 0;
287                                         delete uv;
288                                         PostReadEvent(eh);
289                                 }
290                                 else
291                                 {
292                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
293                                         eh->m_readEvent = 0;
294                                         if(ret != 0 || bytes_recv == 0)
295                                         {
296                                                 /* end of file */
297                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
298                                         }
299                                         else
300                                         {
301                                                 eh->HandleEvent(EVENT_READ, 0);
302                                                 PostReadEvent(eh);
303                                         }
304                                 }
305                         }
306                         break;
307                 
308                         case SOCKET_IO_EVENT_ACCEPT:
309                         {
310                                 /* this is kinda messy.. :/ */
311                                 eh->HandleEvent(EVENT_READ, ov->m_params);
312                                 delete ((accept_overlap*)ov->m_params);
313                                 eh->m_acceptEvent = 0;
314                                 PostAcceptEvent(eh);
315                         }
316                         break;
317
318                         case SOCKET_IO_EVENT_ERROR:
319                         {
320                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
321                         }
322                         break;
323                 }
324                 
325                 delete ov;
326         }
327
328         return 0;
329 }
330
331 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
332 {
333         if (!eh)
334                 return;
335
336         int on = 1;
337         u_long arg = 1;
338         struct linger linger = { 0 };
339
340         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
341
342         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
343         /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
344         linger.l_onoff = 1;
345         linger.l_linger = 1;
346         setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
347         ioctlsocket(fd, FIONBIO, &arg);
348
349         int len = sizeof(sockaddr_in) + 16;
350         DWORD dwBytes;
351         accept_overlap* ao = new accept_overlap;
352         memset(ao->buf, 0, 1024);
353         ao->socket = fd;
354
355         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
356         eh->m_acceptEvent = (void*)ov;
357
358         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
359         {
360                 int err = WSAGetLastError();
361                 if(err != WSA_IO_PENDING)
362                 {
363                         printf("PostAcceptEvent err: %d\n", err);
364                 }
365         }
366 }
367
368
369 std::string IOCPEngine::GetName()
370 {
371         return "iocp";
372 }
373
374 int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
375 {
376         Overlapped* ovl = (Overlapped*)acceptevent;
377         accept_overlap* ov = (accept_overlap*)ovl->m_params;
378
379         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
380         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
381
382         memcpy(addr, client_address, sizeof(sockaddr_in));
383         *addrlen = sizeof(sockaddr_in);
384
385         return ov->socket;
386 }
387
388 int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
389 {
390         Overlapped* ovl = (Overlapped*)acceptevent;
391         accept_overlap* ov = (accept_overlap*)ovl->m_params;
392
393         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
394         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
395
396         memcpy(name, server_address, sizeof(sockaddr_in));
397         *namelen = sizeof(sockaddr_in);
398
399         return 0;
400 }
401
402 int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
403 {
404         memcpy(buf, ov->udp_buffer, ov->udp_len);
405         memcpy(from, ov->udp_sockaddr, *fromlen);
406         return ov->udp_len;
407 }
408
409 EventHandler * IOCPEngine::GetRef(int fd)
410 {
411         map<int, EventHandler*>::iterator itr = m_binding.find(fd);
412         return (itr == m_binding.end()) ? 0 : itr->second;
413 }
414
415 bool IOCPEngine::HasFd(int fd)
416 {
417         return (GetRef(fd) != 0);
418 }
419
420 EventHandler * IOCPEngine::GetIntRef(int fd)
421 {
422         if(fd < 0 || fd > MAX_DESCRIPTORS)
423                 return 0;
424         return ref[fd];
425 }
426