1 /**
2 	TCP/UDP connection and server handling.
3 
4 	Copyright: © 2012-2014 RejectedSoftware e.K.
5 	Authors: Sönke Ludwig
6 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
7 */
8 module vibe.core.net;
9 
10 public import vibe.core.stream;
11 public import std.socket : AddressFamily;
12 
13 import vibe.core.driver;
14 import vibe.core.log;
15 
16 import core.sys.posix.netinet.in_;
17 import core.time;
18 import std.exception;
19 import std.functional;
20 import std.string;
21 
22 
23 @safe:
24 
25 
26 /**
27 	Resolves the given host name/IP address string.
28 
29 	Setting use_dns to false will only allow IP address strings but also guarantees
30 	that the call will not block.
31 */
32 NetworkAddress resolveHost(string host, AddressFamily address_family = AddressFamily.UNSPEC, bool use_dns = true)
33 {
34 	return resolveHost(host, cast(ushort)address_family, use_dns);
35 }
36 /// ditto
37 NetworkAddress resolveHost(string host, ushort address_family, bool use_dns = true)
38 {
39 	return getEventDriver().resolveHost(host, address_family, use_dns);
40 }
41 
42 
43 /**
44 	Starts listening on the specified port.
45 
46 	'connection_callback' will be called for each client that connects to the
47 	server socket. Each new connection gets its own fiber. The stream parameter
48 	then allows to perform blocking I/O on the client socket.
49 
50 	The address parameter can be used to specify the network
51 	interface on which the server socket is supposed to listen for connections.
52 	By default, all IPv4 and IPv6 interfaces will be used.
53 
54 	Using a `@system` callback is scheduled for deprecation. Use a `@safe`
55 	callback instead.
56 */
57 TCPListener[] listenTCP(ushort port, void delegate(TCPConnection stream) @safe connection_callback, TCPListenOptions options = TCPListenOptions.defaults)
58 {
59 	TCPListener[] ret;
60 	try ret ~= listenTCP(port, connection_callback, "::", options);
61 	catch (Exception e) logDiagnostic("Failed to listen on \"::\": %s", e.msg);
62 	try ret ~= listenTCP(port, connection_callback, "0.0.0.0", options);
63 	catch (Exception e) logDiagnostic("Failed to listen on \"0.0.0.0\": %s", e.msg);
64 	enforce(ret.length > 0, format("Failed to listen on all interfaces on port %s", port));
65 	return ret;
66 }
67 /// ditto
68 TCPListener listenTCP(ushort port, void delegate(TCPConnection stream) @safe connection_callback, string address, TCPListenOptions options = TCPListenOptions.defaults)
69 {
70 	return getEventDriver().listenTCP(port, connection_callback, address, options);
71 }
72 /// ditto
73 TCPListener[] listenTCP(ushort port, void delegate(TCPConnection stream) @system connection_callback, TCPListenOptions options = TCPListenOptions.defaults)
74 @system {
75 	return listenTCP(port, (s) @trusted => connection_callback(s), options);
76 }
77 /// ditto
78 TCPListener listenTCP(ushort port, void delegate(TCPConnection stream) @system connection_callback, string address, TCPListenOptions options = TCPListenOptions.defaults)
79 @system {
80 	return listenTCP(port, (s) @trusted => connection_callback(s), address, options);
81 }
82 
83 
84 /**
85 	Starts listening on the specified port.
86 
87 	This function is the same as listenTCP but takes a function callback instead of a delegate.
88 */
89 TCPListener[] listenTCP_s(ushort port, void function(TCPConnection stream) @safe connection_callback, TCPListenOptions options = TCPListenOptions.defaults)
90 {
91 	return listenTCP(port, () @trusted { return toDelegate(connection_callback); } (), options);
92 }
93 /// ditto
94 TCPListener listenTCP_s(ushort port, void function(TCPConnection stream) @safe connection_callback, string address, TCPListenOptions options = TCPListenOptions.defaults)
95 {
96 	return listenTCP(port, () @trusted { return toDelegate(connection_callback); } (), address, options);
97 }
98 
99 /**
100 	Establishes a connection to the given host/port.
101 */
102 TCPConnection connectTCP(string host, ushort port, string bind_interface = null, ushort bind_port = 0)
103 {
104 	NetworkAddress addr = resolveHost(host);
105 	if (addr.family != AddressFamily.UNIX)
106 		addr.port = port;
107 	NetworkAddress bind_address;
108 	if (bind_interface.length) bind_address = resolveHost(bind_interface, addr.family);
109 	else {
110 		bind_address.family = addr.family;
111 		if (bind_address.family == AddressFamily.INET) bind_address.sockAddrInet4.sin_addr.s_addr = 0;
112 		else if (bind_address.family != AddressFamily.UNIX) bind_address.sockAddrInet6.sin6_addr.s6_addr[] = 0;
113 	}
114 	if (addr.family != AddressFamily.UNIX)
115 		bind_address.port = bind_port;
116 	return getEventDriver().connectTCP(addr, bind_address);
117 }
118 /// ditto
119 TCPConnection connectTCP(NetworkAddress addr, NetworkAddress bind_address = anyAddress)
120 {
121 	if (bind_address.family == AddressFamily.UNSPEC) {
122 		bind_address.family = addr.family;
123 		if (bind_address.family == AddressFamily.INET) bind_address.sockAddrInet4.sin_addr.s_addr = 0;
124 		else if (bind_address.family != AddressFamily.UNIX) bind_address.sockAddrInet6.sin6_addr.s6_addr[] = 0;
125 		if (bind_address.family != AddressFamily.UNIX)
126 			bind_address.port = 0;
127 	}
128 	enforce(addr.family == bind_address.family, "Destination address and bind address have different address families.");
129 	return getEventDriver().connectTCP(addr, bind_address);
130 }
131 
132 
133 /**
134 	Creates a bound UDP socket suitable for sending and receiving packets.
135 */
136 UDPConnection listenUDP(ushort port, string bind_address = "0.0.0.0")
137 {
138 	return getEventDriver().listenUDP(port, bind_address);
139 }
140 
141 NetworkAddress anyAddress()
142 {
143 	NetworkAddress ret;
144 	ret.family = AddressFamily.UNSPEC;
145 	return ret;
146 }
147 
148 /**
149 	Represents a network/socket address.
150 
151 	To construct a `NetworkAddress`, use either `resolveHost` or set the
152 	`family` property accordingly, followed by setting the fields of
153 	`sockAddrInet4`/`sockAddrInet6`/`sockAddrUnix`.
154 */
155 struct NetworkAddress {
156 	version(Windows) {
157 		import core.sys.windows.winsock2 : sockaddr, sockaddr_in, sockaddr_in6;
158 	}
159 	version(Posix)
160 	{
161 		import core.sys.posix.sys.un : sockaddr_un;
162 	}
163 
164 	@safe:
165 
166 	private union {
167 		sockaddr addr;
168 		version (Posix) sockaddr_un addr_unix;
169 		sockaddr_in addr_ip4;
170 		sockaddr_in6 addr_ip6;
171 	}
172 
173 	version(VibeLibasyncDriver) {
174 		static import libasync.events;
175 		this(libasync.events.NetworkAddress addr)
176 		@trusted {
177 			this.family = addr.family;
178 			switch (addr.family) {
179 				default: assert(false, "Got unsupported address family from libasync.");
180 				case AddressFamily.INET: this.addr_ip4 = *addr.sockAddrInet4; break;
181 				case AddressFamily.INET6: this.addr_ip6 = *addr.sockAddrInet6; break;
182 			}
183 		}
184 
185 		T opCast(T)() @trusted
186 			if (is(T == libasync.events.NetworkAddress))
187 		{
188 			T ret;
189 			ret.family = this.family;
190 			(cast(ubyte*)ret.sockAddr)[0 .. this.sockAddrLen] = (cast(ubyte*)this.sockAddr)[0 .. this.sockAddrLen];
191 			return ret;
192 		}
193 	}
194 
195 
196 	/** Family of the socket address.
197 	*/
198 	@property ushort family() const pure nothrow { return addr.sa_family; }
199 	/// ditto
200 	@property void family(AddressFamily val) pure nothrow { addr.sa_family = cast(ubyte)val; }
201 	/// ditto
202 	@property void family(ushort val) pure nothrow { addr.sa_family = cast(ubyte)val; }
203 
204 	/** The port in host byte order.
205 	*/
206 	@property ushort port()
207 	const pure nothrow {
208 		ushort nport;
209 		switch (this.family) {
210 			default: assert(false, "port() called for invalid address family.");
211 			case AddressFamily.INET: nport = addr_ip4.sin_port; break;
212 			case AddressFamily.INET6: nport = addr_ip6.sin6_port; break;
213 		}
214 		return () @trusted { return ntoh(nport); } ();
215 	}
216 	/// ditto
217 	@property void port(ushort val)
218 	pure nothrow {
219 		auto nport = () @trusted { return hton(val); } ();
220 		switch (this.family) {
221 			default: assert(false, "port() called for invalid address family.");
222 			case AddressFamily.INET: addr_ip4.sin_port = nport; break;
223 			case AddressFamily.INET6: addr_ip6.sin6_port = nport; break;
224 		}
225 	}
226 
227 	/** A pointer to a sockaddr struct suitable for passing to socket functions.
228 	*/
229 	@property inout(sockaddr)* sockAddr() inout pure nothrow { return &addr; }
230 
231 	/** Size of the sockaddr struct that is returned by sockAddr().
232 	*/
233 	@property int sockAddrLen()
234 	const pure nothrow {
235 		switch (this.family) {
236 			default: assert(false, "sockAddrLen() called for invalid address family.");
237 			version (Posix) {
238 				case AddressFamily.UNIX: return addr_unix.sizeof;
239 			}
240 			case AddressFamily.INET: return addr_ip4.sizeof;
241 			case AddressFamily.INET6: return addr_ip6.sizeof;
242 		}
243 	}
244 
245 	@property inout(sockaddr_in)* sockAddrInet4() inout pure nothrow
246 		in { assert (family == AddressFamily.INET); }
247 		body { return &addr_ip4; }
248 
249 	@property inout(sockaddr_in6)* sockAddrInet6() inout pure nothrow
250 		in { assert (family == AddressFamily.INET6); }
251 		body { return &addr_ip6; }
252 
253 	version (Posix) {
254 		@property inout(sockaddr_un)* sockAddrUnix() inout pure nothrow
255 			in { assert (family == AddressFamily.UNIX); }
256 			body { return &addr_unix; }
257 	}
258 
259 	/** Returns a string representation of the IP address
260 	*/
261 	string toAddressString()
262 	const {
263 		import std.array : appender;
264 		auto ret = appender!string();
265 		ret.reserve(40);
266 		toAddressString(str => ret.put(str));
267 		return ret.data;
268 	}
269 	/// ditto
270 	void toAddressString(scope void delegate(const(char)[]) @safe sink)
271 	const {
272 		import std.array : appender;
273 		import std.format : formattedWrite;
274 		ubyte[2] _dummy = void; // Workaround for DMD regression in master
275 
276 		switch (this.family) {
277 			default: assert(false, "toAddressString() called for invalid address family.");
278 			case AddressFamily.INET: {
279 				ubyte[4] ip = () @trusted { return (cast(ubyte*)&addr_ip4.sin_addr.s_addr)[0 .. 4]; } ();
280 				sink.formattedWrite("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
281 				} break;
282 			case AddressFamily.INET6: {
283 				ubyte[16] ip = addr_ip6.sin6_addr.s6_addr;
284 				foreach (i; 0 .. 8) {
285 					if (i > 0) sink(":");
286 					_dummy[] = ip[i*2 .. i*2+2];
287 					sink.formattedWrite("%x", bigEndianToNative!ushort(_dummy));
288 				}
289 				} break;
290 			version (Posix) {
291 				case AddressFamily.UNIX:
292 					import std.traits : hasMember;
293 					static if (hasMember!(sockaddr_un, "sun_len"))
294 						sink.formattedWrite("%s",() @trusted { return cast(char[])addr_unix.sun_path[0..addr_unix.sun_len]; } ());
295 					else
296 						sink.formattedWrite("%s",() @trusted { return (cast(char*)addr_unix.sun_path.ptr).fromStringz; } ());
297 					break;
298 			}
299 		}
300 	}
301 
302 	/** Returns a full string representation of the address, including the port number.
303 	*/
304 	string toString()
305 	const {
306 		import std.array : appender;
307 		auto ret = appender!string();
308 		toString(str => ret.put(str));
309 		return ret.data;
310 	}
311 	/// ditto
312 	void toString(scope void delegate(const(char)[]) @safe sink)
313 	const {
314 		import std.format : formattedWrite;
315 		switch (this.family) {
316 			default: assert(false, "toString() called for invalid address family.");
317 			case AddressFamily.INET:
318 				toAddressString(sink);
319 				sink.formattedWrite(":%s", port);
320 				break;
321 			case AddressFamily.INET6:
322 				sink("[");
323 				toAddressString(sink);
324 				sink.formattedWrite("]:%s", port);
325 				break;
326 			case AddressFamily.UNIX:
327 				toAddressString(sink);
328 				break;
329 		}
330 	}
331 
332 	unittest {
333 		void test(string ip) {
334 			auto res = () @trusted { return resolveHost(ip, AddressFamily.UNSPEC, false); } ().toAddressString();
335 			assert(res == ip,
336 				   "IP "~ip~" yielded wrong string representation: "~res);
337 		}
338 		test("1.2.3.4");
339 		test("102:304:506:708:90a:b0c:d0e:f10");
340 	}
341 }
342 
343 
344 /**
345 	Represents a single TCP connection.
346 */
347 interface TCPConnection : ConnectionStream {
348 	/// Used to disable Nagle's algorithm.
349 	@property void tcpNoDelay(bool enabled);
350 	/// ditto
351 	@property bool tcpNoDelay() const;
352 
353 
354 	/// Enables TCP keep-alive packets.
355 	@property void keepAlive(bool enable);
356 	/// ditto
357 	@property bool keepAlive() const;
358 
359 	/// Controls the read time out after which the connection is closed automatically.
360 	@property void readTimeout(Duration duration);
361 	/// ditto
362 	@property Duration readTimeout() const;
363 
364 	/// Returns the IP address of the connected peer.
365 	@property string peerAddress() const;
366 
367 	/// The local/bind address of the underlying socket.
368 	@property NetworkAddress localAddress() const;
369 
370 	/// The address of the connected peer.
371 	@property NetworkAddress remoteAddress() const;
372 }
373 
374 
375 /**
376 	Represents a listening TCP socket.
377 */
378 interface TCPListener {
379 	/// The local address at which TCP connections are accepted.
380 	@property NetworkAddress bindAddress();
381 
382 	/// Stops listening and closes the socket.
383 	void stopListening();
384 }
385 
386 
387 /**
388 	Represents a bound and possibly 'connected' UDP socket.
389 */
390 interface UDPConnection {
391 	/** Returns the address to which the UDP socket is bound.
392 	*/
393 	@property string bindAddress() const;
394 
395 	/** Determines if the socket is allowed to send to broadcast addresses.
396 	*/
397 	@property bool canBroadcast() const;
398 	/// ditto
399 	@property void canBroadcast(bool val);
400 
401 	/// The local/bind address of the underlying socket.
402 	@property NetworkAddress localAddress() const;
403 
404 	/** Stops listening for datagrams and frees all resources.
405 	*/
406 	void close();
407 
408 	/** Locks the UDP connection to a certain peer.
409 
410 		Once connected, the UDPConnection can only communicate with the specified peer.
411 		Otherwise communication with any reachable peer is possible.
412 	*/
413 	void connect(string host, ushort port);
414 	/// ditto
415 	void connect(NetworkAddress address);
416 
417 	/** Sends a single packet.
418 
419 		If peer_address is given, the packet is send to that address. Otherwise the packet
420 		will be sent to the address specified by a call to connect().
421 	*/
422 	void send(in ubyte[] data, in NetworkAddress* peer_address = null);
423 
424 	/** Receives a single packet.
425 
426 		If a buffer is given, it must be large enough to hold the full packet.
427 
428 		The timeout overload will throw an Exception if no data arrives before the
429 		specified duration has elapsed.
430 	*/
431 	ubyte[] recv(ubyte[] buf = null, NetworkAddress* peer_address = null);
432 	/// ditto
433 	ubyte[] recv(Duration timeout, ubyte[] buf = null, NetworkAddress* peer_address = null);
434 
435 	/** Become member of IP multicast group
436 
437 		The multiaddr parameter should be in the range 239.0.0.0-239.255.255.255.
438 		See https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml#multicast-addresses-12
439 		and https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
440 	*/
441 	void addMembership(ref NetworkAddress multiaddr);
442 
443 	/** Set IP multicast loopback
444 
445 		This is on by default.
446 		All packets send will also loopback if enabled.
447 		Useful if more than one application is running on same host and both need each other's packets.
448 	*/
449 	@property void multicastLoopback(bool loop);
450 }
451 
452 
453 /**
454 	Flags to control the behavior of listenTCP.
455 */
456 enum TCPListenOptions {
457 	/// Don't enable any particular option
458 	defaults = 0,
459 	/// Causes incoming connections to be distributed across the thread pool
460 	distribute = 1<<0,
461 	/// Disables automatic closing of the connection when the connection callback exits
462 	disableAutoClose = 1<<1,
463 	/** Enable port reuse on linux kernel version >=3.9, do nothing on other OS
464 	    Does not affect libasync driver because it is always enabled by libasync.
465 	*/
466 	reusePort = 1<<2,
467 }
468 
469 private pure nothrow {
470 	import std.bitmanip;
471 
472 	ushort ntoh(ushort val)
473 	{
474 		version (LittleEndian) return swapEndian(val);
475 		else version (BigEndian) return val;
476 		else static assert(false, "Unknown endianness.");
477 	}
478 
479 	ushort hton(ushort val)
480 	{
481 		version (LittleEndian) return swapEndian(val);
482 		else version (BigEndian) return val;
483 		else static assert(false, "Unknown endianness.");
484 	}
485 }