1 /*
2  * GNetwork Library: libgnetwork/gnetwork-ip-address.c
3  *
4  * Copyright (C) 2001, 2003 James M. Cape
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; version 2.1 of the
9  * License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif /* HAVE_CONFIG_H */
25 
26 #include "gnetwork-ip-address.h"
27 #include "gnetwork-utils.h"
28 
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <unistd.h>
33 #include <string.h>
34 
35 
36 GType
gnetwork_ip_address_get_type(void)37 gnetwork_ip_address_get_type (void)
38 {
39   static GType type = G_TYPE_INVALID;
40 
41   if (type == G_TYPE_INVALID)
42     {
43       type = g_boxed_type_register_static ("GNetworkIpAddress",
44 					   (GBoxedCopyFunc) gnetwork_ip_address_dup, g_free);
45     }
46 
47   return type;
48 }
49 
50 
51 /**
52  * gnetwork_ip_address_new:
53  * @str: the string representation of an IP address.
54  *
55  * Creates a new IP address based on the data in @str. If @str is %NULL, a
56  * zeroed address structure will be returned. The returned data should be freed
57  * with g_free() when no longer needed.
58  *
59  * Returns: a new #GNetworkIpAddress structure for @str.
60  *
61  * Since: 1.0
62  **/
63 GNetworkIpAddress *
gnetwork_ip_address_new(const gchar * str)64 gnetwork_ip_address_new (const gchar * str)
65 {
66   GNetworkIpAddress *retval;
67 
68   retval = g_new0 (GNetworkIpAddress, 1);
69 
70   gnetwork_ip_address_set_from_string (retval, str);
71 
72   return retval;
73 }
74 
75 
76 /**
77  * gnetwork_ip_address_to_string:
78  * @address: the address to examine.
79  *
80  * Creates a string representation of the IP address in @address. The returned
81  * value should be freed with g_free() when no longer needed.
82  *
83  * Returns: the string representation of @address.
84  *
85  * Since: 1.0
86  **/
87 gchar *
gnetwork_ip_address_to_string(const GNetworkIpAddress * address)88 gnetwork_ip_address_to_string (const GNetworkIpAddress * address)
89 {
90   gchar *retval;
91 
92   g_return_val_if_fail (address != NULL, NULL);
93 
94   if (gnetwork_ip_address_is_ipv4 (address))
95     {
96       retval = g_strdup_printf ("%u.%u.%u.%u", gnetwork_ip_address_get8 (address, 12),
97 				gnetwork_ip_address_get8 (address, 13),
98 				gnetwork_ip_address_get8 (address, 14),
99 				gnetwork_ip_address_get8 (address, 15));
100     }
101   else if (!gnetwork_ip_address_is_valid (address))
102     {
103       retval = NULL;
104     }
105   else
106     {
107       gchar addr[INET_ADDRESS_SIZE] = { 0 };
108 
109       if (inet_ntop (AF_INET6, address, addr, INET_ADDRESS_SIZE) < 0)
110 	retval = NULL;
111       else
112 	retval = g_strdup (addr);
113     }
114 
115   return retval;
116 }
117 
118 
119 /**
120  * gnetwork_ip_address_set_from_string:
121  * @address: the address to modify.
122  * @str: the new IP address in string format.
123  *
124  * Overwrites the existing IP address in @address using the string data in @str.
125  * If @str is not a valid IP address string, @address will be set to the invalid
126  * address (all 0s), and the function will return %FALSE. @str may be in IPv4
127  * ("127.0.0.1"), IPv6 ("::1"), or IPv4-compatable IPv6 format ("::127.0.0.1").
128  *
129  * Returns: %TRUE or %FALSE, depending on whether @str was a valid IP address string.
130  *
131  * Since: 1.0.
132  **/
133 gboolean
gnetwork_ip_address_set_from_string(GNetworkIpAddress * address,const gchar * str)134 gnetwork_ip_address_set_from_string (GNetworkIpAddress * address, const gchar * str)
135 {
136   struct in_addr addr;
137 
138   g_return_val_if_fail (address != NULL, FALSE);
139   g_return_val_if_fail (str == NULL || (str[0] != '\0' && strlen (str) <= INET6_ADDRSTRLEN), FALSE);
140 
141   memset (address, 0, sizeof (GNetworkIpAddress));
142 
143   if (str == NULL)
144     return FALSE;
145 
146   memset (&addr, 0, sizeof (struct in_addr));
147 
148   /* If we've got an IPv4 string, copy it in appropriately. */
149   if (inet_pton (AF_INET, str, &addr) >= 0)
150     {
151       gnetwork_ip_address_set16 (address, 5, 0xFFFF);
152       GNETWORK_IP_ADDRESS32 (address, 3) = ((addr.s_addr) & 0xFFFFFFFF);
153       return TRUE;
154     }
155   /* Otherwise, try and set the IPv6 string */
156 
157   return (inet_pton (AF_INET6, str, address) >= 0);
158 }
159 
160 
161 /**
162  * gnetwork_str_is_ip_address:
163  * @str: the character string to check, or %NULL.
164  *
165  * Checks if @str contains a valid IP address string (in either IPv6 or IPv4
166  * format). %NULL and empty strings are considered invalid addresses.
167  *
168  * Returns: %TRUE or %FALSE.
169  *
170  * Since: 1.0
171  **/
172 gboolean
gnetwork_str_is_ip_address(const gchar * str)173 gnetwork_str_is_ip_address (const gchar * str)
174 {
175   GNetworkIpAddress addr;
176 
177   return gnetwork_ip_address_set_from_string (&addr, str);
178 }
179 
180 
181 /**
182  * _gnetwork_ip_address_set_from_string:
183  * @address: the address to modify.
184  * @str: the new IP address in string format.
185  *
186  * Overwrites the existing IP address in @address using the string data in @str.
187  * If @str is not a valid string, @address will be set to the invalid address
188  * (all 0s). @str may be in IPv4 ("127.0.0.1"), IPv6 ("::1"), or IPv4-on-IPv6
189  * format ("::ffff::127.0.0.1").
190  *
191  * Since: 1.0.
192  **/
193 void
_gnetwork_ip_address_set_from_sockaddr(GNetworkIpAddress * address,const struct sockaddr * sa)194 _gnetwork_ip_address_set_from_sockaddr (GNetworkIpAddress * address, const struct sockaddr *sa)
195 {
196   g_return_if_fail (address != NULL);
197   g_return_if_fail (sa == NULL || sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
198 
199   memset (address, 0, sizeof (GNetworkIpAddress));
200 
201   if (sa == NULL)
202     return;
203 
204   switch (sa->sa_family)
205     {
206     case AF_INET:
207       gnetwork_ip_address_set16 (address, 5, 0xFFFF);
208       memcpy (&(GNETWORK_IP_ADDRESS32 (address, 3)),
209 	      &(((struct sockaddr_in *) sa)->sin_addr.s_addr), sizeof (struct in_addr));
210       break;
211     case AF_INET6:
212       memcpy (address, &(((struct sockaddr_in6 *) sa)->sin6_addr), sizeof (GNetworkIpAddress));
213       break;
214     default:
215       break;
216     }
217 }
218 
219 
220 /**
221  * _gnetwork_ip_address_to_sockaddr:
222  * @ip_address: the IP address to use.
223  * @port: the port number to use.
224  * @sa_size: the location to store the size of the returned value, or %NULL.
225  *
226  * Creates a new sockaddr_in or sockaddr_in6 (if IPv6 is available) based on
227  * @ip_address and @port. It doesn't matter whether @ip_address is in IPv4
228  * ("127.0.0.1") or IPv6 ("0000:0000:0000:0001") for the new addr to be created.
229  *
230  * Returns: a new sockaddr structure.
231  *
232  * Since: 1.0
233  **/
234 struct sockaddr *
_gnetwork_ip_address_to_sockaddr(const GNetworkIpAddress * address,guint16 port,gint * sa_size)235 _gnetwork_ip_address_to_sockaddr (const GNetworkIpAddress * address, guint16 port, gint * sa_size)
236 {
237   gint sockfd;
238   gpointer retval = NULL;
239 
240   sockfd = socket (AF_INET6, SOCK_DGRAM, 0);
241 
242   if (sockfd < 0)
243     {
244       struct sockaddr_in *sa;
245 
246       if (sa_size != NULL)
247 	*sa_size = sizeof (struct sockaddr_in);
248 
249       sa = g_new0 (struct sockaddr_in, 1);
250 
251       if (address != NULL && gnetwork_ip_address_is_ipv4 (address))
252 	{
253 	  memcpy (&(sa->sin_addr.s_addr), &(GNETWORK_IP_ADDRESS32 (address, 3)), sizeof (guint32));
254 	}
255       else
256 	{
257 	  sa->sin_addr.s_addr = INADDR_ANY;
258 	}
259 
260       sa->sin_family = AF_INET;
261       sa->sin_port = g_htons (port);
262 
263       retval = sa;
264     }
265   else
266     {
267       struct sockaddr_in6 *sa;
268 
269       close (sockfd);
270 
271       if (sa_size != NULL)
272 	*sa_size = sizeof (struct sockaddr_in6);
273 
274       sa = g_new0 (struct sockaddr_in6, 1);
275 
276       if (address != NULL && gnetwork_ip_address_is_valid (address))
277 	{
278 	  memcpy (&(sa->sin6_addr), address, sizeof (GNetworkIpAddress));
279 	}
280       else
281 	{
282 	  sa->sin6_addr = in6addr_any;
283 	}
284 
285       sa->sin6_family = AF_INET6;
286       sa->sin6_port = g_htons (port);
287 
288       retval = sa;
289     }
290 
291   return retval;
292 }
293 
294 
295 /**
296  * gnetwork_ip_address_dup:
297  * @address: a pointer to the address to duplicate.
298  *
299  * Creates a copy of the IP address in @address. The returned value should be
300  * freed with g_free() when no longer needed.
301  *
302  * Returns: a copy of @address.
303  *
304  * Since: 1.0
305  **/
306 GNetworkIpAddress *
gnetwork_ip_address_dup(const GNetworkIpAddress * address)307 gnetwork_ip_address_dup (const GNetworkIpAddress * address)
308 {
309   return g_memdup (address, sizeof (GNetworkIpAddress));
310 }
311 
312 
313 /**
314  * gnetwork_ip_address_collate:
315  * @address1: a pointer to the first address to test.
316  * @address2: a pointer to the second address to test.
317  *
318  * Compares @address1 and @address2 for sorting.
319  *
320  * Returns: %-1 if @address1 should be sorted first, %1 if @address2 should be sorted first, and %0 if both addresses are equal.
321  *
322  * Since: 1.0
323  **/
324 gint
gnetwork_ip_address_collate(const GNetworkIpAddress * address1,const GNetworkIpAddress * address2)325 gnetwork_ip_address_collate (const GNetworkIpAddress * address1, const GNetworkIpAddress * address2)
326 {
327   if (address1 == NULL && address2 != NULL)
328     {
329       return 1;
330     }
331   else if (address1 != NULL && address2 == NULL)
332     {
333       return -1;
334     }
335   else
336     {
337       if (gnetwork_ip_address_get64 (address1, 0) < gnetwork_ip_address_get64 (address2, 0))
338 	return 1;
339       else if (gnetwork_ip_address_get64 (address1, 0) > gnetwork_ip_address_get64 (address2, 0))
340 	return -1;
341       else if (gnetwork_ip_address_get64 (address1, 1) < gnetwork_ip_address_get64 (address2, 1))
342 	return 1;
343       else if (gnetwork_ip_address_get64 (address1, 1) > gnetwork_ip_address_get64 (address2, 1))
344 	return -1;
345     }
346 
347   return 0;
348 }
349 
350 
351 /**
352  * gnetwork_ip_address_hash:
353  * @address: a pointer to the address to hash.
354  *
355  * Computes a hash value for the IP address in @address.
356  *
357  * Returns: the hash of @address.
358  *
359  * Since: 1.0
360  **/
361 guint
gnetwork_ip_address_hash(gconstpointer address)362 gnetwork_ip_address_hash (gconstpointer address)
363 {
364   if (address == NULL)
365     return 0;
366 #if (G_MAXUINT == 0xFFFFFFFF)	/* 32-bit arch */
367   return gnetwork_ip_address_get32 (address, 3);
368 #elif (G_MAXUINT == G_MAXUINT64)	/* 64-bit arch */
369   return gnetwork_ip_address_get64 (address, 1);
370 #else /* 128+-bit arch? */
371   return *GNETWORK_IP_ADDRESS (address);
372 #endif /* That's all folks */
373 }
374 
375 
376 /**
377  * gnetwork_ip_address_equal:
378  * @address1: a pointer to the first address to test.
379  * @address2: a pointer to the second address to test.
380  *
381  * Tests the equality of @address1 and @address2. This is a #GEqualFunc, so it
382  * can be used straight-away in a #GHashTable.
383  *
384  * Returns: %TRUE of @address1 equals @address2, %FALSE if it does not.
385  *
386  * Since: 1.0
387  **/
388 gboolean
gnetwork_ip_address_equal(gconstpointer address1,gconstpointer address2)389 gnetwork_ip_address_equal (gconstpointer address1, gconstpointer address2)
390 {
391   return ((address1 == address2) ||
392 	  (address1 != NULL && address2 != NULL &&
393 	   gnetwork_ip_address_get64 (address1, 0) == gnetwork_ip_address_get64 (address2, 0) &&
394 	   gnetwork_ip_address_get64 (address2, 1) == gnetwork_ip_address_get64 (address2, 1)));
395 }
396 
397 
398 #ifndef __GNUC__
399 void
gnetwork_ip_address_set_ipv4(GNetworkIpAddress * address,guint32 ip)400 gnetwork_ip_address_set_ipv4 (GNetworkIpAddress * address, guint32 ip)
401 {
402   g_return_if_fail (address != NULL);
403 
404   memset (address, 0, 80);
405   gnetwork_ip_address_set16 (address, 5, 0xFFFF);
406   gnetwork_ip_address_set32 (address, 3, ip);
407 }
408 #endif /* !__GNUC__ */
409