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