Mercurial > masqmail
diff src/lookup.c @ 0:08114f7dcc23 0.2.21
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lookup.c Fri Sep 26 17:05:23 2008 +0200 @@ -0,0 +1,376 @@ +/* MasqMail Copyright (C) Oliver Kurth, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> + +#include "masqmail.h" + +#ifdef RESOLV_TEST + +#undef DEBUG +#define DEBUG(x) if(x > 0) + +#define debugf g_print +#endif + +#ifdef ENABLE_RESOLVER + +static union { + HEADER hdr; + unsigned char buf[PACKETSZ]; +} response; +static unsigned char *resp_end; +static unsigned char *resp_pos; + +static int num_answers; +static char name[MAX_DNSNAME]; + +unsigned short rr_type; +unsigned short rr_dlen; + +static +unsigned short getshort(unsigned char *c) +{ + unsigned short u; + u = c[0]; + return (u << 8) + c[1]; +} + +static +int dns_resolve(char *domain, int type, gboolean do_search) +{ + int n; + int i; + + int resp_len; + /*errno = 0;*/ + + /* + if (!stralloc_copy(&glue,domain)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + */ + + // resp_len = res_query(domain, C_IN, type, response.buf, sizeof(response)); + DEBUG(5) debugf("DNS: before res_search()\n"); + if(do_search) + resp_len = res_search(domain, C_IN, type, response.buf, sizeof(response)); + else + resp_len = res_query(domain, C_IN, type, response.buf, sizeof(response)); + DEBUG(5) debugf("DBG: after res_search()\n"); + + if (resp_len <= 0){ + /* + if (errno == ECONNREFUSED) return DNS_SOFT; + if (h_errno == TRY_AGAIN) return DNS_SOFT; + return DNS_HARD; + */ + return -1; + } + if (resp_len >= sizeof(response)) + resp_len = sizeof(response); + + resp_end = response.buf + resp_len; + resp_pos = response.buf + sizeof(HEADER); + n = ntohs(response.hdr.qdcount); + + while (n-- > 0){ + i = dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME); + if (i < 0) + return -1; + DEBUG(5) debugf("DBG: resolve name = %s\n", name); + resp_pos += i; + i = resp_end - resp_pos; + if (i < QFIXEDSZ) + return -1; + resp_pos += QFIXEDSZ; + } + num_answers = ntohs(response.hdr.ancount); + + return 0; +} + +static int dns_next() +{ + int i; + + if (num_answers <= 0) return 2; + num_answers--; + + if (resp_pos == resp_end) + return -1 /* soft */; + + i = dn_expand(response.buf, resp_end, resp_pos, name, 256); + if (i < 0) + return -1; /* soft */ + resp_pos += i; + + i = resp_end - resp_pos; + if (i < 4 + 3 * 2) + return -1; /* soft */ + + rr_type = getshort(resp_pos); + rr_dlen = getshort(resp_pos + 8); + resp_pos += 10; + + return 0; +} + +static +int dns_getip(guint32 *ip) +{ + int ret; + + if((ret = dns_next())) return ret; + + if (rr_type == T_A){ + if (rr_dlen < 4) + return -1; /* soft */ + *ip = *(guint32 *)(resp_pos); + DEBUG(5) debugf("DNS: dns_getip(): ip = %s\n", inet_ntoa(*(struct in_addr*)ip)); + resp_pos += rr_dlen; + + return 1; + } + resp_pos += rr_dlen; + return 0; +} + +static +int dns_getmx(int *pref) +{ + int ret; + + if((ret = dns_next())) return ret; + + if (rr_type == T_MX){ + if (rr_dlen < 3) + return -1; /* soft */ + + *pref = (resp_pos[0] << 8) + resp_pos[1]; + if (dn_expand(response.buf, resp_end, resp_pos + 2, name, MAX_DNSNAME) < 0) + return -1; + + resp_pos += rr_dlen; + + return 1; + } + resp_pos += rr_dlen; + return 0; +} + +/* +static +int dns_getname(int type) +{ + int ret; + + if((ret = dns_next())) return ret; + + if (rr_type == type){ + if (dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME) < 0) + return -1; + + resp_pos += rr_dlen; + + return 1; + } + resp_pos += rr_dlen; + return 0; +} +*/ + +static +int dns_look_ip(gchar *domain, guint32 *ip) +{ + gchar *n = domain; + + while(TRUE){ + if(dns_resolve(n, T_A, FALSE) == 0){ + dns_next(); + if(rr_type == T_A){ + if (rr_dlen < 4) + return -1; /* soft */ + *ip = *(guint32 *)(resp_pos); + + DEBUG(5) debugf("DNS: dns_look_ip(): ip = %s\n", + inet_ntoa(*(struct in_addr*)ip)); + + resp_pos += rr_dlen; + return 0; + }else if(rr_type == T_CNAME){ + if (dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME) < 0) + return -1; + + DEBUG(5) debugf("DNS: (CNAME) dns_look_ip(): name = %s\n", name); + + resp_pos += rr_dlen; + n = name; + }else + return -1; + }else + return -1; + } +} + +GList *resolve_dns_a(GList *list, gchar *domain) +{ + int ret; + + DEBUG(5) debugf("DNS: resolve_dns_a entered\n"); + + if(dns_resolve(domain, T_A, TRUE) == 0){ + mxip_addr mxip; + while((ret = dns_getip(&(mxip.ip))) != 2){ + if(ret == 1){ + mxip.name = g_strdup(name); + mxip.pref = 0; + list = g_list_append(list, g_memdup(&mxip, sizeof(mxip))); + } + } + } + return list; +} + +static +gint _mx_sort_func(gconstpointer aa, gconstpointer bb) +{ + const mxip_addr *a = (mxip_addr *)aa; + const mxip_addr *b = (mxip_addr *)bb; + + if(a->pref == b->pref) + return a->ip - b->ip; + else + return a->pref - b->pref; +} + +GList *resolve_dns_mx(GList *list, gchar *domain) +{ + GList *node; + int ret; + int cnt = 0; + + DEBUG(5) debugf("DNS: resolve_dns_mx entered\n"); + + if(dns_resolve(domain, T_MX, TRUE) == 0){ + GList *node_next; + mxip_addr mxip; + while((ret = dns_getmx(&(mxip.pref))) != 2){ + if(ret == 1){ + mxip.name = g_strdup(name); + mxip.ip = rand(); + list = g_list_append(list, g_memdup(&mxip, sizeof(mxip))); + cnt++; + } + } + + DEBUG(5) debugf("DNS: found %d mx records\n", cnt); + + /* to randomize sequences with equal pref values, + we temporarily 'misused' the ip field and + put a random number in it as a secondary sort key. + */ + list = g_list_sort(list, _mx_sort_func); + + /* CNAME resolving has to be added as well. */ + + for(node = g_list_first(list); + node != NULL; + node = node_next){ + + mxip_addr *p_mxip = (mxip_addr *)(node->data); + node_next = g_list_next(node); + + if(dns_look_ip(p_mxip->name, &(p_mxip->ip)) != 0){ + DEBUG(1) debugf("DNS: could not resolve target of mx %s\n", p_mxip->name); + list = g_list_remove_link(list, node); + g_free(node->data); + g_list_free_1(node); + } + } + } + return list; +} + +#endif + +/* now something completely different... */ + +GList *resolve_byname(GList *list, gchar *domain) +{ + struct hostent *hent; + + DEBUG(5) debugf("DNS: resolve_byname entered\n"); + + if((hent = gethostbyname(domain))){ + char *haddr; + int i = 0; + while((haddr = hent->h_addr_list[i++])){ + mxip_addr mxip; + mxip.ip = *(guint32 *)(haddr); + mxip.pref = 0; + mxip.name = g_strdup(hent->h_name); + list = g_list_append(list, g_memdup(&mxip, sizeof(mxip))); + } + } + return list; +} + +#ifdef RESOLV_TEST +int main(int argc, char *argv[]) +{ + GList *addr_list = NULL, *node; + + g_print("starting res_init()\n"); + + g_print("retrans = %d, retry = %d\n", _res.retrans, _res.retry); + + if(res_init() == 0){ + + addr_list = resolve_dns_a(NULL, argv[1]); + g_print("A:\n"); + + foreach(addr_list, node){ + mxip_addr *p_mxip = (mxip_addr *)(node->data); + + printf("name = %s\n IP = %s\n", + p_mxip->name, + inet_ntoa(*(struct in_addr *)&(p_mxip->ip))); + } + addr_list = resolve_dns_mx(NULL, argv[1]); + g_print("MX:\n"); + + foreach(addr_list, node){ + mxip_addr *p_mxip = (mxip_addr *)(node->data); + + printf("name = %s\n IP = %s pref = %d\n", + p_mxip->name, + inet_ntoa(*(struct in_addr *)&(p_mxip->ip)), + p_mxip->pref); + } + { + guint32 ip; + dns_look_ip(argv[1], &ip); + printf("dns_look_ip: %s\n", inet_ntoa(*((struct in_addr *)(&ip)))); + } + }else + printf("res_init() failed.\n"); + +} +#endif