Use sockets with new IP Address structs
This commit is contained in:
		| @@ -35,9 +35,6 @@ type information at compile-time, to transform from one type to another. It has | ||||
| also different algorithms for iterating, searching and modifying template | ||||
| arguments. | ||||
| * `net`: URL-Parsing, network programming. | ||||
| * `network`: Socket implementation. `network` is currently under rework. | ||||
| After finishing the new socket implementation will land in the `net` package and | ||||
| `network` will be deprecated. | ||||
| * `os`: Platform-independent interfaces to operating system functionality. | ||||
| * `range`: Generic functions and templates for D ranges. | ||||
| * `test`: Test suite for unittest-blocks. | ||||
| @@ -167,7 +164,7 @@ parameter is used) | ||||
|  | ||||
| | DMD     | GCC       | | ||||
| |:-------:|:---------:| | ||||
| | 2.091.1 | gdc trunk | | ||||
| | 2.096.0 | 10.3      | | ||||
|  | ||||
| ## Further characteristics | ||||
|  | ||||
|   | ||||
| @@ -31,7 +31,7 @@ import tanya.async.transport; | ||||
| import tanya.async.watcher; | ||||
| import tanya.container.array; | ||||
| import tanya.memory.allocator; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| extern (C) nothrow @nogc | ||||
| { | ||||
|   | ||||
| @@ -26,7 +26,7 @@ import tanya.async.watcher; | ||||
| import tanya.container.array; | ||||
| import tanya.container.buffer; | ||||
| import tanya.memory.allocator; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| /** | ||||
|  * Transport for stream sockets. | ||||
|   | ||||
| @@ -34,7 +34,8 @@ | ||||
|  * | ||||
|  * void main() | ||||
|  * { | ||||
|  *     auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); | ||||
|  *     auto address = address4("127.0.0.1"); | ||||
|  *     auto endpoint = Endpoint(address.get, cast(ushort) 8192); | ||||
|  *     | ||||
|  *     version (Windows) | ||||
|  *     { | ||||
| @@ -46,7 +47,7 @@ | ||||
|  *         sock.blocking = false; | ||||
|  *     } | ||||
|  * | ||||
|  *     sock.bind(address); | ||||
|  *     sock.bind(endpoint); | ||||
|  *     sock.listen(5); | ||||
|  *     | ||||
|  *     auto io = defaultAllocator.make!ConnectionWatcher(sock); | ||||
| @@ -58,7 +59,6 @@ | ||||
|  *     sock.shutdown(); | ||||
|  *     defaultAllocator.dispose(io); | ||||
|  *     defaultAllocator.dispose(sock); | ||||
|  *     defaultAllocator.dispose(address); | ||||
|  * } | ||||
|  * --- | ||||
|  * | ||||
| @@ -78,7 +78,7 @@ import tanya.bitmanip; | ||||
| import tanya.container.buffer; | ||||
| import tanya.container.list; | ||||
| import tanya.memory.allocator; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| version (DisableBackends) | ||||
| { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| module tanya.async.protocol; | ||||
|  | ||||
| import tanya.async.transport; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| /** | ||||
|  * Common protocol interface. | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| module tanya.async.transport; | ||||
|  | ||||
| import tanya.async.protocol; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| /** | ||||
|  * Base transport interface. | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import tanya.async.transport; | ||||
| import tanya.container.buffer; | ||||
| import tanya.container.list; | ||||
| import tanya.memory.allocator; | ||||
| import tanya.network.socket; | ||||
| import tanya.net.socket; | ||||
|  | ||||
| /** | ||||
|  * A watcher is an opaque structure that you allocate and register to record | ||||
|   | ||||
| @@ -142,3 +142,23 @@ String indexToName(uint index) @nogc nothrow @trusted | ||||
|         return String(findNullTerminated(buffer)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects | ||||
|  * the protocol family which will be used for communication. | ||||
|  */ | ||||
| enum AddressFamily : int | ||||
| { | ||||
|     unspec    = 0,     /// Unspecified. | ||||
|     local     = 1,     /// Local to host (pipes and file-domain). | ||||
|     unix      = local, /// POSIX name for PF_LOCAL. | ||||
|     inet      = 2,     /// IP protocol family. | ||||
|     ax25      = 3,     /// Amateur Radio AX.25. | ||||
|     ipx       = 4,     /// Novell Internet Protocol. | ||||
|     appletalk = 5,     /// Appletalk DDP. | ||||
|     netrom    = 6,     /// Amateur radio NetROM. | ||||
|     bridge    = 7,     /// Multiprotocol bridge. | ||||
|     atmpvc    = 8,     /// ATM PVCs. | ||||
|     x25       = 9,     /// Reserved for X.25 project. | ||||
|     inet6     = 10,    /// IP version 6. | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,6 @@ | ||||
|  */ | ||||
| module tanya.net.ip; | ||||
|  | ||||
| import core.sys.posix.sys.socket; | ||||
| import std.algorithm.comparison; | ||||
| import std.ascii; | ||||
| import std.typecons; | ||||
| @@ -1331,7 +1330,7 @@ struct Address | ||||
|  */ | ||||
| struct Endpoint | ||||
| { | ||||
|     private sa_family_t family = AF_UNSPEC; | ||||
|     private AddressFamily family = AddressFamily.unspec; | ||||
|     private ubyte[ushort.sizeof] service; | ||||
|     private Address4 address4; // Unused sin6_flowinfo if IPv6 | ||||
|     private Address6 address6; // Unused if IPv4 | ||||
| @@ -1345,10 +1344,12 @@ struct Endpoint | ||||
|      * Constructs an endpoint. | ||||
|      * | ||||
|      * Params: | ||||
|      *     T = Address type (IPv4 or IPv6). | ||||
|      *     address = IP address that should be associated with the endpoint. | ||||
|      *     port = Port number in network byte order. | ||||
|      */ | ||||
|     this(Address address, const ushort port = anyPort) | ||||
|     this(T)(T address, const ushort port = anyPort) | ||||
|     if (is(T == Address) || is(T == Address4) || is(T == Address6)) | ||||
|     { | ||||
|         this.address = address; | ||||
|         this.port = port; | ||||
| @@ -1357,7 +1358,7 @@ struct Endpoint | ||||
|     /** | ||||
|      * Returns: Port number in network byte order. | ||||
|      */ | ||||
|     @property ushort port() const @nogc nothrow pure @safe | ||||
|     @property inout(ushort) port() inout const @nogc nothrow pure @safe | ||||
|     { | ||||
|         return this.service[].toHostOrder!ushort(); | ||||
|     } | ||||
| @@ -1374,13 +1375,13 @@ struct Endpoint | ||||
|     /** | ||||
|      * Returns: IP address associated with the endpoint. | ||||
|      */ | ||||
|     @property Address address() @nogc nothrow pure @safe | ||||
|     @property inout(Address) address() inout @nogc nothrow pure @safe | ||||
|     { | ||||
|         if (this.family == AF_INET) | ||||
|         if (this.family == AddressFamily.inet) | ||||
|         { | ||||
|             return Address(this.address4); | ||||
|         } | ||||
|         else if (this.family == AF_INET6) | ||||
|         else if (this.family == AddressFamily.inet6) | ||||
|         { | ||||
|             return Address(this.address6); | ||||
|         } | ||||
| @@ -1395,14 +1396,26 @@ struct Endpoint | ||||
|     { | ||||
|         if (address.isV4()) | ||||
|         { | ||||
|             this.family = AF_INET; | ||||
|             this.address4 = address.toV4(); | ||||
|             this.address = address.toV4(); | ||||
|         } | ||||
|         else if (address.isV6()) | ||||
|         { | ||||
|             this.family = AF_INET6; | ||||
|             this.address4 = Address4(0); | ||||
|             this.address6 = address.toV6(); | ||||
|             this.address = address.toV6(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// ditto | ||||
|     @property void address(Address4 address) @nogc nothrow pure @safe | ||||
|     { | ||||
|         this.family = AddressFamily.inet; | ||||
|         this.address4 = address; | ||||
|     } | ||||
|  | ||||
|     /// ditto | ||||
|     @property void address(Address6 address) @nogc nothrow pure @safe | ||||
|     { | ||||
|         this.family = AddressFamily.inet6; | ||||
|         this.address4 = Address4(0); | ||||
|         this.address6 = address; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,4 +17,5 @@ module tanya.net; | ||||
| public import tanya.net.iface; | ||||
| public import tanya.net.inet; | ||||
| public import tanya.net.ip; | ||||
| public import tanya.net.socket; | ||||
| public import tanya.net.uri; | ||||
|   | ||||
| @@ -7,37 +7,6 @@ | ||||
|  * | ||||
|  * Current API supports only server-side TCP communication. | ||||
|  * | ||||
|  * Here is an example of a cross-platform blocking server: | ||||
|  * | ||||
|  * --- | ||||
|  * import std.stdio; | ||||
|  * import tanya.memory; | ||||
|  * import tanya.network; | ||||
|  * | ||||
|  * void main() | ||||
|  * { | ||||
|  *     auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet); | ||||
|  *     auto address = defaultAllocator.make!InternetAddress("127.0.0.1", | ||||
|  *                                                          cast(ushort) 8192); | ||||
|  * | ||||
|  *     socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); | ||||
|  *     socket.blocking = true; | ||||
|  *     socket.bind(address); | ||||
|  *     socket.listen(5); | ||||
|  * | ||||
|  *     auto client = socket.accept(); | ||||
|  *     client.send(cast(const(ubyte)[]) "Test\n"); | ||||
|  * | ||||
|  *     ubyte[100] buf; | ||||
|  *     auto response = client.receive(buf[]); | ||||
|  * | ||||
|  *     writeln(cast(const(char)[]) buf[0 .. response]); | ||||
|  * | ||||
|  *     defaultAllocator.dispose(client); | ||||
|  *     defaultAllocator.dispose(socket); | ||||
|  * } | ||||
|  * --- | ||||
|  * | ||||
|  * For an example of an asynchronous server refer to the documentation of the | ||||
|  * $(D_PSYMBOL tanya.async.loop) module. | ||||
|  * | ||||
| @@ -48,7 +17,7 @@ | ||||
|  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/socket.d,
 | ||||
|  *                 tanya/network/socket.d) | ||||
|  */ | ||||
| module tanya.network.socket; | ||||
| module tanya.net.socket; | ||||
| 
 | ||||
| import core.stdc.errno; | ||||
| import core.time; | ||||
| @@ -57,6 +26,8 @@ import tanya.bitmanip; | ||||
| import tanya.memory.allocator; | ||||
| import tanya.meta.trait; | ||||
| import tanya.os.error; | ||||
| import tanya.net.iface; | ||||
| import tanya.net.ip; | ||||
| 
 | ||||
| /// Value returned by socket operations on error.
 | ||||
| enum int socketError = -1; | ||||
| @@ -588,26 +559,6 @@ shared static this() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects | ||||
|  * the protocol family which will be used for communication. | ||||
|  */ | ||||
| enum AddressFamily : int | ||||
| { | ||||
|     unspec    = 0,     /// Unspecified.
 | ||||
|     local     = 1,     /// Local to host (pipes and file-domain).
 | ||||
|     unix      = local, /// POSIX name for PF_LOCAL.
 | ||||
|     inet      = 2,     /// IP protocol family.
 | ||||
|     ax25      = 3,     /// Amateur Radio AX.25.
 | ||||
|     ipx       = 4,     /// Novell Internet Protocol.
 | ||||
|     appletalk = 5,     /// Appletalk DDP.
 | ||||
|     netrom    = 6,     /// Amateur radio NetROM.
 | ||||
|     bridge    = 7,     /// Multiprotocol bridge.
 | ||||
|     atmpvc    = 8,     /// ATM PVCs.
 | ||||
|     x25       = 9,     /// Reserved for X.25 project.
 | ||||
|     inet6     = 10,    /// IP version 6.
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions | ||||
|  * $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because | ||||
| @@ -1064,13 +1015,13 @@ class StreamSocket : Socket, ConnectionOrientedSocket | ||||
|      * Associate a local address with this socket. | ||||
|      * | ||||
|      * Params: | ||||
|      *  address = Local address. | ||||
|      *  endpoint = Local address. | ||||
|      * | ||||
|      * Throws: $(D_PSYMBOL SocketException) if unable to bind. | ||||
|      */ | ||||
|     void bind(Address address) const @trusted @nogc | ||||
|     void bind(const Endpoint endpoint) const @nogc | ||||
|     { | ||||
|         if (.bind(handle_, address.name, address.length) == socketError) | ||||
|         if (.bind(handle_, cast(const(sockaddr)*) &endpoint, Endpoint.sizeof)) | ||||
|         { | ||||
|             throw defaultAllocator.make!SocketException("Unable to bind socket"); | ||||
|         } | ||||
| @@ -1268,157 +1219,6 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Socket address representation. | ||||
|  */ | ||||
| abstract class Address | ||||
| { | ||||
|     /** | ||||
|      * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. | ||||
|      */ | ||||
|     abstract @property inout(sockaddr)* name() inout pure nothrow @nogc; | ||||
| 
 | ||||
|     /** | ||||
|      * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. | ||||
|      */ | ||||
|     abstract @property inout(socklen_t) length() inout const pure nothrow @nogc; | ||||
| } | ||||
| 
 | ||||
| class InternetAddress : Address | ||||
| { | ||||
|     version (Windows) | ||||
|     { | ||||
|         /// Internal internet address representation.
 | ||||
|         protected SOCKADDR_STORAGE storage; | ||||
|     } | ||||
|     else version (Posix) | ||||
|     { | ||||
|         /// Internal internet address representation.
 | ||||
|         protected sockaddr_storage storage; | ||||
|     } | ||||
|     const ushort port_; | ||||
| 
 | ||||
|     enum ushort anyPort = 0; | ||||
| 
 | ||||
|     this(string host, const ushort port = anyPort) @nogc | ||||
|     { | ||||
|         if (getaddrinfoPointer is null || freeaddrinfoPointer is null) | ||||
|         { | ||||
|             throw make!SocketException(defaultAllocator, | ||||
|                                        "Address info lookup is not available on this system"); | ||||
|         } | ||||
|         addrinfo* ai_res; | ||||
|         this.port_ = port; | ||||
| 
 | ||||
|         // Make C-string from host.
 | ||||
|         auto node = cast(char[]) allocator.allocate(host.length + 1); | ||||
|         node[0 .. $ - 1] = host; | ||||
|         node[$ - 1] = '\0'; | ||||
|         scope (exit) | ||||
|         { | ||||
|             allocator.deallocate(node); | ||||
|         } | ||||
| 
 | ||||
|         // Convert port to a C-string.
 | ||||
|         char[6] service = [0, 0, 0, 0, 0, 0]; | ||||
|         const(char)* servicePointer; | ||||
|         if (port) | ||||
|         { | ||||
|             ushort originalPort = port; | ||||
|             ushort start; | ||||
|             for (ushort j = 10, i = 4; i > 0; j *= 10, --i) | ||||
|             { | ||||
|                 ushort rest = originalPort % 10; | ||||
|                 if (rest != 0) | ||||
|                 { | ||||
|                     service[i] = cast(char) (rest + '0'); | ||||
|                     start = i; | ||||
|                 } | ||||
|                 originalPort /= 10; | ||||
|             } | ||||
|             servicePointer = service[start .. $].ptr; | ||||
|         } | ||||
| 
 | ||||
|         auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res); | ||||
|         if (ret) | ||||
|         { | ||||
|             throw defaultAllocator.make!SocketException("Address info lookup failed"); | ||||
|         } | ||||
|         scope (exit) | ||||
|         { | ||||
|             freeaddrinfoPointer(ai_res); | ||||
|         } | ||||
| 
 | ||||
|         ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr; | ||||
|         for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++) | ||||
|         { | ||||
|             *dp = *sp; | ||||
|         } | ||||
|         if (ai_res.ai_family != AddressFamily.inet && ai_res.ai_family != AddressFamily.inet6) | ||||
|         { | ||||
|             throw defaultAllocator.make!SocketException("Wrong address family"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ///
 | ||||
|     unittest | ||||
|     { | ||||
|         auto address = defaultAllocator.make!InternetAddress("127.0.0.1"); | ||||
|         assert(address.port == InternetAddress.anyPort); | ||||
|         assert(address.name !is null); | ||||
|         assert(address.family == AddressFamily.inet); | ||||
| 
 | ||||
|         defaultAllocator.dispose(address); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure. | ||||
|      */ | ||||
|     override @property inout(sockaddr)* name() inout pure nothrow @nogc | ||||
|     { | ||||
|         return cast(sockaddr*) &storage; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure. | ||||
|      */ | ||||
|     override @property inout(socklen_t) length() inout const pure nothrow @nogc | ||||
|     { | ||||
|         // FreeBSD wants to know the exact length of the address on bind.
 | ||||
|         switch (family) | ||||
|         { | ||||
|             case AddressFamily.inet: | ||||
|                 return sockaddr_in.sizeof; | ||||
|             case AddressFamily.inet6: | ||||
|                 return sockaddr_in6.sizeof; | ||||
|             default: | ||||
|                 assert(false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns: Family of this address. | ||||
|      */ | ||||
|     @property inout(AddressFamily) family() inout const pure nothrow @nogc | ||||
|     { | ||||
|         return cast(AddressFamily) storage.ss_family; | ||||
|     } | ||||
| 
 | ||||
|     @property inout(ushort) port() inout const pure nothrow @nogc | ||||
|     { | ||||
|         return port_; | ||||
|     } | ||||
| 
 | ||||
|     ///
 | ||||
|     unittest | ||||
|     { | ||||
|         auto address = defaultAllocator.make!InternetAddress("127.0.0.1", | ||||
|                                                              cast(ushort) 1234); | ||||
|         assert(address.port == 1234); | ||||
|         defaultAllocator.dispose(address); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Checks if the last error is a serious error or just a special | ||||
|  * behaviour error of non-blocking sockets (for example an error | ||||
| @@ -1,17 +0,0 @@ | ||||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
|  | ||||
| /** | ||||
|  * Network programming. | ||||
|  * | ||||
|  * Copyright: Eugene Wissner 2016-2020. | ||||
|  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, | ||||
|  *                  Mozilla Public License, v. 2.0). | ||||
|  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) | ||||
|  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/package.d, | ||||
|  *                 tanya/network/package.d) | ||||
|  */ | ||||
| module tanya.network; | ||||
|  | ||||
| public import tanya.network.socket; | ||||
		Reference in New Issue
	
	Block a user