source: trunk/third/libsoup/libsoup/soup-address.c @ 21108

Revision 21108, 11.0 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21107, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/*
3 * soup-address.c: Internet address handing
4 *
5 * Copyright (C) 2000-2003, Ximian, Inc.
6 */
7
8#ifdef HAVE_CONFIG_H
9#include <config.h>
10#endif
11
12#include <unistd.h>
13#include <errno.h>
14#include <fcntl.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <signal.h>
20
21#include <netinet/in.h>
22#include <arpa/inet.h>
23#include <netdb.h>
24
25#include "soup-address.h"
26#include "soup-dns.h"
27#include "soup-marshal.h"
28#include "soup-misc.h"
29
30#ifndef INET_ADDRSTRLEN
31#  define INET_ADDRSTRLEN 16
32#  define INET6_ADDRSTRLEN 46
33#endif
34
35#ifndef INADDR_NONE
36#define INADDR_NONE -1
37#endif
38
39struct SoupAddressPrivate {
40        struct sockaddr *sockaddr;
41
42        char *name, *physical;
43        guint port;
44
45        SoupDNSEntry *lookup;
46        guint timeout_id;
47};
48
49
50/* sockaddr generic macros */
51#define SOUP_SIN(addr) ((struct sockaddr_in *)addr->priv->sockaddr)
52#ifdef HAVE_IPV6
53#define SOUP_SIN6(addr) ((struct sockaddr_in6 *)addr->priv->sockaddr)
54#endif
55
56/* sockaddr family macros */
57#define SOUP_ADDRESS_GET_FAMILY(addr) (addr->priv->sockaddr->sa_family)
58#define SOUP_ADDRESS_SET_FAMILY(addr, family) \
59        (addr->priv->sockaddr->sa_family = family)
60#ifdef HAVE_IPV6
61#define SOUP_ADDRESS_FAMILY_IS_VALID(family) \
62        (family == AF_INET || family == AF_INET6)
63#define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) \
64        (family == AF_INET ? sizeof (struct sockaddr_in) : \
65                             sizeof (struct sockaddr_in6))
66#define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) \
67        (family == AF_INET ? sizeof (struct in_addr) : \
68                             sizeof (struct in6_addr))
69#else
70#define SOUP_ADDRESS_FAMILY_IS_VALID(family) (family == AF_INET)
71#define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) sizeof (struct sockaddr_in)
72#define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) sizeof (struct in_addr)
73#endif
74
75/* sockaddr port macros */
76#define SOUP_ADDRESS_PORT_IS_VALID(port) (port >= 0 && port <= 65535)
77#ifdef HAVE_IPV6
78#define SOUP_ADDRESS_GET_PORT(addr) \
79        (addr->priv->sockaddr->sa_family == AF_INET ? \
80                SOUP_SIN(addr)->sin_port : \
81                SOUP_SIN6(addr)->sin6_port)
82#define SOUP_ADDRESS_SET_PORT(addr, port) \
83        G_STMT_START {                                  \
84        if (addr->priv->sockaddr->sa_family == AF_INET) \
85                SOUP_SIN(addr)->sin_port = port;        \
86        else                                            \
87                SOUP_SIN6(addr)->sin6_port = port;      \
88        } G_STMT_END
89#else
90#define SOUP_ADDRESS_GET_PORT(addr) (SOUP_SIN(addr)->sin_port)
91#define SOUP_ADDRESS_SET_PORT(addr, port) (SOUP_SIN(addr)->sin_port = port)
92#endif
93
94/* sockaddr data macros */
95#ifdef HAVE_IPV6
96#define SOUP_ADDRESS_GET_DATA(addr) \
97        (addr->priv->sockaddr->sa_family == AF_INET ? \
98                (gpointer)&SOUP_SIN(addr)->sin_addr : \
99                (gpointer)&SOUP_SIN6(addr)->sin6_addr)
100#else
101#define SOUP_ADDRESS_GET_DATA(addr) ((gpointer)&SOUP_SIN(addr)->sin_addr)
102#endif
103#define SOUP_ADDRESS_SET_DATA(addr, data, length) \
104        memcpy (SOUP_ADDRESS_GET_DATA (addr), data, length)
105
106
107enum {
108        DNS_RESULT,
109        LAST_SIGNAL
110};
111
112static guint signals[LAST_SIGNAL] = { 0 };
113
114#define PARENT_TYPE G_TYPE_OBJECT
115static GObjectClass *parent_class;
116
117static void
118init (GObject *object)
119{
120        SoupAddress *addr = SOUP_ADDRESS (object);
121
122        addr->priv = g_new0 (SoupAddressPrivate, 1);
123}
124
125static void
126finalize (GObject *object)
127{
128        SoupAddress *addr = SOUP_ADDRESS (object);
129
130        if (addr->priv->sockaddr)
131                g_free (addr->priv->sockaddr);
132        if (addr->priv->name)
133                g_free (addr->priv->name);
134        if (addr->priv->physical)
135                g_free (addr->priv->physical);
136
137        if (addr->priv->lookup)
138                soup_dns_entry_cancel_lookup (addr->priv->lookup);
139        if (addr->priv->timeout_id)
140                g_source_remove (addr->priv->timeout_id);
141
142        g_free (addr->priv);
143
144        G_OBJECT_CLASS (parent_class)->finalize (object);
145}
146
147static void
148class_init (GObjectClass *object_class)
149{
150        parent_class = g_type_class_ref (PARENT_TYPE);
151
152        /* virtual method override */
153        object_class->finalize = finalize;
154
155        /* signals */
156        signals[DNS_RESULT] =
157                g_signal_new ("dns_result",
158                              G_OBJECT_CLASS_TYPE (object_class),
159                              G_SIGNAL_RUN_FIRST,
160                              G_STRUCT_OFFSET (SoupAddressClass, dns_result),
161                              NULL, NULL,
162                              soup_marshal_NONE__INT,
163                              G_TYPE_NONE, 1,
164                              G_TYPE_INT);
165}
166
167SOUP_MAKE_TYPE (soup_address, SoupAddress, class_init, init, PARENT_TYPE)
168
169
170
171/**
172 * soup_address_new:
173 * @name: a hostname or physical address
174 * @port: a port number
175 *
176 * Creates a #SoupAddress from @name and @port. The #SoupAddress's IP
177 * address may not be available right away; the caller can call
178 * soup_address_resolve_async() or soup_address_resolve_sync() to
179 * force a DNS resolution.
180 *
181 * Return value: a #SoupAddress
182 **/
183SoupAddress *
184soup_address_new (const char *name, guint port)
185{
186        SoupAddress *addr;
187
188        g_return_val_if_fail (name != NULL, NULL);
189        g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
190
191        addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
192        addr->priv->name = g_strdup (name);
193        addr->priv->port = port;
194
195        return addr;
196}
197
198/**
199 * soup_address_new_from_sockaddr:
200 * @sa: a pointer to a sockaddr
201 * @len: size of @sa
202 *
203 * Returns a #SoupAddress equivalent to @sa (or %NULL if @sa's
204 * address family isn't supported)
205 *
206 * Return value: the new #SoupAddress
207 **/
208SoupAddress *
209soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
210{
211        SoupAddress *addr;
212
213        g_return_val_if_fail (sa != NULL, NULL);
214        g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (sa->sa_family), NULL);
215        g_return_val_if_fail (len == SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family), NULL);
216
217        addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
218        addr->priv->sockaddr = g_memdup (sa, len);
219        addr->priv->port = ntohs (SOUP_ADDRESS_GET_PORT (addr));
220        return addr;
221}
222
223/**
224 * soup_address_new_any:
225 * @family: the address family
226 * @port: the port number (usually %SOUP_ADDRESS_ANY_PORT)
227 *
228 * Returns a #SoupAddress corresponding to the "any" address
229 * for @family (or %NULL if @family isn't supported), suitable for
230 * passing to soup_socket_server_new().
231 *
232 * Return value: the new #SoupAddress
233 **/
234SoupAddress *
235soup_address_new_any (SoupAddressFamily family, guint port)
236{
237        SoupAddress *addr;
238
239        g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (family), NULL);
240        g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
241
242        addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
243        addr->priv->port = port;
244
245        addr->priv->sockaddr =
246                g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family));
247        SOUP_ADDRESS_SET_FAMILY (addr, family);
248        SOUP_ADDRESS_SET_PORT (addr, htons (port));
249
250        return addr;
251}
252
253/**
254 * soup_address_get_name:
255 * @addr: a #SoupAddress
256 *
257 * Returns the hostname associated with @addr.
258 *
259 * Return value: the hostname, or %NULL if it is not known.
260 **/
261const char *
262soup_address_get_name (SoupAddress *addr)
263{
264        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
265
266        return addr->priv->name;
267}
268
269/**
270 * soup_address_get_sockaddr:
271 * @addr: a #SoupAddress
272 * @len: return location for sockaddr length
273 *
274 * Returns the sockaddr associated with @addr, with its length in
275 * *@len. If the sockaddr is not yet know, returns %NULL.
276 *
277 * Return value: the sockaddr, or %NULL
278 **/
279struct sockaddr *
280soup_address_get_sockaddr (SoupAddress *addr, int *len)
281{
282        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
283
284        if (addr->priv->sockaddr && len)
285                *len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (addr));
286
287        return addr->priv->sockaddr;
288}
289
290/**
291 * soup_address_get_physical:
292 * @addr: a #SoupAddress
293 *
294 * Returns the physical address associated with @addr as a string.
295 * (Eg, "127.0.0.1"). If the address is not yet known, returns %NULL.
296 *
297 * Return value: the physical address, or %NULL
298 **/
299const char *
300soup_address_get_physical (SoupAddress *addr)
301{
302        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
303
304        if (!addr->priv->sockaddr)
305                return NULL;
306
307        if (!addr->priv->physical) {
308                addr->priv->physical =
309                        soup_dns_ntop (SOUP_ADDRESS_GET_DATA (addr),
310                                       SOUP_ADDRESS_GET_FAMILY (addr));
311        }
312
313        return addr->priv->physical;
314}
315
316/**
317 * soup_address_get_port:
318 * @addr: a #SoupAddress
319 *
320 * Returns the port associated with @addr.
321 *
322 * Return value: the port
323 **/
324guint
325soup_address_get_port (SoupAddress *addr)
326{
327        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), 0);
328
329        return addr->priv->port;
330}
331
332
333static guint
334update_address_from_entry (SoupAddress *addr, SoupDNSEntry *entry)
335{
336        struct hostent *h;
337
338        h = soup_dns_entry_get_hostent (entry);
339        if (!h)
340                return SOUP_STATUS_CANT_RESOLVE;
341
342        if (!addr->priv->name)
343                addr->priv->name = g_strdup (h->h_name);
344
345        if (!addr->priv->sockaddr &&
346            SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype) &&
347            SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) == h->h_length) {
348                addr->priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (h->h_addrtype));
349                SOUP_ADDRESS_SET_FAMILY (addr, h->h_addrtype);
350                SOUP_ADDRESS_SET_PORT (addr, htons (addr->priv->port));
351                SOUP_ADDRESS_SET_DATA (addr, h->h_addr, h->h_length);
352        }
353
354        soup_dns_free_hostent (h);
355
356        if (addr->priv->name && addr->priv->sockaddr)
357                return SOUP_STATUS_OK;
358        else
359                return SOUP_STATUS_CANT_RESOLVE;
360}
361
362static gboolean
363timeout_check_lookup (gpointer user_data)
364{
365        SoupAddress *addr = user_data;
366        guint status;
367
368        if (addr->priv->name && addr->priv->sockaddr) {
369                addr->priv->timeout_id = 0;
370                g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_STATUS_OK);
371                return FALSE;
372        }
373
374        if (!soup_dns_entry_check_lookup (addr->priv->lookup))
375                return TRUE;
376
377        status = update_address_from_entry (addr, addr->priv->lookup);
378        addr->priv->lookup = NULL;
379        addr->priv->timeout_id = 0;
380
381        g_signal_emit (addr, signals[DNS_RESULT], 0, status);
382        return FALSE;
383}
384
385/**
386 * soup_address_resolve_async:
387 * @addr: a #SoupAddress
388 * @callback: callback to call with the result
389 * @user_data: data for @callback
390 *
391 * Asynchronously resolves the missing half of @addr. (Its IP address
392 * if it was created with soup_address_new(), or its hostname if it
393 * was created with soup_address_new_from_sockaddr() or
394 * soup_address_new_any().) @callback will be called when the
395 * resolution finishes (successfully or not).
396 **/
397void
398soup_address_resolve_async (SoupAddress *addr,
399                            SoupAddressCallback callback,
400                            gpointer user_data)
401{
402        g_return_if_fail (SOUP_IS_ADDRESS (addr));
403
404        if (callback) {
405                soup_signal_connect_once (addr, "dns_result",
406                                          G_CALLBACK (callback), user_data);
407        }
408
409        if (addr->priv->timeout_id)
410                return;
411
412        if (!addr->priv->sockaddr) {
413                addr->priv->lookup =
414                        soup_dns_entry_from_name (addr->priv->name);
415        } else if (!addr->priv->name) {
416                addr->priv->lookup =
417                        soup_dns_entry_from_addr (SOUP_ADDRESS_GET_DATA (addr),
418                                                  SOUP_ADDRESS_GET_FAMILY (addr));
419        }
420
421        addr->priv->timeout_id = g_timeout_add (100, timeout_check_lookup, addr);
422}
423
424/**
425 * soup_address_resolve_sync:
426 * @addr: a #SoupAddress
427 *
428 * Synchronously resolves the missing half of @addr, as with
429 * soup_address_resolve_async().
430 *
431 * Return value: %SOUP_STATUS_OK or %SOUP_STATUS_CANT_RESOLVE
432 **/
433guint
434soup_address_resolve_sync (SoupAddress *addr)
435{
436        SoupDNSEntry *entry;
437
438        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
439
440        if (addr->priv->name)
441                entry = soup_dns_entry_from_name (addr->priv->name);
442        else {
443                entry = soup_dns_entry_from_addr (SOUP_ADDRESS_GET_DATA (addr),
444                                                  SOUP_ADDRESS_GET_FAMILY (addr));
445        }
446
447        return update_address_from_entry (addr, entry);
448}
Note: See TracBrowser for help on using the repository browser.