]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socketengine_iocp.cpp
Apply diff for userprocess.cpp from earlier in the day
[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         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                 if (intfd < 0 || intfd > MAX_DESCRIPTORS)
261                         continue;
262
263                 // woot, we got an event on a socket :P
264                 eh = ref[intfd];
265                 ov = CONTAINING_RECORD(overlap, Overlapped, m_overlap);
266
267                 if (eh == 0)
268                         continue;
269
270                 switch(ov->m_event)
271                 {
272                         case SOCKET_IO_EVENT_WRITE_READY:
273                         {
274                                 eh->m_writeEvent = 0;
275                                 eh->HandleEvent(EVENT_WRITE, 0);
276                         }
277                         break;
278
279                         case SOCKET_IO_EVENT_READ_READY:
280                         {
281                                 if(ov->m_params)
282                                 {
283                                         // if we had params, it means we are a udp socket with a udp_overlap pointer in this long.
284                                         udp_overlap * uv = (udp_overlap*)ov->m_params;
285                                         uv->udp_len = len;
286                                         this->udp_ov = uv;
287                                         eh->m_readEvent = 0;
288                                         eh->HandleEvent(EVENT_READ, 0);
289                                         this->udp_ov = 0;
290                                         delete uv;
291                                         PostReadEvent(eh);
292                                 }
293                                 else
294                                 {
295                                         ret = ioctlsocket(eh->GetFd(), FIONREAD, &bytes_recv);
296                                         eh->m_readEvent = 0;
297                                         if(ret != 0 || bytes_recv == 0)
298                                         {
299                                                 /* end of file */
300                                                 PostCompletionEvent(eh, SOCKET_IO_EVENT_ERROR, EIO); /* Old macdonald had an error, EIEIO. */
301                                         }
302                                         else
303                                         {
304                                                 eh->HandleEvent(EVENT_READ, 0);
305                                                 PostReadEvent(eh);
306                                         }
307                                 }
308                         }
309                         break;
310                 
311                         case SOCKET_IO_EVENT_ACCEPT:
312                         {
313                                 /* this is kinda messy.. :/ */
314                                 eh->HandleEvent(EVENT_READ, ov->m_params);
315                                 delete ((accept_overlap*)ov->m_params);
316                                 eh->m_acceptEvent = 0;
317                                 PostAcceptEvent(eh);
318                         }
319                         break;
320
321                         case SOCKET_IO_EVENT_ERROR:
322                         {
323                                 eh->HandleEvent(EVENT_ERROR, ov->m_params);
324                         }
325                         break;
326                 }
327                 
328                 delete ov;
329         }
330
331         return 0;
332 }
333
334 void IOCPEngine::PostAcceptEvent(EventHandler * eh)
335 {
336         if (!eh)
337                 return;
338
339         int on = 1;
340         u_long arg = 1;
341         struct linger linger = { 0 };
342
343         int fd = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
344
345         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
346         /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
347         linger.l_onoff = 1;
348         linger.l_linger = 1;
349         setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
350         ioctlsocket(fd, FIONBIO, &arg);
351
352         int len = sizeof(sockaddr_in) + 16;
353         DWORD dwBytes;
354         accept_overlap* ao = new accept_overlap;
355         memset(ao->buf, 0, 1024);
356         ao->socket = fd;
357
358         Overlapped* ov = new Overlapped(SOCKET_IO_EVENT_ACCEPT, (int)ao);
359         eh->m_acceptEvent = (void*)ov;
360
361         if(AcceptEx(eh->GetFd(), fd, ao->buf, 0, len, len, &dwBytes, &ov->m_overlap) == FALSE)
362         {
363                 int err = WSAGetLastError();
364                 if(err != WSA_IO_PENDING)
365                 {
366                         printf("PostAcceptEvent err: %d\n", err);
367                 }
368         }
369 }
370
371
372 std::string IOCPEngine::GetName()
373 {
374         return "iocp";
375 }
376
377 int __accept_socket(SOCKET s, sockaddr * addr, int * addrlen, void * acceptevent)
378 {
379         Overlapped* ovl = (Overlapped*)acceptevent;
380         accept_overlap* ov = (accept_overlap*)ovl->m_params;
381
382         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
383         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
384
385         memcpy(addr, client_address, sizeof(sockaddr_in));
386         *addrlen = sizeof(sockaddr_in);
387
388         return ov->socket;
389 }
390
391 int __getsockname(SOCKET s, sockaddr * name, int * namelen, void * acceptevent)
392 {
393         Overlapped* ovl = (Overlapped*)acceptevent;
394         accept_overlap* ov = (accept_overlap*)ovl->m_params;
395
396         sockaddr_in* server_address = (sockaddr_in*)&ov->buf[10];
397         sockaddr_in* client_address = (sockaddr_in*)&ov->buf[38];
398
399         memcpy(name, server_address, sizeof(sockaddr_in));
400         *namelen = sizeof(sockaddr_in);
401
402         return 0;
403 }
404
405 int __recvfrom(SOCKET s, char * buf, int len, int flags, struct sockaddr * from, int * fromlen, udp_overlap * ov)
406 {
407         memcpy(buf, ov->udp_buffer, ov->udp_len);
408         memcpy(from, ov->udp_sockaddr, *fromlen);
409         return ov->udp_len;
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 EventHandler * IOCPEngine::GetIntRef(int fd)
424 {
425         if(fd < 0 || fd > MAX_DESCRIPTORS)
426                 return 0;
427         return ref[fd];
428 }
429