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