]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengines/socketengine_iocp.cpp
7dce02dbd925b71b4794a30e03d5c0cd27f3a93a
[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://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 "socketengines/socketengine_iocp.h"
15 #include "exitcodes.h"
16 #include <mswsock.h>
17
18 IOCPEngine::IOCPEngine(InspIRCd * Instance) : SocketEngine(Instance)
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         EngineHandle = 0;
37         MAX_DESCRIPTORS = 10240;
38         ref = new EventHandler* [10240];
39         memset(ref, 0, sizeof(EventHandler*) * MAX_DESCRIPTORS);
40 }
41
42 IOCPEngine::~IOCPEngine()
43 {
44         /* Clean up winsock and close completion port */
45         CloseHandle(m_completionPort);
46         WSACleanup();
47         delete[] ref;
48 }
49
50 bool IOCPEngine::AddFd(EventHandler* eh)
51 {
52         /* Does it at least look valid? */
53         if (!eh)
54                 return false;
55
56         int* fake_fd = new int(GenerateFd(eh->GetFd()));
57         int is_accept = 0;
58         int opt_len = sizeof(int);
59
60         /* In range? */
61         if ((*fake_fd < 0) || (*fake_fd > MAX_DESCRIPTORS))
62         {
63                 delete fake_fd;
64                 return false;
65         }
66
67         /* Already an entry here */
68         if (ref[*fake_fd])
69         {
70                 delete fake_fd;
71                 return false;
72         }
73
74         /* are we a listen socket? */
75         getsockopt(eh->GetFd(), SOL_SOCKET, SO_ACCEPTCONN, (char*)&is_accept, &opt_len);
76
77         /* set up the read event so the socket can actually receive data :P */
78         eh->Extend("internal_fd", fake_fd);
79
80         unsigned long completion_key = (ULONG_PTR)*fake_fd;
81         /* assign the socket to the completion port */
82         if (!CreateIoCompletionPort((HANDLE)eh->GetFd(), m_completionPort, completion_key, 0))
83                 return false;
84
85         /* setup initial events */
86         if(is_accept)
87                 PostAcceptEvent(eh);
88         else
89                 PostReadEvent(eh);
90
91         /* log message */
92         ServerInstance->Logs->Log("SOCKET",DEBUG, "New fake fd: %u, real fd: %u, address 0x%p", *fake_fd, eh->GetFd(), eh);
93
94         /* post a write event if there is data to be written */
95         if(eh->Writeable())
96                 WantWrite(eh);
97
98         /* we're all good =) */
99         try
100         {
101                 m_binding.insert( std::map<int, EventHandler*>::value_type( eh->GetFd(), eh ) );
102         }
103         catch (...)
104         {
105                 /* Ohshi-, map::insert failed :/ */
106                 return false;
107         }
108
109         ++CurrentSetSize;
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::WantWrite(EventHandler* eh)
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(!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                                 eh->HandleEvent(EVENT_WRITE, 0);
319                         }
320                         break;
321
322                         case SOCKET_IO_EVENT_READ_READY:
323                         {
324                                 ReadEvents++;
325                                 if(ov->m_params)
326                                 {
327                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
328                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
329                                         uv->udp_len = len;
330                                         this->udp_ov = uv;
331                                         eh->Shrink("windows_readevent");
332                                         eh->HandleEvent(EVENT_READ, 0);
333                                         this->udp_ov = 0;
334                                         delete uv;
335                                         PostReadEvent(eh);
336                                 }
337                                 else
338                                 {
339                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
340                                         eh->Shrink("windows_readevent");
341                                         if(ret != 0 || bytes_recv == 0)
342                                         {
343                                                 /* end of file */
344                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
345                                         }
346                                         else
347                                         {
348                                                 eh->HandleEvent(EVENT_READ, 0);
349                                                 PostReadEvent(eh);
350                                         }
351                                 }
352                         }
353                         break;
354                 
355                         case SOCKET_IO_EVENT_ACCEPT:
356                         {
357                                 /* this is kinda messy.. :/ */
358                                 ReadEvents++;
359                                 eh->HandleEvent(EVENT_READ, ov->m_params);
360                                 delete ((accept_overlap*)ov->m_params);
361                                 eh->Shrink("windows_acceptevent");
362                                 PostAcceptEvent(eh);
363                         }
364                         break;
365
366                         case SOCKET_IO_EVENT_ERROR:
367                         {
368                                 ErrorEvents++;
369                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
370                         }
371                         break;
372                 }
373                 
374                 delete ov;
375         }
376
377         return 0;
378 }
379
380 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
381 {
382         if (!eh)
383                 return;
384
385         int on = 1;
386         u_long arg = 1;
387         struct linger linger = { 0 };
388
389         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
390
391         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
392         /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
393         linger.l_onoff = 1;
394         linger.l_linger = 1;
395         setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
396         ioctlsocket(fd, FIONBIO, &arg);
397
398         int len = sizeof(sockaddr_in) + 16;
399         DWORD dwBytes;
400         accept_overlap* ao = new accept_overlap;
401         memset(ao->buf, 0, 1024);
402         ao->socket = fd;
403
404         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
405         eh->Extend("windows_acceptevent", ov);
406
407         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
408         {
409                 int err = WSAGetLastError();
410                 if(err != WSA_IO_PENDING)
411                 {
412                         printf("PostAcceptEvent err: %d\n", err);
413                 }
414         }
415 }
416
417
418 std::string IOCPEngine::GetName()
419 {
420         return "iocp";
421 }
422
423 EventHandler * IOCPEngine::GetRef(int fd)
424 {
425         std::map<int, EventHandler*>::iterator itr = m_binding.find(fd);
426         return (itr == m_binding.end()) ? 0 : itr->second;
427 }
428
429 bool IOCPEngine::HasFd(int fd)
430 {
431         return (GetRef(fd) != 0);
432 }
433
434 bool IOCPEngine::BoundsCheckFd(EventHandler* eh)
435 {
436         int * internal_fd;
437         if (!eh || eh->GetFd() < 0)
438                 return false;
439
440         if(!eh->GetExt("internal_fd", internal_fd))
441                 return false;
442
443         if(*internal_fd > MAX_DESCRIPTORS)
444                 return false;
445
446         return true;
447 }
448
449 EventHandler * IOCPEngine::GetIntRef(int fd)
450 {
451         if(fd < 0 || fd > MAX_DESCRIPTORS)
452                 return 0;
453         return ref[fd];
454 }
455
456 int IOCPEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen)
457 {
458         //SOCKET s = fd->GetFd();
459
460         Overlapped* acceptevent = NULL;
461         if (!fd->GetExt("windows_acceptevent", acceptevent))
462                 /* Shit, no accept event on this socket! :( */
463                 return -1;
464
465         Overlapped* ovl = acceptevent;
466         accept_overlap* ov = (accept_overlap*)ovl->m_params;
467         
468         //sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
469         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
470
471         memcpy(addr, client_address, sizeof(sockaddr_in));
472         *addrlen = sizeof(sockaddr_in);
473
474         return ov->socket;
475 }
476
477 int IOCPEngine::GetSockName(EventHandler* fd, sockaddr *name, socklen_t* namelen)
478 {
479         Overlapped* ovl = NULL;
480         
481         if (!fd->GetExt("windows_acceptevent", ovl))
482                 return -1;
483
484         accept_overlap* ov = (accept_overlap*)ovl->m_params;
485
486         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
487         //sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
488
489         memcpy(name, server_address, sizeof(sockaddr_in));
490         *namelen = sizeof(sockaddr_in);
491
492         return 0;
493 }
494
495 int IOCPEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
496 {
497         this->UpdateStats(len, 0);
498         udp_overlap * ov = NULL;
499         if (!fd->GetExt("windows_readevent", ov))
500                 return -1;
501         memcpy(buf, ov->udp_buffer, ov->udp_len);
502         memcpy(from, ov->udp_sockaddr, *fromlen);
503         return ov->udp_len;
504 }
505
506 int IOCPEngine::Blocking(int fd)
507 {
508         unsigned long opt = 0;
509         return ioctlsocket(fd, FIONBIO, &opt);
510 }
511
512 int IOCPEngine::NonBlocking(int fd)
513 {
514         unsigned long opt = 1;
515         return ioctlsocket(fd, FIONBIO, &opt);
516 }
517
518 int IOCPEngine::Close(int fd)
519 {
520         return closesocket(fd);
521 }
522
523 int IOCPEngine::Close(EventHandler* fd)
524 {
525         return this->Close(fd->GetFd());
526 }
527
528