/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ /* Lac - Library for asynchronous communication * Copyright (C) 2000, 2001, 2002, 2003 Søren Sandmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library 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. */ #define LAC_IPV6_UNSAFE #include "lac.h" #include #include "lacdns-messages.h" #include "lacdns-cache.h" #include "lacdns-nameserver.h" #include "lacdns-query.h" #include "lacdns-config.h" #include "lacinternals.h" #include #include "config.h" #ifdef HAVE_NETINET_IN_H # include #endif /* * DNS RFC's: * * 1034: Domain names - concepts and facilities * - standard * * 1035: Domain names - implementation and specification * - standard * * 1101: DNS Encoding of Network Names and Other Types * - textual representation for resource records * * 1183: New DNS RR Definitions * - defines RR type 17-21 * - experimental * * 1637: DNS NSAP Resource Records * - defines RR type 22 * - (Network Service Access Protocol) * - experimental * * 1876: A Means for Expressing Location Information * in the Domain Name System * - defines RR type 29 * - longitude/altitude * - experimental * * 1982: Serial Number Arithmetic * - defines the meaning of the "serial" field in SOA * records * - proposed standard * * 1995: Incremental Zone Transfers in DNS * - proposed standard * * 1996: A Mechanism for Prompt Notification of * Zone Changes (DNS NOTIFY). * - proposed standards * * 2136: Dynamic Updates in the Domain Name System (DNS UPDATE) * * 2137: Secure Domain Name System Dynamic Update * * 2181: Clarifications to the DNS Specification * - IP packet header address usage from * multi-homed servers, * - TTLs in sets of records with the same name, * class, and type, * - correct handling of zone cuts, * - three minor issues concerning SOA records and * their use, * - the precise definition of the Time to Live (TTL) * - Use of the TC (truncated) header bit * - the issue of what is an authoritative, or * canonical, name, * - and the issue of what makes a valid DNS label. * * 2308: Negative Caching * * 2535: Domain Name System Security Extensions * * 2845: Secret Key Transaction Authentication for DNS (TSIG) */ struct LacAddress { struct in_addr in_address; }; /* lac_dns_error_quark */ GQuark lac_dns_error_quark (void) { static GQuark q = 0; if (q == 0) q = g_quark_from_static_string ("lac-dns-error-quark"); return q; } /* * lac_address_new_lookup_all_from_name () */ typedef struct AddressesLookupInfo { gchar * domain; LacAddressesFunc f; gpointer data; } AddressesLookupInfo; static void get_addresses_callback (const DnsQueryEvent *event, gpointer data) { GPtrArray *addresses = NULL; GError *err = NULL; AddressesLookupInfo *info = data; const GList *list; switch (event->type) { case DNS_QUERY_EVENT_RESULTS: addresses = g_ptr_array_new (); for (list = event->results.results; list; list = list->next) { RData *a = list->data; LacAddress *addr = lac_address_new_from_a_b_c_d ( a->u.a.a, a->u.a.b, a->u.a.c, a->u.a.d); g_ptr_array_add (addresses, addr); } break; case DNS_QUERY_EVENT_NO_SUCH_NAME: err = g_error_new ( LAC_DNS_ERROR, LAC_DNS_ERROR_NO_SUCH_NAME, "The name %s was not found", info->domain); break; case DNS_QUERY_EVENT_NO_DATA: err = g_error_new ( LAC_DNS_ERROR, LAC_DNS_ERROR_NO_DATA, "No addresses found for %s", info->domain); break; case DNS_QUERY_EVENT_ERROR: err = g_error_copy (event->error.err); break; } info->f (addresses, info->data, err); g_free (info->domain); g_free (info); if (addresses) { gint i; for (i = 0; i < addresses->len; ++i) lac_address_free (addresses->pdata[i]); g_ptr_array_free (addresses, TRUE); } if (err) g_error_free (err); } void lac_address_new_lookup_all_from_name (const gchar *name, LacAddressesFunc f, gpointer data) { AddressesLookupInfo *info; LacAddress *address; GError *err = NULL; if (!dns_config_initialize (&err)) { f (NULL, data, err); g_error_free (err); return; } /* check if name is on the "a.b.c.d" form */ address = lac_address_new_from_string (name); if (address) { GPtrArray *addresses; addresses = g_ptr_array_new (); g_ptr_array_add (addresses, address); f (addresses, data, NULL); lac_address_free (address); g_ptr_array_free (addresses, TRUE); return; } info = g_new (AddressesLookupInfo, 1); info->domain = g_strdup (name); info->f = f; info->data = data; dns_query (name, A_TYPE, get_addresses_callback, info); } /* * lac_address_lookup_from_name() */ typedef struct { LacAddressFunc f; gpointer data; } GetAddressInfo; static void dns_addr_callback (const GPtrArray *addresses, gpointer data, const GError *err) { GetAddressInfo *info = data; if (err) info->f (NULL, info->data, err); else info->f (addresses->pdata[0], info->data, err); g_free (info); } void lac_address_new_lookup_from_name (const gchar *name, LacAddressFunc f, gpointer data) { GetAddressInfo *info; g_return_if_fail (name != NULL); g_return_if_fail (f != NULL); info = g_new (GetAddressInfo, 1); info->f = f; info->data = data; lac_address_new_lookup_all_from_name (name, dns_addr_callback, info); } /* * lac_address_lookup_name() */ typedef struct NameLookupInfo { LacAddress *address; LacNameFunc f; gpointer data; } NameLookupInfo; static void get_name_callback (const DnsQueryEvent *event, gpointer data) { NameLookupInfo *info = data; gchar *name = NULL; GError *err = NULL; gchar *free_me = NULL; RData *ptr; switch (event->type) { case DNS_QUERY_EVENT_RESULTS: ptr = event->results.results->data; free_me = name = domain_name_to_string (ptr->u.ptr.ptr_name); break; case DNS_QUERY_EVENT_NO_SUCH_NAME: free_me = lac_address_to_string (info->address); err = g_error_new ( LAC_DNS_ERROR, LAC_DNS_ERROR_NO_SUCH_NAME, "No names found for %s", free_me); break; case DNS_QUERY_EVENT_NO_DATA: free_me = lac_address_to_string (info->address); err = g_error_new ( LAC_DNS_ERROR, LAC_DNS_ERROR_NO_DATA, "No names found for %s", free_me); break; case DNS_QUERY_EVENT_ERROR: err = g_error_copy (event->error.err); break; } info->f (name, info->data, err); lac_address_free (info->address); g_free (info); if (free_me) g_free (free_me); } static gchar * address_to_in_addr_arpa (const LacAddress *addr) { gint a, b, c, d; g_return_val_if_fail (addr != NULL, NULL); lac_address_get_a_b_c_d (addr, &a, &b, &c, &d); return g_strdup_printf ("%d.%d.%d.%d.in-addr.arpa.", d, c, b, a); } void lac_address_lookup_name (const LacAddress *address, LacNameFunc f, gpointer data) { NameLookupInfo *info; gchar *domain_str; GError *err = NULL; if (!dns_config_initialize (&err)) { f (NULL, data, err); g_error_free (err); return; } domain_str = address_to_in_addr_arpa (address); info = g_new (NameLookupInfo, 1); info->address = lac_address_copy (address); info->f = f; info->data = data; dns_query (domain_str, PTR_TYPE, get_name_callback, info); g_free (domain_str); } /* * "blocking" versions */ /* * lac_address_new_from_name_wait() */ typedef struct { LacAddress *result; GError * err; GMainLoop * loop; } GetHostByNameInfo; static void ghbn_callback (const GPtrArray *addresses, gpointer data, const GError *err) { GetHostByNameInfo *ghbn_info = data; if (addresses) { ghbn_info->result = lac_address_copy (addresses->pdata[0]); ghbn_info->err = NULL; } else { ghbn_info->result = NULL; ghbn_info->err = g_error_copy (err); } if (g_main_loop_is_running (ghbn_info->loop)) g_main_loop_quit (ghbn_info->loop); } /* * Lookup an address from a name. This function waits for the name * to arrive. Note that this function runs a recursive main loop, * so for example GTK+ applications will still process expose * events etc. while waiting inside this function. Of course * this also means that timeouts and idles etc. will still possibly * run. */ LacAddress * lac_address_new_from_name_wait (const gchar *name, GError **err) { GetHostByNameInfo ghbn_info; ghbn_info.loop = g_main_loop_new (NULL, FALSE); lac_address_new_lookup_all_from_name ( name, ghbn_callback, &ghbn_info); g_main_loop_run (ghbn_info.loop); g_main_loop_unref (ghbn_info.loop); if (ghbn_info.err) { g_propagate_error (err, ghbn_info.err); return NULL; } else { return ghbn_info.result; } } /* * lac_address_lookup_name_wait */ typedef struct { gchar * result; GError * err; GMainLoop * loop; } GetHostByAddrInfo; static void ghba_callback (const gchar *name, gpointer data, const GError *err) { GetHostByAddrInfo *ghba_info = data; if (name) { ghba_info->result = g_strdup (name); ghba_info->err = NULL; } else { ghba_info->result = NULL; ghba_info->err = g_error_copy (err); } if (g_main_loop_is_running (ghba_info->loop)) g_main_loop_quit (ghba_info->loop); } gchar * lac_address_lookup_name_wait (const LacAddress *addr, GError **err) { GetHostByAddrInfo ghba_info; ghba_info.loop = g_main_loop_new (NULL, FALSE); lac_address_lookup_name (addr, ghba_callback, &ghba_info); g_main_loop_run (ghba_info.loop); g_main_loop_unref (ghba_info.loop); if (ghba_info.err) { g_propagate_error (err, ghba_info.err); return NULL; } else { return ghba_info.result; } } LacAddress * lac_address_allocate (void) { LacAddress *addr = g_new0 (LacAddress, 1); return addr; } LacAddress * lac_address_copy (const LacAddress *addr) { LacAddress *copy = lac_address_allocate (); *copy = *addr; return copy; } void lac_address_free (LacAddress *addr) { g_return_if_fail (addr != NULL); g_free (addr); } LacAddress * lac_address_new_from_a_b_c_d (gchar a, gchar b, gchar c, gchar d) { LacAddress *addr = lac_address_allocate (); guchar *addr_bytes = (guchar *)&(addr->in_address.s_addr); addr_bytes[0] = a; addr_bytes[1] = b; addr_bytes[2] = c; addr_bytes[3] = d; return addr; } /* FIXME: is this correct when there are more than three dots? */ LacAddress * lac_address_new_from_string (const gchar *str) { LacAddress *addr = NULL; gchar **strs; gint numbers[4]; gint i; g_return_val_if_fail (str != NULL, NULL); strs = g_strsplit (str, ".", 4); for (i = 0; i < 4; ++i) { gchar *endptr; if (!strs[i]) goto err; numbers[i] = strtol (strs[i], &endptr, 10); if (*endptr != '\0' || endptr == strs[i]) goto err; } if (strs[4]) goto err; addr = lac_address_new_from_a_b_c_d ( numbers[0], numbers[1], numbers[2], numbers[3]); err: g_strfreev (strs); return addr; } void lac_address_new_lookup_from_localhost (LacAddressFunc f, gpointer data) { gchar *name; name = lac_gethostname (); lac_address_new_lookup_from_name (name, f, data); g_free (name); } typedef struct { LacAddress *result; GError *err; GMainLoop *loop; } NewFromLocalHostInfo; static void localhost_callback (const LacAddress *address, gpointer data, const GError *err) { NewFromLocalHostInfo *info = data; if (address) { info->result = lac_address_copy (address); info->err = NULL; } else { info->result = NULL; info->err = g_error_copy (err); } if (g_main_loop_is_running (info->loop)) g_main_loop_quit (info->loop); } LacAddress * lac_address_new_from_localhost_wait (GError **err) { NewFromLocalHostInfo info; info.loop = g_main_loop_new (NULL, FALSE); lac_address_new_lookup_from_localhost (localhost_callback, &info); g_main_loop_run (info.loop); g_main_loop_unref (info.loop); if (info.err) { g_propagate_error (err, info.err); return NULL; } else { return info.result; } } guint lac_address_hash (gconstpointer addr) { return (guint)(((LacAddress *)addr)->in_address.s_addr); } gint lac_address_compare (gconstpointer addr1, gconstpointer addr2) { const LacAddress *a1 = addr1; const LacAddress *a2 = addr2; if (a1->in_address.s_addr == a2->in_address.s_addr) return 0; if (a1->in_address.s_addr > a2->in_address.s_addr) return 1; return -1; } gboolean lac_address_equal (gconstpointer addr1, gconstpointer addr2) { const LacAddress *a1 = addr1; const LacAddress *a2 = addr2; return a1->in_address.s_addr == a2->in_address.s_addr; } gchar * lac_address_to_string (const LacAddress *addr) { guchar *addr_bytes; struct in_addr in_addr; g_return_val_if_fail (addr != NULL, NULL); lac_address_get_in_addr (addr, &in_addr); addr_bytes = (guchar *)&(in_addr.s_addr); return g_strdup_printf ("%d.%d.%d.%d", addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]); } void lac_address_get_a_b_c_d (const LacAddress *addr, gint *a, gint *b, gint *c, gint *d) { const guchar *addr_bytes; g_return_if_fail (addr != NULL); addr_bytes = (const guchar *)&(addr->in_address.s_addr); if (a) *a = addr_bytes[0]; if (b) *b = addr_bytes[1]; if (c) *c = addr_bytes[2]; if (d) *d = addr_bytes[3]; } void lac_address_get_in_addr (const LacAddress *addr, struct in_addr *in_addr) { *in_addr = addr->in_address; } void lac_address_set_in_addr (LacAddress *addr, struct in_addr *in_addr) { addr->in_address = *in_addr; }