X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgresolver.c;h=7c69d1a907ce152d5ef6e1e6194897369623554d;hb=958da1e9dc82fbb91862501226b8928faf2f9558;hp=e92715752f5c282d42af16adf587eb002efb953c;hpb=cb0ed84d4022e0fba6aaa9e99e258d8d507a90e5;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gresolver.c b/gio/gresolver.c index e927157..7c69d1a 100644 --- a/gio/gresolver.c +++ b/gio/gresolver.c @@ -15,9 +15,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . */ #include "config.h" @@ -28,7 +26,6 @@ #include "gnetworkingprivate.h" #include "gasyncresult.h" #include "ginetaddress.h" -#include "ginetsocketaddress.h" #include "gsimpleasyncresult.h" #include "gtask.h" #include "gsrvtarget.h" @@ -77,7 +74,9 @@ struct _GResolverPrivate { * The object that handles DNS resolution. Use g_resolver_get_default() * to get the default resolver. */ -G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE (GResolver, g_resolver, G_TYPE_OBJECT, + G_ADD_PRIVATE (GResolver) + g_networking_init ();) static GList * srv_records_to_targets (GList *records) @@ -152,22 +151,6 @@ g_resolver_class_init (GResolverClass *resolver_class) resolver_class->lookup_service_async = g_resolver_real_lookup_service_async; resolver_class->lookup_service_finish = g_resolver_real_lookup_service_finish; - g_type_class_add_private (resolver_class, sizeof (GResolverPrivate)); - - /* Make sure _g_networking_init() has been called */ - g_type_ensure (G_TYPE_INET_ADDRESS); - - /* Initialize _g_resolver_addrinfo_hints */ -#ifdef AI_ADDRCONFIG - _g_resolver_addrinfo_hints.ai_flags |= AI_ADDRCONFIG; -#endif - /* These two don't actually matter, they just get copied into the - * returned addrinfo structures (and then we ignore them). But if - * we leave them unset, we'll get back duplicate answers. - */ - _g_resolver_addrinfo_hints.ai_socktype = SOCK_STREAM; - _g_resolver_addrinfo_hints.ai_protocol = IPPROTO_TCP; - /** * GResolver::reload: * @resolver: a #GResolver @@ -192,7 +175,7 @@ g_resolver_init (GResolver *resolver) struct stat st; #endif - resolver->priv = G_TYPE_INSTANCE_GET_PRIVATE (resolver, G_TYPE_RESOLVER, GResolverPrivate); + resolver->priv = g_resolver_get_instance_private (resolver); #ifdef G_OS_UNIX if (stat (_PATH_RESCONF, &st) == 0) @@ -209,7 +192,7 @@ static GResolver *default_resolver; * with it. #GResolver may use its reference count as a hint about how * many threads it should allocate for concurrent DNS resolutions. * - * Return value: (transfer full): the default #GResolver. + * Returns: (transfer full): the default #GResolver. * * Since: 2.22 */ @@ -246,6 +229,10 @@ g_resolver_set_default (GResolver *resolver) default_resolver = g_object_ref (resolver); } +/* Bionic has res_init() but it's not in any header */ +#ifdef __BIONIC__ +int res_init (void); +#endif static void g_resolver_maybe_reload (GResolver *resolver) @@ -258,7 +245,7 @@ g_resolver_maybe_reload (GResolver *resolver) if (st.st_mtime != resolver->priv->resolv_conf_timestamp) { resolver->priv->resolv_conf_timestamp = st.st_mtime; -#ifndef __BIONIC__ +#ifdef HAVE_RES_INIT res_init (); #endif g_signal_emit (resolver, signals[RELOAD], 0); @@ -296,6 +283,46 @@ remove_duplicates (GList *addrs) } } +/* Note that this does not follow the "FALSE means @error is set" + * convention. The return value tells the caller whether it should + * return @addrs and @error to the caller right away, or if it should + * continue and trying to resolve the name as a hostname. + */ +static gboolean +handle_ip_address (const char *hostname, + GList **addrs, + GError **error) +{ + GInetAddress *addr; + struct in_addr ip4addr; + + addr = g_inet_address_new_from_string (hostname); + if (addr) + { + *addrs = g_list_append (NULL, addr); + return TRUE; + } + + *addrs = NULL; + + /* Reject non-standard IPv4 numbers-and-dots addresses. + * g_inet_address_new_from_string() will have accepted any "real" IP + * address, so if inet_aton() succeeds, then it's an address we want + * to reject. This check is not necessary for Windows, as getaddrinfo() + * already rejects such IPv4 addresses on Windows. + */ +#ifndef G_OS_WIN32 + if (inet_aton (hostname, &ip4addr)) + { + g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, + _("Error resolving '%s': %s"), + hostname, gai_strerror (EAI_NONAME)); + return TRUE; + } +#endif + + return FALSE; +} /** * g_resolver_lookup_by_name: @@ -309,7 +336,7 @@ remove_duplicates (GList *addrs) * the textual form of an IP address (in which case this just becomes * a wrapper around g_inet_address_new_from_string()). * - * On success, g_resolver_lookup_by_name() will return a #GList of + * On success, g_resolver_lookup_by_name() will return a non-empty #GList of * #GInetAddress, sorted in order of preference and guaranteed to not * contain duplicates. That is, if using the result to connect to * @hostname, you should attempt to connect to the first address @@ -318,7 +345,7 @@ remove_duplicates (GList *addrs) * result using e.g. g_socket_listener_add_address(). * * If the DNS resolution fails, @error (if non-%NULL) will be set to a - * value from #GResolverError. + * value from #GResolverError and %NULL will be returned. * * If @cancellable is non-%NULL, it can be used to cancel the * operation, in which case @error (if non-%NULL) will be set to @@ -328,7 +355,7 @@ remove_duplicates (GList *addrs) * address, it may be easier to create a #GNetworkAddress and use its * #GSocketConnectable interface. * - * Return value: (element-type GInetAddress) (transfer full): a #GList + * Returns: (element-type GInetAddress) (transfer full): a non-empty #GList * of #GInetAddress, or %NULL on error. You * must unref each of the addresses and free the list when you are * done with it. (You can use g_resolver_free_addresses() to do this.) @@ -341,7 +368,6 @@ g_resolver_lookup_by_name (GResolver *resolver, GCancellable *cancellable, GError **error) { - GInetAddress *addr; GList *addrs; gchar *ascii_hostname = NULL; @@ -349,9 +375,8 @@ g_resolver_lookup_by_name (GResolver *resolver, g_return_val_if_fail (hostname != NULL, NULL); /* Check if @hostname is just an IP address */ - addr = g_inet_address_new_from_string (hostname); - if (addr) - return g_list_append (NULL, addr); + if (handle_ip_address (hostname, &addrs, error)) + return addrs; if (g_hostname_is_non_ascii (hostname)) hostname = ascii_hostname = g_hostname_to_ascii (hostname); @@ -388,22 +413,24 @@ g_resolver_lookup_by_name_async (GResolver *resolver, GAsyncReadyCallback callback, gpointer user_data) { - GInetAddress *addr; gchar *ascii_hostname = NULL; + GList *addrs; + GError *error = NULL; g_return_if_fail (G_IS_RESOLVER (resolver)); g_return_if_fail (hostname != NULL); /* Check if @hostname is just an IP address */ - addr = g_inet_address_new_from_string (hostname); - if (addr) + if (handle_ip_address (hostname, &addrs, &error)) { GTask *task; task = g_task_new (resolver, cancellable, callback, user_data); g_task_set_source_tag (task, g_resolver_lookup_by_name_async); - g_task_return_pointer (task, g_list_append (NULL, addr), - (GDestroyNotify) g_resolver_free_addresses); + if (addrs) + g_task_return_pointer (task, addrs, (GDestroyNotify) g_resolver_free_addresses); + else + g_task_return_error (task, error); g_object_unref (task); return; } @@ -431,7 +458,7 @@ g_resolver_lookup_by_name_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GInetAddress) (transfer full): a #GList + * Returns: (element-type GInetAddress) (transfer full): a #GList * of #GInetAddress, or %NULL on error. See g_resolver_lookup_by_name() * for more details. * @@ -500,7 +527,7 @@ g_resolver_free_addresses (GList *addresses) * operation, in which case @error (if non-%NULL) will be set to * %G_IO_ERROR_CANCELLED. * - * Return value: a hostname (either ASCII-only, or in ASCII-encoded + * Returns: a hostname (either ASCII-only, or in ASCII-encoded * form), or %NULL on error. * * Since: 2.22 @@ -561,7 +588,7 @@ g_resolver_lookup_by_address_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: a hostname (either ASCII-only, or in ASCII-encoded + * Returns: a hostname (either ASCII-only, or in ASCII-encoded * form), or %NULL on error. * * Since: 2.22 @@ -608,17 +635,16 @@ g_resolver_get_service_rrname (const char *service, * Synchronously performs a DNS SRV lookup for the given @service and * @protocol in the given @domain and returns an array of #GSrvTarget. * @domain may be an ASCII-only or UTF-8 hostname. Note also that the - * @service and @protocol arguments do not - * include the leading underscore that appears in the actual DNS - * entry. + * @service and @protocol arguments do not include the leading underscore + * that appears in the actual DNS entry. * - * On success, g_resolver_lookup_service() will return a #GList of + * On success, g_resolver_lookup_service() will return a non-empty #GList of * #GSrvTarget, sorted in order of preference. (That is, you should * attempt to connect to the first target first, then the second if * the first fails, etc.) * * If the DNS resolution fails, @error (if non-%NULL) will be set to - * a value from #GResolverError. + * a value from #GResolverError and %NULL will be returned. * * If @cancellable is non-%NULL, it can be used to cancel the * operation, in which case @error (if non-%NULL) will be set to @@ -628,9 +654,10 @@ g_resolver_get_service_rrname (const char *service, * to create a #GNetworkService and use its #GSocketConnectable * interface. * - * Return value: (element-type GSrvTarget) (transfer full): a #GList of #GSrvTarget, - * or %NULL on error. You must free each of the targets and the list when you are - * done with it. (You can use g_resolver_free_targets() to do this.) + * Returns: (element-type GSrvTarget) (transfer full): a non-empty #GList of + * #GSrvTarget, or %NULL on error. You must free each of the targets and the + * list when you are done with it. (You can use g_resolver_free_targets() to do + * this.) * * Since: 2.22 */ @@ -716,8 +743,9 @@ g_resolver_lookup_service_async (GResolver *resolver, * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GSrvTarget) (transfer full): a #GList of #GSrvTarget, - * or %NULL on error. See g_resolver_lookup_service() for more details. + * Returns: (element-type GSrvTarget) (transfer full): a non-empty #GList of + * #GSrvTarget, or %NULL on error. See g_resolver_lookup_service() for more + * details. * * Since: 2.22 */ @@ -769,15 +797,16 @@ g_resolver_free_targets (GList *targets) * information on what the records contain for each @record_type. * * If the DNS resolution fails, @error (if non-%NULL) will be set to - * a value from #GResolverError. + * a value from #GResolverError and %NULL will be returned. * * If @cancellable is non-%NULL, it can be used to cancel the * operation, in which case @error (if non-%NULL) will be set to * %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GVariant) (transfer full): a #GList of #GVariant, - * or %NULL on error. You must free each of the records and the list when you are - * done with it. (You can use g_list_free_full() with g_variant_unref() to do this.) + * Returns: (element-type GVariant) (transfer full): a non-empty #GList of + * #GVariant, or %NULL on error. You must free each of the records and the list + * when you are done with it. (You can use g_list_free_full() with + * g_variant_unref() to do this.) * * Since: 2.34 */ @@ -839,16 +868,18 @@ g_resolver_lookup_records_async (GResolver *resolver, * @error: return location for a #GError, or %NULL * * Retrieves the result of a previous call to - * g_resolver_lookup_records_async(). Returns a list of records as #GVariant - * tuples. See #GResolverRecordType for information on what the records contain. + * g_resolver_lookup_records_async(). Returns a non-empty list of records as + * #GVariant tuples. See #GResolverRecordType for information on what the + * records contain. * * If the DNS resolution failed, @error (if non-%NULL) will be set to * a value from #GResolverError. If the operation was cancelled, * @error will be set to %G_IO_ERROR_CANCELLED. * - * Return value: (element-type GVariant) (transfer full): a #GList of #GVariant, - * or %NULL on error. You must free each of the records and the list when you are - * done with it. (You can use g_list_free_full() with g_variant_unref() to do this.) + * Returns: (element-type GVariant) (transfer full): a non-empty #GList of + * #GVariant, or %NULL on error. You must free each of the records and the list + * when you are done with it. (You can use g_list_free_full() with + * g_variant_unref() to do this.) * * Since: 2.34 */ @@ -862,691 +893,27 @@ g_resolver_lookup_records_finish (GResolver *resolver, lookup_records_finish (resolver, result, error); } +guint64 +g_resolver_get_serial (GResolver *resolver) +{ + g_return_val_if_fail (G_IS_RESOLVER (resolver), 0); + + g_resolver_maybe_reload (resolver); + +#ifdef G_OS_UNIX + return (guint64) resolver->priv->resolv_conf_timestamp; +#else + return 1; +#endif +} + /** * g_resolver_error_quark: * * Gets the #GResolver Error Quark. * - * Return value: a #GQuark. + * Returns: a #GQuark. * * Since: 2.22 */ G_DEFINE_QUARK (g-resolver-error-quark, g_resolver_error) - -static GResolverError -g_resolver_error_from_addrinfo_error (gint err) -{ - switch (err) - { - case EAI_FAIL: -#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) - case EAI_NODATA: -#endif - case EAI_NONAME: - return G_RESOLVER_ERROR_NOT_FOUND; - - case EAI_AGAIN: - return G_RESOLVER_ERROR_TEMPORARY_FAILURE; - - default: - return G_RESOLVER_ERROR_INTERNAL; - } -} - -struct addrinfo _g_resolver_addrinfo_hints; - -/* Private method to process a getaddrinfo() response. */ -GList * -_g_resolver_addresses_from_addrinfo (const char *hostname, - struct addrinfo *res, - gint gai_retval, - GError **error) -{ - struct addrinfo *ai; - GSocketAddress *sockaddr; - GInetAddress *addr; - GList *addrs; - - if (gai_retval != 0) - { - g_set_error (error, G_RESOLVER_ERROR, - g_resolver_error_from_addrinfo_error (gai_retval), - _("Error resolving '%s': %s"), - hostname, gai_strerror (gai_retval)); - return NULL; - } - - g_return_val_if_fail (res != NULL, NULL); - - addrs = NULL; - for (ai = res; ai; ai = ai->ai_next) - { - sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen); - if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr)) - continue; - - addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr)); - addrs = g_list_prepend (addrs, addr); - g_object_unref (sockaddr); - } - - return g_list_reverse (addrs); -} - -/* Private method to set up a getnameinfo() request */ -void -_g_resolver_address_to_sockaddr (GInetAddress *address, - struct sockaddr_storage *sa, - gsize *len) -{ - GSocketAddress *sockaddr; - - sockaddr = g_inet_socket_address_new (address, 0); - g_socket_address_to_native (sockaddr, (struct sockaddr *)sa, sizeof (*sa), NULL); - *len = g_socket_address_get_native_size (sockaddr); - g_object_unref (sockaddr); -} - -/* Private method to process a getnameinfo() response. */ -char * -_g_resolver_name_from_nameinfo (GInetAddress *address, - const gchar *name, - gint gni_retval, - GError **error) -{ - if (gni_retval != 0) - { - gchar *phys; - - phys = g_inet_address_to_string (address); - g_set_error (error, G_RESOLVER_ERROR, - g_resolver_error_from_addrinfo_error (gni_retval), - _("Error reverse-resolving '%s': %s"), - phys ? phys : "(unknown)", gai_strerror (gni_retval)); - g_free (phys); - return NULL; - } - - return g_strdup (name); -} - -#if defined(G_OS_UNIX) - -#ifdef __BIONIC__ -/* Copy from bionic/libc/private/arpa_nameser_compat.h - * and bionic/libc/private/arpa_nameser.h */ -typedef struct { - unsigned id :16; /* query identification number */ -#if BYTE_ORDER == BIG_ENDIAN - /* fields in third byte */ - unsigned qr: 1; /* response flag */ - unsigned opcode: 4; /* purpose of message */ - unsigned aa: 1; /* authoritive answer */ - unsigned tc: 1; /* truncated message */ - unsigned rd: 1; /* recursion desired */ - /* fields in fourth byte */ - unsigned ra: 1; /* recursion available */ - unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ - unsigned ad: 1; /* authentic data from named */ - unsigned cd: 1; /* checking disabled by resolver */ - unsigned rcode :4; /* response code */ -#endif -#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN - /* fields in third byte */ - unsigned rd :1; /* recursion desired */ - unsigned tc :1; /* truncated message */ - unsigned aa :1; /* authoritive answer */ - unsigned opcode :4; /* purpose of message */ - unsigned qr :1; /* response flag */ - /* fields in fourth byte */ - unsigned rcode :4; /* response code */ - unsigned cd: 1; /* checking disabled by resolver */ - unsigned ad: 1; /* authentic data from named */ - unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ - unsigned ra :1; /* recursion available */ -#endif - /* remaining bytes */ - unsigned qdcount :16; /* number of question entries */ - unsigned ancount :16; /* number of answer entries */ - unsigned nscount :16; /* number of authority entries */ - unsigned arcount :16; /* number of resource entries */ -} HEADER; - -#define NS_INT32SZ 4 /* #/bytes of data in a uint32_t */ -#define NS_INT16SZ 2 /* #/bytes of data in a uint16_t */ - -#define NS_GET16(s, cp) do { \ - const u_char *t_cp = (const u_char *)(cp); \ - (s) = ((uint16_t)t_cp[0] << 8) \ - | ((uint16_t)t_cp[1]) \ - ; \ - (cp) += NS_INT16SZ; \ -} while (/*CONSTCOND*/0) - -#define NS_GET32(l, cp) do { \ - const u_char *t_cp = (const u_char *)(cp); \ - (l) = ((uint32_t)t_cp[0] << 24) \ - | ((uint32_t)t_cp[1] << 16) \ - | ((uint32_t)t_cp[2] << 8) \ - | ((uint32_t)t_cp[3]) \ - ; \ - (cp) += NS_INT32SZ; \ -} while (/*CONSTCOND*/0) - -#define GETSHORT NS_GET16 -#define GETLONG NS_GET32 - -#define C_IN 1 - -/* From bionic/libc/private/resolv_private.h */ -int dn_expand(const u_char *, const u_char *, const u_char *, char *, int); -#define dn_skipname __dn_skipname -int dn_skipname(const u_char *, const u_char *); - -/* From bionic/libc/private/arpa_nameser_compat.h */ -#define T_MX ns_t_mx -#define T_TXT ns_t_txt -#define T_SOA ns_t_soa -#define T_NS ns_t_ns - -/* From bionic/libc/private/arpa_nameser.h */ -typedef enum __ns_type { - ns_t_invalid = 0, /* Cookie. */ - ns_t_a = 1, /* Host address. */ - ns_t_ns = 2, /* Authoritative server. */ - ns_t_md = 3, /* Mail destination. */ - ns_t_mf = 4, /* Mail forwarder. */ - ns_t_cname = 5, /* Canonical name. */ - ns_t_soa = 6, /* Start of authority zone. */ - ns_t_mb = 7, /* Mailbox domain name. */ - ns_t_mg = 8, /* Mail group member. */ - ns_t_mr = 9, /* Mail rename name. */ - ns_t_null = 10, /* Null resource record. */ - ns_t_wks = 11, /* Well known service. */ - ns_t_ptr = 12, /* Domain name pointer. */ - ns_t_hinfo = 13, /* Host information. */ - ns_t_minfo = 14, /* Mailbox information. */ - ns_t_mx = 15, /* Mail routing information. */ - ns_t_txt = 16, /* Text strings. */ - ns_t_rp = 17, /* Responsible person. */ - ns_t_afsdb = 18, /* AFS cell database. */ - ns_t_x25 = 19, /* X_25 calling address. */ - ns_t_isdn = 20, /* ISDN calling address. */ - ns_t_rt = 21, /* Router. */ - ns_t_nsap = 22, /* NSAP address. */ - ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ - ns_t_sig = 24, /* Security signature. */ - ns_t_key = 25, /* Security key. */ - ns_t_px = 26, /* X.400 mail mapping. */ - ns_t_gpos = 27, /* Geographical position (withdrawn). */ - ns_t_aaaa = 28, /* Ip6 Address. */ - ns_t_loc = 29, /* Location Information. */ - ns_t_nxt = 30, /* Next domain (security). */ - ns_t_eid = 31, /* Endpoint identifier. */ - ns_t_nimloc = 32, /* Nimrod Locator. */ - ns_t_srv = 33, /* Server Selection. */ - ns_t_atma = 34, /* ATM Address */ - ns_t_naptr = 35, /* Naming Authority PoinTeR */ - ns_t_kx = 36, /* Key Exchange */ - ns_t_cert = 37, /* Certification record */ - ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ - ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ - ns_t_sink = 40, /* Kitchen sink (experimentatl) */ - ns_t_opt = 41, /* EDNS0 option (meta-RR) */ - ns_t_apl = 42, /* Address prefix list (RFC 3123) */ - ns_t_tkey = 249, /* Transaction key */ - ns_t_tsig = 250, /* Transaction signature. */ - ns_t_ixfr = 251, /* Incremental zone transfer. */ - ns_t_axfr = 252, /* Transfer zone of authority. */ - ns_t_mailb = 253, /* Transfer mailbox records. */ - ns_t_maila = 254, /* Transfer mail agent records. */ - ns_t_any = 255, /* Wildcard match. */ - ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ - ns_t_max = 65536 -} ns_type; - -#endif - -static gboolean -parse_short (guchar **p, - guchar *end, - guint16 *value) -{ - if (*p + 2 > end) - return FALSE; - GETSHORT (*value, *p); - return TRUE; -} - -static gboolean -parse_long (guchar **p, - guchar *end, - guint32 *value) -{ - if (*p + 4 > end) - return FALSE; - GETLONG (*value, *p); - return TRUE; -} - -static GVariant * -parse_res_srv (guchar *answer, - guchar *end, - guchar *p) -{ - gchar namebuf[1024]; - guint16 priority, weight, port; - gint n; - - if (!parse_short (&p, end, &priority) || - !parse_short (&p, end, &weight) || - !parse_short (&p, end, &port)) - return NULL; - - n = dn_expand (answer, end, p, namebuf, sizeof (namebuf)); - if (n < 0) - return NULL; - *p += n; - - return g_variant_new ("(qqqs)", - priority, - weight, - port, - namebuf); -} - -static GVariant * -parse_res_soa (guchar *answer, - guchar *end, - guchar *p) -{ - gchar mnamebuf[1024]; - gchar rnamebuf[1024]; - guint32 serial, refresh, retry, expire, ttl; - gint n; - - n = dn_expand (answer, end, p, mnamebuf, sizeof (mnamebuf)); - if (n < 0) - return NULL; - p += n; - - n = dn_expand (answer, end, p, rnamebuf, sizeof (rnamebuf)); - if (n < 0) - return NULL; - p += n; - - if (!parse_long (&p, end, &serial) || - !parse_long (&p, end, &refresh) || - !parse_long (&p, end, &retry) || - !parse_long (&p, end, &expire) || - !parse_long (&p, end, &ttl)) - return NULL; - - return g_variant_new ("(ssuuuuu)", - mnamebuf, - rnamebuf, - serial, - refresh, - retry, - expire, - ttl); -} - -static GVariant * -parse_res_ns (guchar *answer, - guchar *end, - guchar *p) -{ - gchar namebuf[1024]; - gint n; - - n = dn_expand (answer, end, p, namebuf, sizeof (namebuf)); - if (n < 0) - return NULL; - - return g_variant_new ("(s)", namebuf); -} - -static GVariant * -parse_res_mx (guchar *answer, - guchar *end, - guchar *p) -{ - gchar namebuf[1024]; - guint16 preference; - gint n; - - if (!parse_short (&p, end, &preference)) - return NULL; - - n = dn_expand (answer, end, p, namebuf, sizeof (namebuf)); - if (n < 0) - return NULL; - p += n; - - return g_variant_new ("(qs)", - preference, - namebuf); -} - -static GVariant * -parse_res_txt (guchar *answer, - guchar *end, - guchar *p) -{ - GVariant *record; - GPtrArray *array; - gsize len; - - array = g_ptr_array_new_with_free_func (g_free); - while (p < end) - { - len = *(p++); - if (len > p - end) - break; - g_ptr_array_add (array, g_strndup ((gchar *)p, len)); - p += len; - } - - record = g_variant_new ("(@as)", - g_variant_new_strv ((const gchar **)array->pdata, array->len)); - g_ptr_array_free (array, TRUE); - return record; -} - -gint -_g_resolver_record_type_to_rrtype (GResolverRecordType type) -{ - switch (type) - { - case G_RESOLVER_RECORD_SRV: - return T_SRV; - case G_RESOLVER_RECORD_TXT: - return T_TXT; - case G_RESOLVER_RECORD_SOA: - return T_SOA; - case G_RESOLVER_RECORD_NS: - return T_NS; - case G_RESOLVER_RECORD_MX: - return T_MX; - } - g_return_val_if_reached (-1); -} - -/* Private method to process a res_query response into GSrvTargets */ -GList * -_g_resolver_records_from_res_query (const gchar *rrname, - gint rrtype, - guchar *answer, - gint len, - gint herr, - GError **error) -{ - gint count; - guchar *end, *p; - guint16 type, qclass, rdlength; - guint32 ttl; - HEADER *header; - GList *records; - GVariant *record; - gint n, i; - - if (len <= 0) - { - GResolverError errnum; - const gchar *format; - - if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA) - { - errnum = G_RESOLVER_ERROR_NOT_FOUND; - format = _("No DNS record of the requested type for '%s'"); - } - else if (herr == TRY_AGAIN) - { - errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE; - format = _("Temporarily unable to resolve '%s'"); - } - else - { - errnum = G_RESOLVER_ERROR_INTERNAL; - format = _("Error resolving '%s'"); - } - - g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname); - return NULL; - } - - records = NULL; - - header = (HEADER *)answer; - p = answer + sizeof (HEADER); - end = answer + len; - - /* Skip query */ - count = ntohs (header->qdcount); - for (i = 0; i < count && p < end; i++) - { - n = dn_skipname (p, end); - if (n < 0) - break; - p += n; - p += 4; - } - - /* Incomplete response */ - if (i < count) - { - g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE, - _("Incomplete data received for '%s'"), rrname); - return NULL; - } - - /* Read answers */ - count = ntohs (header->ancount); - for (i = 0; i < count && p < end; i++) - { - n = dn_skipname (p, end); - if (n < 0) - break; - p += n; - - if (!parse_short (&p, end, &type) || - !parse_short (&p, end, &qclass) || - !parse_long (&p, end, &ttl) || - !parse_short (&p, end, &rdlength)) - break; - - ttl = ttl; /* To avoid -Wunused-but-set-variable */ - - if (p + rdlength > end) - break; - - if (type == rrtype && qclass == C_IN) - { - switch (rrtype) - { - case T_SRV: - record = parse_res_srv (answer, end, p); - break; - case T_MX: - record = parse_res_mx (answer, end, p); - break; - case T_SOA: - record = parse_res_soa (answer, end, p); - break; - case T_NS: - record = parse_res_ns (answer, end, p); - break; - case T_TXT: - record = parse_res_txt (answer, p + rdlength, p); - break; - default: - g_warn_if_reached (); - record = NULL; - break; - } - - if (record != NULL) - records = g_list_prepend (records, record); - } - - p += rdlength; - } - - /* Somehow got a truncated response */ - if (i < count) - { - g_list_free_full (records, (GDestroyNotify)g_variant_unref); - g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE, - _("Incomplete data received for '%s'"), rrname); - return NULL; - } - - return records; -} - -#elif defined(G_OS_WIN32) -static GVariant * -parse_dns_srv (DNS_RECORD *rec) -{ - return g_variant_new ("(qqqs)", - (guint16)rec->Data.SRV.wPriority, - (guint16)rec->Data.SRV.wWeight, - (guint16)rec->Data.SRV.wPort, - rec->Data.SRV.pNameTarget); -} - -static GVariant * -parse_dns_soa (DNS_RECORD *rec) -{ - return g_variant_new ("(ssuuuuu)", - rec->Data.SOA.pNamePrimaryServer, - rec->Data.SOA.pNameAdministrator, - (guint32)rec->Data.SOA.dwSerialNo, - (guint32)rec->Data.SOA.dwRefresh, - (guint32)rec->Data.SOA.dwRetry, - (guint32)rec->Data.SOA.dwExpire, - (guint32)rec->Data.SOA.dwDefaultTtl); -} - -static GVariant * -parse_dns_ns (DNS_RECORD *rec) -{ - return g_variant_new ("(s)", rec->Data.NS.pNameHost); -} - -static GVariant * -parse_dns_mx (DNS_RECORD *rec) -{ - return g_variant_new ("(qs)", - (guint16)rec->Data.MX.wPreference, - rec->Data.MX.pNameExchange); -} - -static GVariant * -parse_dns_txt (DNS_RECORD *rec) -{ - GVariant *record; - GPtrArray *array; - DWORD i; - - array = g_ptr_array_new (); - for (i = 0; i < rec->Data.TXT.dwStringCount; i++) - g_ptr_array_add (array, rec->Data.TXT.pStringArray[i]); - record = g_variant_new ("(@as)", - g_variant_new_strv ((const gchar **)array->pdata, array->len)); - g_ptr_array_free (array, TRUE); - return record; -} - -WORD -_g_resolver_record_type_to_dnstype (GResolverRecordType type) -{ - switch (type) - { - case G_RESOLVER_RECORD_SRV: - return DNS_TYPE_SRV; - case G_RESOLVER_RECORD_TXT: - return DNS_TYPE_TEXT; - case G_RESOLVER_RECORD_SOA: - return DNS_TYPE_SOA; - case G_RESOLVER_RECORD_NS: - return DNS_TYPE_NS; - case G_RESOLVER_RECORD_MX: - return DNS_TYPE_MX; - } - g_return_val_if_reached (-1); -} - -/* Private method to process a DnsQuery response into GVariants */ -GList * -_g_resolver_records_from_DnsQuery (const gchar *rrname, - WORD dnstype, - DNS_STATUS status, - DNS_RECORD *results, - GError **error) -{ - DNS_RECORD *rec; - gpointer record; - GList *records; - - if (status != ERROR_SUCCESS) - { - GResolverError errnum; - const gchar *format; - - if (status == DNS_ERROR_RCODE_NAME_ERROR) - { - errnum = G_RESOLVER_ERROR_NOT_FOUND; - format = _("No DNS record of the requested type for '%s'"); - } - else if (status == DNS_ERROR_RCODE_SERVER_FAILURE) - { - errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE; - format = _("Temporarily unable to resolve '%s'"); - } - else - { - errnum = G_RESOLVER_ERROR_INTERNAL; - format = _("Error resolving '%s'"); - } - - g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname); - return NULL; - } - - records = NULL; - for (rec = results; rec; rec = rec->pNext) - { - if (rec->wType != dnstype) - continue; - switch (dnstype) - { - case DNS_TYPE_SRV: - record = parse_dns_srv (rec); - break; - case DNS_TYPE_SOA: - record = parse_dns_soa (rec); - break; - case DNS_TYPE_NS: - record = parse_dns_ns (rec); - break; - case DNS_TYPE_MX: - record = parse_dns_mx (rec); - break; - case DNS_TYPE_TEXT: - record = parse_dns_txt (rec); - break; - default: - g_warn_if_reached (); - record = NULL; - break; - } - if (record != NULL) - records = g_list_prepend (records, g_variant_ref_sink (record)); - } - - return records; -} - -#endif