1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  * $(U IpAddress) is a utility class for manipulating network addresses. It
27  * provides a set a implicit constructors and conversion functions to easily
28  * build or transform an IP address from/to various representations.
29  *
30  *
31  * Note that $(U IpAddress) currently doesn't support IPv6 nor other types of
32  * network addresses.
33  * Example:
34  * ---
35  * // an invalid address
36  * IpAddress a0;
37  *
38  * // an invalid address (same as a0)
39  * IpAddress a1 = IpAddress.None;
40  *
41  * // the local host address
42  * IpAddress a2 = IpAddress("127.0.0.1");
43  *
44  * // the broadcast address
45  * IpAddress a3 = IpAddress.Broadcast;
46  *
47  * // a local address
48  * IpAddress a4 = IpAddress(192, 168, 1, 56);
49  *
50  * // a local address created from a network name
51  * IpAddress a5 = IpAddress("my_computer");
52  *
53  * // a distant address
54  * IpAddress a6 = IpAddress("89.54.1.169");
55  *
56  * // a distant address created from a network name
57  * IpAddress a7("www.google.com");
58  *
59  * // my address on the local network
60  * IpAddress a8 = IpAddress.getLocalAddress();
61  *
62  * // my address on the internet
63  * IpAddress a9 = IpAddress.getPublicAddress();
64  * ---
65  */
66 module dsfml.network.ipaddress;
67 
68 public import core.time;
69 
70 /**
71  * Encapsulate an IPv4 network address.
72  */
73 struct IpAddress
74 {
75     package uint m_address;
76     package bool m_valid;
77 
78     /**
79      * Construct the address from a string.
80      *
81      * Here address can be either a decimal address (ex: "192.168.1.56") or a
82      * network name (ex: "localhost").
83      *
84      * Params:
85      * 		address = IP address or network name.
86      */
87     this(const(char)[] address)
88     {
89         m_address=  htonl(sfIpAddress_integerFromString(address.ptr, address.length));
90         m_valid = true;
91     }
92 
93     /**
94      * Construct the address from 4 bytes.
95      *
96      * Calling `IpAddress(a, b, c, d)` is equivalent to calling
97      * `IpAddress("a.b.c.d")`, but safer as it doesn't have to parse a string to
98      * get the address components.
99      *
100      * Params:
101      * 		byte0 = First byte of the address.
102      * 		byte1 = Second byte of the address.
103      * 		byte2 = Third byte of the address.
104      * 		byte3 = Fourth byte of the address.
105      */
106     this(ubyte byte0,ubyte byte1,ubyte byte2,ubyte byte3)
107     {
108         m_address = htonl((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
109         m_valid = true;
110     }
111 
112     /**
113      * Construct the address from a 32-bits integer.
114      *
115      * This constructor uses the internal representation of the address
116      * directly. It should be used only if you got that representation from
117      * `IpAddress.toInteger()`.
118      *
119      * Params:
120      * 	address = 4 bytes of the address packed into a 32-bits integer
121      */
122     this(uint address)
123     {
124         m_address = htonl(address);
125 
126         m_valid = true;
127     }
128 
129     /**
130      * Get an integer representation of the address.
131      *
132      * The returned number is the internal representation of the address, and
133      * should be used for optimization purposes only (like sending the address
134      * through a socket). The integer produced by this function can then be
135      * converted back to an $(U IpAddress) with the proper constructor.
136      *
137      * Returns: 32-bits unsigned integer representation of the address.
138      */
139     int toInteger() const
140     {
141         return ntohl(m_address);
142     }
143 
144     /**
145      * Get a string representation of the address.
146      *
147      * The returned string is the decimal representation of the IP address
148      * (like "192.168.1.56"), even if it was constructed from a host name.
149      *
150      * This string is built using an internal buffer. If you need to store the
151      * string, make a copy.
152      *
153      * Returns: String representation of the address
154      */
155     const(char)[] toString() const @nogc @trusted
156     {
157         import core.stdc.stdio: sprintf;
158 
159         //internal string buffer to prevent using the GC to build the strings
160         static char[16] m_string;
161 
162         ubyte* bytes = cast(ubyte*)&m_address;
163         int length = sprintf(m_string.ptr, "%d.%d.%d.%d", bytes[0], bytes[1],
164                                                           bytes[2], bytes[3]);
165         return m_string[0..length];
166     }
167 
168     /**
169      * Get the computer's local address.
170      *
171      * The local address is the address of the computer from the LAN point of
172      * view, i.e. something like 192.168.1.56. It is meaningful only for
173      * communications over the local network. Unlike `getPublicAddress`, this
174      * function is fast and may be used safely anywhere.
175      *
176      * Returns: Local IP address of the computer.
177      */
178     static IpAddress getLocalAddress()
179     {
180         IpAddress temp;
181         sfIpAddress_getLocalAddress(&temp);
182         return temp;
183     }
184 
185     /**
186      * Get the computer's public address.
187      *
188      * The public address is the address of the computer from the internet point
189      * of view, i.e. something like 89.54.1.169.
190      *
191      * It is necessary for communications over the world wide web. The only way
192      * to get a public address is to ask it to a distant website; as a
193      * consequence, this function depends on both your network connection and
194      * the server, and may be very slow. You should use it as few as possible.
195      *
196      * Because this function depends on the network connection and on a distant
197      * server, you may use a time limit if you don't want your program to be
198      * possibly stuck waiting in case there is a problem; this limit is
199      * deactivated by default.
200      *
201      * Params:
202      * 	timeout = Maximum time to wait
203      *
204      * Returns: Public IP address of the computer.
205      */
206     static IpAddress getPublicAddress(Duration timeout = Duration.zero())
207     {
208         IpAddress temp;
209         sfIpAddress_getPublicAddress(&temp, timeout.total!"usecs");
210         return temp;
211     }
212 
213     /// Value representing an empty/invalid address.
214     static immutable(IpAddress) None;
215     /// Value representing any address (0.0.0.0)
216     static immutable(IpAddress) Any = IpAddress(0,0,0,0);
217     /// The "localhost" address (for connecting a computer to itself locally)
218     static immutable(IpAddress) LocalHost = IpAddress(127,0,0,1);
219     /// The "broadcast" address (for sending UDP messages to everyone on a local network)
220     static immutable(IpAddress) Broadcast = IpAddress(255,255,255,255);
221 }
222 
223 
224 //these have the same implementation, but use different names for readability
225 private uint htonl(uint host) nothrow @nogc @safe
226 {
227     version(LittleEndian)
228     {
229         import core.bitop;
230         return bswap(host);
231     }
232     else
233     {
234         return host;
235     }
236 }
237 
238 private uint ntohl(uint network) nothrow @nogc @safe
239 {
240     version(LittleEndian)
241     {
242         import core.bitop;
243         return bswap(network);
244     }
245     else
246     {
247         return network;
248     }
249 }
250 
251 
252 unittest
253 {
254     version(DSFML_Unittest_Network)
255     {
256         import std.stdio;
257 
258         writeln("Unittest for IpAdress");
259 
260         IpAddress address1;
261 
262         assert(address1 == IpAddress.None);
263         assert(IpAddress.LocalHost == IpAddress("127.0.0.1"));
264         assert(IpAddress.LocalHost == IpAddress(127,0,0,1));
265         assert(IpAddress(127, 0, 0, 1) == IpAddress(IpAddress(127,0,0,1).toInteger()));
266 
267         IpAddress googleIP = IpAddress("google.com");
268 
269         writeln("Google's Ip address: ",googleIP);
270 
271         writeln("Your local Ip Address: ", IpAddress.getLocalAddress());
272 
273         writeln("Your public Ip Address: ", IpAddress.getPublicAddress());
274 
275         writeln("Full Ip Address: ", IpAddress(111,111,111,111));
276 
277         writeln();
278     }
279 }
280 
281 private extern(C):
282 
283 ///Create an address from a string
284 uint sfIpAddress_integerFromString(const(char)* address, size_t addressLength);
285 
286 ///Get the computer's local address
287 void sfIpAddress_getLocalAddress(IpAddress* ipAddress);
288 
289 ///Get the computer's public address
290 void sfIpAddress_getPublicAddress(IpAddress* ipAddress, long timeout);