]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_iocp.cpp
e09fb4d0a9345bacc84f7719ac8d83fd0332f0c2
[user/henk/code/inspircd.git] / src / socketengines / socketengine_iocp.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 #include "socketengines/socketengine_iocp.h"
15 #include "exitcodes.h"
16 #include <mswsock.h>
17
18 IOCPEngine::IOCPEngine()
19 {
20         MAX_DESCRIPTORS = 10240;
21
22         /* Create completion port */
23         m_completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)0, 0);
24
25         if (!m_completionPort)
26         {
27                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
28                 ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
29                 printf("ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.\n");
30                 printf("ERROR: this is a fatal error, exiting now.\n");
31                 ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
32         }
33
34         /* Null variables out. */
35         CurrentSetSize = 0;
36         MAX_DESCRIPTORS = 10240;
37         ref = new EventHandler* [10240];
38         memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
39 }
40
41 IOCPEngine::~IOCPEngine()
42 {
43         /* Clean up winsock and close completion port */
44         CloseHandle(m_completionPort);
45         WSACleanup();
46         delete[] ref;
47 }
48
49 bool IOCPEngine::AddFd(EventHandler* eh, int event_mask)
50 {
51         /* Does it at least look valid? */
52         if (!eh)
53                 return false;
54
55         int* fake_fd = new int(GenerateFd(eh->GetFd()));
56         int is_accept = 0;
57         int opt_len = sizeof(int);
58
59         /* In range? */
60         if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS))
61         {
62                 delete fake_fd;
63                 return false;
64         }
65
66         /* Already an entry here */
67         if (ref[*fake_fd])
68         {
69                 delete fake_fd;
70                 return false;
71         }
72
73         /* are we a listen socket? */
74         getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
75
76         /* set up the read event so the socket can actually receive data :P */
77         eh->Extend("internal_fd", fake_fd);
78
79         unsigned long completion_key = (ULONG_PTR)*fake_fd;
80         /* assign the socket to the completion port */
81         if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
82                 return false;
83
84         /* setup initial events */
85         if(is_accept)
86                 PostAcceptEvent(eh);
87         else
88                 PostReadEvent(eh);
89
90         /* log message */
91         ServerInstance->Logs->Log("SOCKET",DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
92
93         /* post a write event if there is data to be written */
94         if (event_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE))
95                 WantWrite(eh);
96
97         /* we're all good =) */
98         try
99         {
100                 m_binding.insert( std::map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
101         }
102         catch (...)
103         {
104                 /* Ohshi-, map::insert failed :/ */
105                 return false;
106         }
107
108         ++CurrentSetSize;
109         SocketEngine::SetEventMask(eh, event_mask);
110         ref[*fake_fd] = eh;
111
112         return true;
113 }
114
115 bool IOCPEngine::DelFd(EventHandler* eh, bool force /* = false */)
116 {
117         if (!eh)
118                 return false;
119
120         int* fake_fd = NULL;
121
122         if (!eh->GetExt("internal_fd", fake_fd))
123                 return false;
124
125         int fd = eh->GetFd();
126
127         void* m_readEvent = NULL;
128         void* m_writeEvent = NULL;
129         void* m_acceptEvent = NULL;
130
131         ServerInstance->Logs->Log("SOCKET",DEBUG, "Removing fake fd %u, real fd %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
132
133         /* Cancel pending i/o operations. */
134         if (CancelIo((HANDLE)fd) == FALSE)
135                 return false;
136
137         /* Free the buffer, and delete the event. */
138         if (eh->GetExt("windows_readevent", m_readEvent))
139         {
140                 if(((Overlapped*)m_readEvent)->m_params != 0)
141                         delete ((udp_overlap*)((Overlapped*)m_readEvent)->m_params);
142
143                 delete ((Overlapped*)m_readEvent);
144                 eh->Shrink("windows_readevent");
145         }
146
147         if(eh->GetExt("windows_writeevent", m_writeEvent))
148         {
149                 delete ((Overlapped*)m_writeEvent);
150                 eh->Shrink("windows_writeevent");
151         }
152
153         if(eh->GetExt("windows_acceptevent", m_acceptEvent))
154         {
155                 delete ((accept_overlap*)((Overlapped*)m_acceptEvent)->m_params);
156                 delete ((Overlapped*)m_acceptEvent);
157                 eh->Shrink("windows_accepevent");
158         }
159
160         /* Clear binding */
161         ref[*fake_fd] = 0;
162         m_binding.erase(eh->GetFd());
163
164         delete fake_fd;
165         eh->Shrink("internal_fd");
166
167         /* decrement set size */
168         --CurrentSetSize;
169
170         /* success */
171         return true;
172 }
173
174 void IOCPEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
175 {
176         if (!eh)
177                 return;
178
179         void* m_writeEvent = NULL;
180
181         int* fake_fd = NULL;
182         if (!eh->GetExt("internal_fd", fake_fd))
183                 return;
184
185         /* Post event - write begin */
186         if((new_mask & (FD_WANT_POLL_WRITE | FD_WANT_FAST_WRITE)) && !eh->GetExt("windows_writeevent", m_writeEvent))
187         {
188                 ULONG_PTR completion_key = (ULONG_PTR)*fake_fd;
189                 Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_WRITE_READY, 0);
190                 eh->Shrink("windows_writeevent");
191                 eh->Extend("windows_writeevent",ov);
192                 PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
193         }
194 }
195
196 bool IOCPEngine::PostCompletionEvent(EventHandler * eh, SocketIOEvent type, int param)
197 {
198         if (!eh)
199                 return false;
200
201         int* fake_fd = NULL;
202         if (!eh->GetExt("internal_fd", fake_fd))
203                 return false;
204
205         Overlapped * ov = new Overlapped(type, param);
206         ULONG_PTR completion_key = (ULONG_PTR)*fake_fd;
207         return PostQueuedCompletionStatus(m_completionPort, 0, completion_key, &ov->m_overlap);
208 }
209
210 void IOCPEngine::PostReadEvent(EventHandler * eh)
211 {
212         if (!eh)
213                 return;
214
215         Overlapped * ov = new Overlapped(SOCKET_IO_EVENT_READ_READY, 0);
216         DWORD flags = 0;
217         DWORD r_length = 0;
218         WSABUF buf;
219
220         /* by passing a null buffer pointer, we can have this working in the same way as epoll..
221          * its slower, but it saves modifying all network code.
222          */
223         buf.buf = 0;
224         buf.len = 0;
225
226         /* determine socket type. */
227         DWORD sock_type;
228         int sock_len = sizeof(DWORD);
229         if(getsockopt(eh->GetFd(), SOL_SOCKET, SO_TYPE, (char*)&sock_type, &sock_len) == -1)
230         {
231                 /* wtfhax? */
232                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
233                 delete ov;
234                 return;
235         }
236         switch(sock_type)
237         {
238                 case SOCK_DGRAM:                        /* UDP Socket */
239                 {
240                         udp_overlap * uv = new udp_overlap;
241                         uv->udp_sockaddr_len = sizeof(sockaddr);
242                         buf.buf = (char*)uv->udp_buffer;
243                         buf.len = sizeof(uv->udp_buffer);
244                         ov->m_params = (unsigned long)uv;
245                         if(WSARecvFrom(eh->GetFd(), &buf, 1, &uv->udp_len, &flags, uv->udp_sockaddr, (LPINT)&uv->udp_sockaddr_len, &ov->m_overlap, 0))
246                         {
247                                 int err = WSAGetLastError();
248                                 if(err != WSA_IO_PENDING)
249                                 {
250                                         delete ov;
251                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
252                                         return;
253                                 }
254                         }
255                 }
256                 break;
257
258                 case SOCK_STREAM:                       /* TCP Socket */
259                 {
260                         if(WSARecv(eh->GetFd(), &buf, 1, &r_length, &flags, &ov->m_overlap, 0) == SOCKET_ERROR)
261                         {
262                                 if(WSAGetLastError() != WSA_IO_PENDING)
263                                 {
264                                         delete ov;
265                                         PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, 0);
266                                         return;
267                                 }
268                         }
269                 }
270                 break;
271
272                 default:
273                 {
274                         printf("unknwon socket type: %u\n", sock_type);
275                         return;
276                 }
277                 break;
278         }
279         eh->Extend("windows_readevent", ov);
280 }
281
282 int IOCPEngine::DispatchEvents()
283 {
284         DWORD len;
285         LPOVERLAPPED overlap;
286         Overlapped * ov;
287         EventHandler * eh;
288         ULONG_PTR intfd;
289         int ret;
290         unsigned long bytes_recv;
291
292         while (GetQueuedCompletionStatus(m_completionPort, &len, &intfd, &overlap, 1000))
293         {
294                 if (intfd > (unsigned long)MAX_DESCRIPTORS)
295                         continue;
296
297                 // woot, we got an event on a socket :P
298                 eh = ref[intfd];
299                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
300
301                 if (eh == 0)
302                         continue;
303
304                 void* m_readEvent = NULL;
305                 void* m_writeEvent = NULL;
306
307                 eh->GetExt("windows_readevent", m_readEvent);
308                 eh->GetExt("windows_writeevent", m_writeEvent);
309
310                 TotalEvents++;
311
312                 switch(ov->m_event)
313                 {
314                         case SOCKET_IO_EVENT_WRITE_READY:
315                         {
316                                 WriteEvents++;
317                                 eh->Shrink("windows_writeevent");
318                                 SetEventMask(eh, eh->GetEventMask() & ~FD_WRITE_WILL_BLOCK);
319                                 eh->HandleEvent(EVENT_WRITE, 0);
320                         }
321                         break;
322
323                         case SOCKET_IO_EVENT_READ_READY:
324                         {
325                                 ReadEvents++;
326                                 SetEventMask(eh, eh->GetEventMask() & ~FD_READ_WILL_BLOCK);
327                                 if(ov->m_params)
328                                 {
329                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
330                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
331                                         uv->udp_len = len;
332                                         this->udp_ov = uv;
333                                         eh->Shrink("windows_readevent");
334                                         eh->HandleEvent(EVENT_READ, 0);
335                                         this->udp_ov = 0;
336                                         delete uv;
337                                         PostReadEvent(eh);
338                                 }
339                                 else
340                                 {
341                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
342                                         eh->Shrink("windows_readevent");
343                                         if(ret != 0 || bytes_recv == 0)
344                                         {
345                                                 /* end of file */
346                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
347                                         }
348                                         else
349                                         {
350                                                 eh->HandleEvent(EVENT_READ, 0);
351                                                 PostReadEvent(eh);
352                                         }
353                                 }
354                         }
355                         break;
356
357                         case SOCKET_IO_EVENT_ACCEPT:
358                         {
359                                 /* this is kinda messy.. :/ */
360                                 ReadEvents++;
361                                 eh->HandleEvent(EVENT_READ, ov->m_params);
362                                 delete ((accept_overlap*)ov->m_params);
363                                 eh->Shrink("windows_acceptevent");
364                                 PostAcceptEvent(eh);
365                         }
366                         break;
367
368                         case SOCKET_IO_EVENT_ERROR:
369                         {
370                                 ErrorEvents++;
371                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
372                         }
373                         break;
374                 }
375
376                 delete ov;
377         }
378
379         return 0;
380 }
381
382 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
383 {
384         if (!eh)
385                 return;
386
387         int on = 1;
388         u_long arg = 1;
389         struct linger linger = { 0 };
390
391         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
392
393         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
394         /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
395         linger.l_onoff = 1;
396         linger.l_linger = 1;
397         setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
398         ioctlsocket(fd, FIONBIO, &arg);
399
400         int len = sizeof(sockaddr_in) + 16;
401         DWORD dwBytes;
402         accept_overlap* ao = new accept_overlap;
403         memset(ao->buf, 0, 1024);
404         ao->socket = fd;
405
406         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
407         eh->Extend("windows_acceptevent", ov);
408
409         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
410         {
411                 int err = WSAGetLastError();
412                 if(err != WSA_IO_PENDING)
413                 {
414                         printf("PostAcceptEvent err: %d\n", err);
415                 }
416         }
417 }
418
419
420 std::string IOCPEngine::GetName()
421 {
422         return "iocp";
423 }
424
425 EventHandler * IOCPEngine::GetRef(int fd)
426 {
427         std::map<int, EventHandler*>::iterator itr = m_binding.find(fd);
428         return (itr == m_binding.end()) ? 0 : itr->second;
429 }
430
431 bool IOCPEngine::HasFd(int fd)
432 {
433         return (GetRef(fd) != 0);
434 }
435
436 bool IOCPEngine::BoundsCheckFd(EventHandler* eh)
437 {
438         int * internal_fd;
439         if (!eh || eh->GetFd() < 0)
440                 return false;
441
442         if(!eh->GetExt("internal_fd", internal_fd))
443                 return false;
444
445         if(*internal_fd > MAX_DESCRIPTORS)
446                 return false;
447
448         return true;
449 }
450
451 EventHandler * IOCPEngine::GetIntRef(int fd)
452 {
453         if(fd < 0 || fd > MAX_DESCRIPTORS)
454                 return 0;
455         return ref[fd];
456 }
457
458 int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
459 {
460         //SOCKET s = fd->GetFd();
461
462         Overlapped* acceptevent = NULL;
463         if (!fd->GetExt("windows_acceptevent", acceptevent))
464                 /* Shit, no accept event on this socket! :( */
465                 return -1;
466
467         Overlapped* ovl = acceptevent;
468         accept_overlap* ov = (accept_overlap*)ovl->m_params;
469
470         //sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
471         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
472
473         memcpy(addr, client_address, sizeof(sockaddr_in));
474         *addrlen = sizeof(sockaddr_in);
475
476         return ov->socket;
477 }
478
479 int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen)
480 {
481         Overlapped* ovl = NULL;
482
483         if (!fd->GetExt("windows_acceptevent", ovl))
484                 return -1;
485
486         accept_overlap* ov = (accept_overlap*)ovl->m_params;
487
488         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
489         //sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
490
491         memcpy(name, server_address, sizeof(sockaddr_in));
492         *namelen = sizeof(sockaddr_in);
493
494         return 0;
495 }
496
497 int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
498 {
499         this->UpdateStats(len, 0);
500         udp_overlap * ov = NULL;
501         if (!fd->GetExt("windows_readevent", ov))
502                 return -1;
503         memcpy(buf, ov->udp_buffer, ov->udp_len);
504         memcpy(from, ov->udp_sockaddr, *fromlen);
505         return ov->udp_len;
506 }
507
508 int IOCPEngine::Blocking(int fd)
509 {
510         unsigned long opt = 0;
511         return ioctlsocket(fd, FIONBIO, &opt);
512 }
513
514 int IOCPEngine::NonBlocking(int fd)
515 {
516         unsigned long opt = 1;
517         return ioctlsocket(fd, FIONBIO, &opt);
518 }
519
520 int IOCPEngine::Close(int fd)
521 {
522         return closesocket(fd);
523 }
524
525 int IOCPEngine::Close(EventHandler* fd)
526 {
527         return this->Close(fd->GetFd());
528 }