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