1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.socket;
7 
8 
9 version(WINE)
10 {
11 	version = DFL_NoSocket;
12 }
13 
14 
15 version(DFL_NoSocket)
16 {
17 }
18 else
19 {
20 
21 private import dfl.internal.dlib, dfl.internal.clib;
22 
23 private
24 {
25 	private import std.socket, core.bitop;
26 	private import std.c.windows.winsock;//core.sys.windows.winsock;
27 	
28 	alias InternetHost DInternetHost;
29 	alias InternetAddress DInternetAddress;
30 	
31 	socket_t getSocketHandle(Socket sock) @nogc nothrow
32 	{
33 		return sock.handle;
34 	}
35 }
36 
37 alias std.socket.Socket DflSocket; ///
38 
39 private import dfl.internal.winapi, dfl.application, dfl.base, dfl.internal.utf;
40 
41 
42 private
43 {
44 	enum
45 	{
46 		FD_READ =       0x01,
47 		FD_WRITE =      0x02,
48 		FD_OOB =        0x04,
49 		FD_ACCEPT =     0x08,
50 		FD_CONNECT =    0x10,
51 		FD_CLOSE =      0x20,
52 		FD_QOS =        0x40,
53 		FD_GROUP_QOS =  0x80,
54 	}
55 	
56 	
57 	extern(Windows) int WSAAsyncSelect(socket_t s, HWND hWnd, UINT wMsg, int lEvent) nothrow @nogc;
58 }
59 
60 
61 ///
62 // Can be OR'ed.
63 enum EventType
64 {
65 	NONE = 0, ///
66 	
67 	READ =       FD_READ, /// ditto
68 	WRITE =      FD_WRITE, /// ditto
69 	OOB =        FD_OOB, /// ditto
70 	ACCEPT =     FD_ACCEPT, /// ditto
71 	CONNECT =    FD_CONNECT, /// ditto
72 	CLOSE =      FD_CLOSE, /// ditto
73 	
74 	QOS =        FD_QOS,
75 	GROUP_QOS =  FD_GROUP_QOS,
76 }
77 
78 
79 ///
80 // -err- will be 0 if no error.
81 // -type- will always contain only one flag.
82 alias void delegate(DflSocket sock, EventType type, int err) RegisterEventCallback;
83 
84 
85 // Calling this twice on the same socket cancels out previously
86 // registered events for the socket.
87 // Requires Application.run() or Application.doEvents() loop.
88 void registerEvent(DflSocket sock, EventType events, RegisterEventCallback callback) // deprecated
89 {
90 	assert(sock !is null, "registerEvent: socket cannot be null");
91 	assert(callback !is null, "registerEvent: callback cannot be null");
92 	
93 	if(!hwNet)
94 		_init();
95 	
96 	sock.blocking = false; // So the getter will be correct.
97 	
98 	// SOCKET_ERROR
99 	if(-1 == WSAAsyncSelect(getSocketHandle(sock), hwNet, WM_DFL_NETEVENT, cast(int)events))
100 		throw new DflException("Unable to register socket events");
101 	
102 	EventInfo ei;
103 	
104 	ei.sock = sock;
105 	ei.callback = callback;
106 	allEvents[getSocketHandle(sock)] = ei;
107 }
108 
109 
110 void unregisterEvent(DflSocket sock) @trusted @nogc nothrow// deprecated
111 {
112 	 WSAAsyncSelect(getSocketHandle(sock), hwNet, 0, 0);
113 	 
114 	//delete allEvents[getSocketHandle(sock)];
115  if(getSocketHandle(sock) in allEvents)
116   allEvents.remove(getSocketHandle(sock)); 
117 }
118 
119 
120 ///
121 class AsyncSocket: DflSocket // docmain
122 {
123 	///
124 	this(AddressFamily af, SocketType type, ProtocolType protocol)
125 	{
126 		super(af, type, protocol);
127 		super.blocking = false;
128 	}
129 	
130 	/// ditto
131 	this(AddressFamily af, SocketType type)
132 	{
133 		super(af, type);
134 		super.blocking = false;
135 	}
136 	
137 	/// ditto
138 	this(AddressFamily af, SocketType type, Dstring protocolName)
139 	{
140 		super(af, type, protocolName);
141 		super.blocking = false;
142 	}
143 	
144 	/// ditto
145 	// For use with accept().
146 	
147 	protected this() @safe pure nothrow
148 	{
149 	}
150  
151 	///
152 	void event(EventType events, RegisterEventCallback callback)
153 	{
154 		registerEvent(this, events, callback);
155 	}
156 	
157 	
158 	protected override AsyncSocket accepting()
159 	{
160 		return new AsyncSocket;
161 	}
162 	 
163 	
164 	override void close()
165 	{
166 		unregisterEvent(this);
167 		super.close();
168 	}
169  
170 
171 	override @property bool blocking() const // getter
172 	{
173 		return false;
174 	}
175 	
176 	
177 	override @property void blocking(bool byes) // setter
178 	{
179 		if(byes)
180 			assert(0);
181 	}
182 	
183 }
184 
185 
186 ///
187 class AsyncTcpSocket: AsyncSocket // docmain
188 {
189 	///
190 	this(AddressFamily family)
191 	{
192 		super(family, SocketType.STREAM, ProtocolType.TCP);
193 	}
194 	
195 	/// ditto
196 	this()
197 	{
198 		this(cast(AddressFamily)AddressFamily.INET);
199 	}
200 	
201 	/// ditto
202 	// Shortcut.
203 	this(Address connectTo, EventType events, RegisterEventCallback eventCallback)
204 	{
205 		this(connectTo.addressFamily());
206 		event(events, eventCallback);
207 		connect(connectTo);
208 	}
209 }
210 
211 
212 ///
213 class AsyncUdpSocket: AsyncSocket // docmain
214 {
215 	///
216 	this(AddressFamily family)
217 	{
218 		super(family, SocketType.DGRAM, ProtocolType.UDP);
219 	}
220 	
221 	/// ditto
222 	this()
223 	{
224 		this(cast(AddressFamily)AddressFamily.INET);
225 	}
226 }
227 
228 
229 /+
230 private class GetHostWaitHandle: WaitHandle
231 {
232 	this(HANDLE h)
233 	{
234 		super.handle = h;
235 	}
236 	
237 	
238 	final:
239 	
240 	alias WaitHandle.handle handle; // Overload.
241 	
242 	override @property void handle(HANDLE h) // setter
243 	{
244 		assert(0);
245 	}
246 	
247 	override void close()
248 	{
249 		WSACancelAsyncRequest(handle);
250 		super.handle = INVALID_HANDLE;
251 	}
252 	
253 	
254 	private void _gotEvent()
255 	{
256 		super.handle = INVALID_HANDLE;
257 	}
258 }
259 
260 
261 private class GetHostAsyncResult, IAsyncResult
262 {
263 	this(HANDLE h, GetHostCallback callback)
264 	{
265 		wh = new GetHostWaitHandle(h);
266 		this.callback = callback;
267 	}
268 	
269 	
270 	@property WaitHandle asyncWaitHandle() // getter
271 	{
272 		return wh;
273 	}
274 	
275 	
276 	@property bool completedSynchronously() // getter
277 	{
278 		return false;
279 	}
280 	
281 	
282 	@property bool isCompleted() // getter
283 	{
284 		return wh.handle != WaitHandle.INVALID_HANDLE;
285 	}
286 	
287 	
288 	private:
289 	GetHostWaitHandle wh;
290 	GetHostCallback callback;
291 	
292 	
293 	void _gotEvent(LPARAM lparam)
294 	{
295 		wh._gotEvent();
296 		
297 		callback(bla, HIWORD(lparam));
298 	}
299 }
300 +/
301 
302 
303 private void _getHostErr()
304 {
305 	throw new DflException("Get host failure"); // Needs a better message.. ?
306 }
307 
308 
309 private class _InternetHost: DInternetHost
310 {
311 	private:
312 	this(void* hostentBytes)
313 	{
314 		super.validHostent(cast(hostent*)hostentBytes);
315 		super.populate(cast(hostent*)hostentBytes);
316 	}
317 }
318 
319 
320 ///
321 // If -err- is nonzero, it is a winsock error code and -inetHost- is null.
322 alias void delegate(DInternetHost inetHost, int err) GetHostCallback;
323 
324 
325 ///
326 class GetHost // docmain
327 {
328 	///
329 	void cancel()
330 	{
331 		WSACancelAsyncRequest(h);
332 		h = null;
333 	}
334 	
335 	
336 	private:
337 	HANDLE h;
338 	GetHostCallback callback;
339 	DThrowable exception;
340 	ubyte[/+MAXGETHOSTSTRUCT+/ 1024] hostentBytes;
341 	
342 	
343 	void _gotEvent(LPARAM lparam)
344 	{
345 		h = null;
346 		
347 		int err;
348 		err = HIWORD(lparam);
349 		if(err)
350 			callback(null, err);
351 		else
352 			callback(new _InternetHost(hostentBytes.ptr), 0);
353 	}
354 	
355 	
356 	this()
357 	{
358 	}
359 }
360 
361 
362 ///
363 GetHost asyncGetHostByName(Dstring name, GetHostCallback callback) // docmain
364 {
365 	if(!hwNet)
366 		_init();
367 	
368 	HANDLE h;
369 	GetHost result;
370 	
371 	result = new GetHost;
372 	h = WSAAsyncGetHostByName(hwNet, WM_DFL_HOSTEVENT, unsafeStringz(name),
373 		cast(char*)result.hostentBytes, result.hostentBytes.length);
374 	if(!h)
375 		_getHostErr();
376 	
377 	result.h = h;
378 	result.callback = callback;
379 	allGetHosts[h] = result;
380 	
381 	return result;
382 }
383 
384 
385 ///
386 GetHost asyncGetHostByAddr(uint32_t addr, GetHostCallback callback) // docmain
387 {
388 	if(!hwNet)
389 		_init();
390 	
391 	HANDLE h;
392 	GetHost result;
393 	
394 	result = new GetHost;
395 	version(LittleEndian)
396 		addr = bswap(addr);
397 	h = WSAAsyncGetHostByAddr(hwNet, WM_DFL_HOSTEVENT, cast(char*)&addr, addr.sizeof,
398 		AddressFamily.INET, cast(char*)result.hostentBytes, result.hostentBytes.length);
399 	if(!h)
400 		_getHostErr();
401 	
402 	result.h = h;
403 	result.callback = callback;
404 	allGetHosts[h] = result;
405 	
406 	return result;
407 }
408 
409 /// ditto
410 // Shortcut.
411 GetHost asyncGetHostByAddr(Dstring addr, GetHostCallback callback) // docmain
412 {
413 	uint uiaddr;
414 	uiaddr = DInternetAddress.parse(addr);
415 	if(DInternetAddress.ADDR_NONE == uiaddr)
416 		_getHostErr();
417 	return asyncGetHostByAddr(uiaddr, callback);
418 }
419 
420 
421 ///
422 class SocketQueue // docmain
423 {
424 	///
425 	this(DflSocket sock)
426 	in
427 	{
428 		assert(sock !is null);
429 	}
430 	body
431 	{
432 		this.sock = sock;
433 	}
434 	
435 	
436 	///
437 	final @property DflSocket socket() // getter
438 	{
439 		return sock;
440 	}
441 	
442 	
443 	///
444 	void reset()
445 	{
446 		writebuf = null;
447 		readbuf = null;
448 	}
449 	
450 	
451 	/+
452 	// DMD 0.92 says error: function toString overrides but is not covariant with toString
453 	override Dstring toString()
454 	{
455 		return cast(Dstring)peek();
456 	}
457 	+/
458 	
459 	
460 	///
461 	void[] peek()
462 	{
463 		return readbuf[0 .. rpos];
464 	}
465 	
466 	/// ditto
467 	void[] peek(uint len)
468 	{
469 		if(len >= rpos)
470 			return peek();
471 		
472 		return readbuf[0 .. len];
473 	}
474 	
475 	
476 	///
477 	void[] receive()
478 	{
479 		ubyte[] result;
480 		
481 		result = readbuf[0 .. rpos];
482 		readbuf = null;
483 		rpos = 0;
484 		
485 		return result;
486 	}
487 	
488 	/// ditto
489 	void[] receive(uint len)
490 	{
491 		if(len >= rpos)
492 			return receive();
493 		
494 		ubyte[] result;
495 		
496 		result = readbuf[0 .. len];
497 		readbuf = readbuf[len .. readbuf.length];
498 		rpos -= len;
499 		
500 		return result;
501 	}
502 	
503 	
504 	///
505 	void send(void[] buf)
506 	{
507 		if(canwrite)
508 		{
509 			assert(!writebuf.length);
510 			
511 			size_t st;
512 			if(buf.length > 4096)
513 				st = 4096;
514 			else
515 				st = buf.length;
516 			
517 			st = sock.send(buf[0 .. st]);
518 			if(st > 0)
519 			{
520 				if(buf.length - st)
521 				{
522 					// dup so it can be appended to.
523 					writebuf = (cast(ubyte[])buf)[st .. buf.length].dup;
524 				}
525 			}
526 			else
527 			{
528 				// dup so it can be appended to.
529 				writebuf = (cast(ubyte[])buf).dup;
530 			}
531 			
532 			//canwrite = false;
533 		}
534 		else
535 		{
536 			writebuf ~= cast(ubyte[])buf;
537 		}
538 	}
539 	
540 	
541 	///
542 	// Number of bytes in send queue.
543 	@property size_t sendBytes() // getter
544 	{
545 		return writebuf.length;
546 	}
547 	
548 	
549 	///
550 	// Number of bytes in recv queue.
551 	@property uint receiveBytes() // getter
552 	{
553 		return rpos;
554 	}
555 	
556 	
557 	///
558 	// Same signature as RegisterEventCallback for simplicity.
559 	void event(DflSocket _sock, EventType type, int err)
560 	in
561 	{
562 		assert(_sock is sock);
563 	}
564 	body
565 	{
566 		switch(type)
567 		{
568 			case EventType.READ:
569 				readEvent();
570 				break;
571 			
572 			case EventType.WRITE:
573 				writeEvent();
574 				break;
575 			
576 			default:
577 		}
578 	}
579 	
580 	
581 	///
582 	// Call on a read event so that incoming data may be buffered.
583 	void readEvent()
584 	{
585 		if(readbuf.length - rpos < 1024)
586 			readbuf.length = readbuf.length + 2048;
587 		
588 		size_t rd = sock.receive(readbuf[rpos .. readbuf.length]);
589 		if(rd > 0)
590 			rpos += cast(uint)rd;
591 	}
592 	
593 	
594 	///
595 	// Call on a write event so that buffered outgoing data may be sent.
596 	void writeEvent()
597 	{
598 		if(writebuf.length)
599 		{
600 			ubyte[] buf;
601 			
602 			if(writebuf.length > 4096)
603 				buf = writebuf[0 .. 4096];
604 			else
605 				buf = writebuf;
606 			
607 			size_t st = sock.send(buf);
608 			if(st > 0)
609 				writebuf = writebuf[st .. writebuf.length];
610 		}
611 		else
612 		{
613 			//canwrite = true;
614 		}
615 	}
616 	
617 	
618 	deprecated
619 	{
620 		alias receiveBytes recvBytes;
621 		alias receive recv;
622 	}
623 	
624 	
625 	private:
626 	ubyte[] writebuf;
627 	ubyte[] readbuf;
628 	uint rpos;
629 	DflSocket sock;
630 	//bool canwrite = false;
631 	
632 	
633 	@property bool canwrite() // getter
634 	{
635 		return writebuf.length == 0;
636 	}
637 }
638 
639 
640 private:
641 
642 struct EventInfo
643 {
644 	DflSocket sock;
645 	RegisterEventCallback callback;
646 	DThrowable exception;
647 }
648 
649 
650 enum UINT WM_DFL_NETEVENT = WM_USER + 104;
651 enum UINT WM_DFL_HOSTEVENT = WM_USER + 105;
652 enum NETEVENT_CLASSNAME = "DFL_NetEvent";
653 
654 EventInfo[socket_t] allEvents;
655 GetHost[HANDLE] allGetHosts;
656 HWND hwNet;
657 
658 
659 extern(Windows) LRESULT netWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow
660 {
661 	switch(msg)
662 	{
663 		case WM_DFL_NETEVENT:
664 			if(cast(socket_t)wparam in allEvents)
665 			{
666 				EventInfo ei = allEvents[cast(socket_t)wparam];
667 				try
668 				{
669 					ei.callback(ei.sock, cast(EventType)LOWORD(lparam), HIWORD(lparam));
670 				}
671 				catch (DThrowable e)
672 				{
673 					ei.exception = e;
674 				}
675 			}
676 			break;
677 		
678 		case WM_DFL_HOSTEVENT:
679 			if(cast(HANDLE)wparam in allGetHosts)
680 			{
681 				GetHost gh;
682 				gh = allGetHosts[cast(HANDLE)wparam];
683 				assert(gh !is null);
684 				//delete allGetHosts[cast(HANDLE)wparam];
685 				allGetHosts.remove(cast(HANDLE)wparam);
686 				try
687 				{
688 					gh._gotEvent(lparam);
689 				}
690 				catch (DThrowable e)
691 				{
692 					gh.exception = e;
693 				}
694 			}
695 			break;
696 		
697 		default:
698 	}
699 	
700 	return 1;
701 }
702 
703 
704 void _init()
705 {
706 	WNDCLASSEXA wce;
707 	wce.cbSize = wce.sizeof;
708 	wce.lpszClassName = NETEVENT_CLASSNAME.ptr;
709 	wce.lpfnWndProc = &netWndProc;
710 	wce.hInstance = GetModuleHandleA(null);
711 	
712 	if(!RegisterClassExA(&wce))
713 	{
714 		debug(APP_PRINT)
715 			cprintf("RegisterClassEx() failed for network event class.\n");
716 		
717 		init_err:
718 		throw new DflException("Unable to initialize asynchronous socket library");
719 	}
720 	
721 	hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, HWND_MESSAGE, null, wce.hInstance, null);
722 	if(!hwNet)
723 	{
724 		// Guess it doesn't support HWND_MESSAGE, so just try null parent.
725 		
726 		hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, null, null, wce.hInstance, null);
727 		if(!hwNet)
728 		{
729 			debug(APP_PRINT)
730 				cprintf("CreateWindowEx() failed for network event window.\n");
731 			
732 			goto init_err;
733 		}
734 	}
735 }
736 
737 }
738