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