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 }