masqmail

annotate src/lookup.c @ 156:ee2afbf92428

require host_name to be set in config file exit otherwise there is no portable way to determine the hostname (actually the hostname that masqmail should use) thus it must be set by the administrator
author meillo@marmaro.de
date Thu, 08 Jul 2010 09:49:05 +0200
parents 26e34ae9a3e3
children 116b0269c934
rev   line source
meillo@0 1 /* MasqMail Copyright (C) Oliver Kurth,
meillo@0 2 *
meillo@0 3 * This program is free software; you can redistribute it and/or modify
meillo@0 4 * it under the terms of the GNU General Public License as published by
meillo@0 5 * the Free Software Foundation; either version 2 of the License, or
meillo@0 6 * (at your option) any later version.
meillo@10 7 *
meillo@0 8 * This program is distributed in the hope that it will be useful,
meillo@0 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 11 * GNU General Public License for more details.
meillo@0 12 *
meillo@0 13 * You should have received a copy of the GNU General Public License
meillo@0 14 * along with this program; if not, write to the Free Software
meillo@0 15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
meillo@0 16 */
meillo@0 17
meillo@0 18 #include <sys/types.h>
meillo@0 19 #include <netinet/in.h>
meillo@0 20 #include <arpa/nameser.h>
meillo@0 21 #include <resolv.h>
meillo@0 22
meillo@0 23 #include "masqmail.h"
meillo@0 24
meillo@0 25 #ifdef RESOLV_TEST
meillo@0 26 #undef DEBUG
meillo@0 27 #define DEBUG(x) if(x > 0)
meillo@0 28 #define debugf g_print
meillo@0 29 #endif
meillo@0 30
meillo@0 31 #ifdef ENABLE_RESOLVER
meillo@0 32
meillo@0 33 static union {
meillo@10 34 HEADER hdr;
meillo@10 35 unsigned char buf[PACKETSZ];
meillo@0 36 } response;
meillo@0 37 static unsigned char *resp_end;
meillo@0 38 static unsigned char *resp_pos;
meillo@0 39
meillo@0 40 static int num_answers;
meillo@0 41 static char name[MAX_DNSNAME];
meillo@0 42
meillo@0 43 unsigned short rr_type;
meillo@0 44 unsigned short rr_dlen;
meillo@0 45
meillo@10 46 static unsigned short
meillo@10 47 getshort(unsigned char *c)
meillo@0 48 {
meillo@10 49 unsigned short u;
meillo@10 50 u = c[0];
meillo@10 51 return (u << 8) + c[1];
meillo@0 52 }
meillo@0 53
meillo@10 54 static int
meillo@10 55 dns_resolve(char *domain, int type, gboolean do_search)
meillo@0 56 {
meillo@10 57 int n;
meillo@10 58 int i;
meillo@0 59
meillo@10 60 int resp_len;
meillo@10 61 /*errno = 0; */
meillo@0 62
meillo@10 63 /*
meillo@10 64 if (!stralloc_copy(&glue,domain)) return DNS_MEM;
meillo@10 65 if (!stralloc_0(&glue)) return DNS_MEM;
meillo@10 66 */
meillo@0 67
meillo@10 68 // resp_len = res_query(domain, C_IN, type, response.buf, sizeof(response));
meillo@10 69 DEBUG(5) debugf("DNS: before res_search()\n");
meillo@10 70 if (do_search)
meillo@10 71 resp_len = res_search(domain, C_IN, type, response.buf, sizeof(response));
meillo@10 72 else
meillo@10 73 resp_len = res_query(domain, C_IN, type, response.buf, sizeof(response));
meillo@10 74 DEBUG(5) debugf("DBG: after res_search()\n");
meillo@0 75
meillo@10 76 if (resp_len <= 0) {
meillo@10 77 /*
meillo@10 78 if (errno == ECONNREFUSED) return DNS_SOFT;
meillo@10 79 if (h_errno == TRY_AGAIN) return DNS_SOFT;
meillo@10 80 return DNS_HARD;
meillo@10 81 */
meillo@10 82 return -1;
meillo@10 83 }
meillo@10 84 if (resp_len >= sizeof(response))
meillo@10 85 resp_len = sizeof(response);
meillo@0 86
meillo@10 87 resp_end = response.buf + resp_len;
meillo@10 88 resp_pos = response.buf + sizeof(HEADER);
meillo@10 89 n = ntohs(response.hdr.qdcount);
meillo@0 90
meillo@10 91 while (n-- > 0) {
meillo@10 92 i = dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME);
meillo@10 93 if (i < 0)
meillo@10 94 return -1;
meillo@10 95 DEBUG(5) debugf("DBG: resolve name = %s\n", name);
meillo@10 96 resp_pos += i;
meillo@10 97 i = resp_end - resp_pos;
meillo@10 98 if (i < QFIXEDSZ)
meillo@10 99 return -1;
meillo@10 100 resp_pos += QFIXEDSZ;
meillo@10 101 }
meillo@10 102 num_answers = ntohs(response.hdr.ancount);
meillo@0 103
meillo@10 104 return 0;
meillo@0 105 }
meillo@0 106
meillo@10 107 static int
meillo@10 108 dns_next()
meillo@0 109 {
meillo@10 110 int i;
meillo@0 111
meillo@10 112 if (num_answers <= 0)
meillo@10 113 return 2;
meillo@10 114 num_answers--;
meillo@0 115
meillo@10 116 if (resp_pos == resp_end)
meillo@10 117 return -1; /* soft */
meillo@0 118
meillo@10 119 i = dn_expand(response.buf, resp_end, resp_pos, name, 256);
meillo@10 120 if (i < 0)
meillo@10 121 return -1; /* soft */
meillo@10 122 resp_pos += i;
meillo@0 123
meillo@10 124 i = resp_end - resp_pos;
meillo@10 125 if (i < 4 + 3 * 2)
meillo@10 126 return -1; /* soft */
meillo@0 127
meillo@10 128 rr_type = getshort(resp_pos);
meillo@10 129 rr_dlen = getshort(resp_pos + 8);
meillo@10 130 resp_pos += 10;
meillo@10 131
meillo@10 132 return 0;
meillo@0 133 }
meillo@0 134
meillo@10 135 static int
meillo@10 136 dns_getip(guint32 * ip)
meillo@0 137 {
meillo@10 138 int ret;
meillo@0 139
meillo@10 140 if ((ret = dns_next()))
meillo@10 141 return ret;
meillo@0 142
meillo@10 143 if (rr_type == T_A) {
meillo@10 144 if (rr_dlen < 4)
meillo@10 145 return -1; /* soft */
meillo@10 146 *ip = *(guint32 *) (resp_pos);
meillo@10 147 DEBUG(5) debugf("DNS: dns_getip(): ip = %s\n", inet_ntoa(*(struct in_addr *) ip));
meillo@10 148 resp_pos += rr_dlen;
meillo@0 149
meillo@10 150 return 1;
meillo@10 151 }
meillo@10 152 resp_pos += rr_dlen;
meillo@10 153 return 0;
meillo@0 154 }
meillo@0 155
meillo@10 156 static int
meillo@10 157 dns_getmx(int *pref)
meillo@0 158 {
meillo@10 159 int ret;
meillo@0 160
meillo@10 161 if ((ret = dns_next()))
meillo@10 162 return ret;
meillo@0 163
meillo@10 164 if (rr_type == T_MX) {
meillo@10 165 if (rr_dlen < 3)
meillo@10 166 return -1; /* soft */
meillo@0 167
meillo@10 168 *pref = (resp_pos[0] << 8) + resp_pos[1];
meillo@10 169 if (dn_expand(response.buf, resp_end, resp_pos + 2, name, MAX_DNSNAME) < 0)
meillo@10 170 return -1;
meillo@0 171
meillo@10 172 resp_pos += rr_dlen;
meillo@10 173
meillo@10 174 return 1;
meillo@10 175 }
meillo@10 176 resp_pos += rr_dlen;
meillo@10 177 return 0;
meillo@0 178 }
meillo@0 179
meillo@0 180 /*
meillo@10 181 static int
meillo@10 182 dns_getname(int type)
meillo@0 183 {
meillo@0 184 int ret;
meillo@0 185
meillo@0 186 if((ret = dns_next())) return ret;
meillo@0 187
meillo@0 188 if (rr_type == type){
meillo@0 189 if (dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME) < 0)
meillo@0 190 return -1;
meillo@10 191
meillo@0 192 resp_pos += rr_dlen;
meillo@0 193
meillo@0 194 return 1;
meillo@0 195 }
meillo@0 196 resp_pos += rr_dlen;
meillo@0 197 return 0;
meillo@0 198 }
meillo@0 199 */
meillo@0 200
meillo@10 201 static int
meillo@10 202 dns_look_ip(gchar * domain, guint32 * ip)
meillo@0 203 {
meillo@10 204 gchar *n = domain;
meillo@0 205
meillo@10 206 while (TRUE) {
meillo@10 207 if (dns_resolve(n, T_A, FALSE) == 0) {
meillo@10 208 dns_next();
meillo@10 209 if (rr_type == T_A) {
meillo@10 210 if (rr_dlen < 4)
meillo@10 211 return -1; /* soft */
meillo@10 212 *ip = *(guint32 *) (resp_pos);
meillo@0 213
meillo@10 214 DEBUG(5) debugf("DNS: dns_look_ip(): ip = %s\n", inet_ntoa(*(struct in_addr *) ip));
meillo@10 215
meillo@10 216 resp_pos += rr_dlen;
meillo@10 217 return 0;
meillo@10 218 } else if (rr_type == T_CNAME) {
meillo@10 219 if (dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME) < 0)
meillo@10 220 return -1;
meillo@10 221
meillo@10 222 DEBUG(5) debugf("DNS: (CNAME) dns_look_ip(): name = %s\n", name);
meillo@10 223
meillo@10 224 resp_pos += rr_dlen;
meillo@10 225 n = name;
meillo@10 226 } else
meillo@10 227 return -1;
meillo@10 228 } else
meillo@10 229 return -1;
meillo@10 230 }
meillo@0 231 }
meillo@0 232
meillo@10 233 GList*
meillo@10 234 resolve_dns_a(GList * list, gchar * domain)
meillo@0 235 {
meillo@10 236 int ret;
meillo@0 237
meillo@10 238 DEBUG(5) debugf("DNS: resolve_dns_a entered\n");
meillo@0 239
meillo@10 240 if (dns_resolve(domain, T_A, TRUE) == 0) {
meillo@10 241 mxip_addr mxip;
meillo@10 242 while ((ret = dns_getip(&(mxip.ip))) != 2) {
meillo@10 243 if (ret == 1) {
meillo@10 244 mxip.name = g_strdup(name);
meillo@10 245 mxip.pref = 0;
meillo@10 246 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 247 }
meillo@10 248 }
meillo@10 249 }
meillo@10 250 return list;
meillo@0 251 }
meillo@0 252
meillo@10 253 static gint
meillo@10 254 _mx_sort_func(gconstpointer aa, gconstpointer bb)
meillo@0 255 {
meillo@10 256 const mxip_addr *a = (mxip_addr *) aa;
meillo@10 257 const mxip_addr *b = (mxip_addr *) bb;
meillo@0 258
meillo@10 259 if (a->pref == b->pref)
meillo@10 260 return a->ip - b->ip;
meillo@10 261 else
meillo@10 262 return a->pref - b->pref;
meillo@0 263 }
meillo@0 264
meillo@10 265 GList*
meillo@10 266 resolve_dns_mx(GList * list, gchar * domain)
meillo@0 267 {
meillo@10 268 GList *node;
meillo@10 269 int ret;
meillo@10 270 int cnt = 0;
meillo@0 271
meillo@10 272 DEBUG(5) debugf("DNS: resolve_dns_mx entered\n");
meillo@0 273
meillo@10 274 if (dns_resolve(domain, T_MX, TRUE) == 0) {
meillo@10 275 GList *node_next;
meillo@10 276 mxip_addr mxip;
meillo@10 277 while ((ret = dns_getmx(&(mxip.pref))) != 2) {
meillo@10 278 if (ret == 1) {
meillo@10 279 mxip.name = g_strdup(name);
meillo@10 280 mxip.ip = rand();
meillo@10 281 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 282 cnt++;
meillo@10 283 }
meillo@10 284 }
meillo@0 285
meillo@10 286 DEBUG(5) debugf("DNS: found %d mx records\n", cnt);
meillo@0 287
meillo@10 288 /* to randomize sequences with equal pref values,
meillo@10 289 we temporarily 'misused' the ip field and
meillo@10 290 put a random number in it as a secondary sort key.
meillo@10 291 */
meillo@10 292 list = g_list_sort(list, _mx_sort_func);
meillo@0 293
meillo@10 294 /* CNAME resolving has to be added as well. */
meillo@0 295
meillo@10 296 for (node = g_list_first(list); node != NULL; node = node_next) {
meillo@0 297
meillo@10 298 mxip_addr *p_mxip = (mxip_addr *) (node->data);
meillo@10 299 node_next = g_list_next(node);
meillo@0 300
meillo@10 301 if (dns_look_ip(p_mxip->name, &(p_mxip->ip)) != 0) {
meillo@10 302 DEBUG(1) debugf("DNS: could not resolve target of mx %s\n", p_mxip->name);
meillo@10 303 list = g_list_remove_link(list, node);
meillo@10 304 g_free(node->data);
meillo@10 305 g_list_free_1(node);
meillo@10 306 }
meillo@10 307 }
meillo@10 308 }
meillo@10 309 return list;
meillo@0 310 }
meillo@0 311
meillo@0 312 #endif
meillo@0 313
meillo@0 314 /* now something completely different... */
meillo@0 315
meillo@10 316 GList*
meillo@10 317 resolve_byname(GList * list, gchar * domain)
meillo@0 318 {
meillo@10 319 struct hostent *hent;
meillo@0 320
meillo@10 321 DEBUG(5) debugf("DNS: resolve_byname entered\n");
meillo@0 322
meillo@10 323 if ((hent = gethostbyname(domain))) {
meillo@10 324 char *haddr;
meillo@10 325 int i = 0;
meillo@10 326 while ((haddr = hent->h_addr_list[i++])) {
meillo@10 327 mxip_addr mxip;
meillo@10 328 mxip.ip = *(guint32 *) (haddr);
meillo@10 329 mxip.pref = 0;
meillo@10 330 mxip.name = g_strdup(hent->h_name);
meillo@10 331 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 332 }
meillo@10 333 }
meillo@10 334 return list;
meillo@0 335 }
meillo@0 336
meillo@0 337 #ifdef RESOLV_TEST
meillo@10 338 int
meillo@10 339 main(int argc, char *argv[])
meillo@0 340 {
meillo@10 341 GList *addr_list = NULL, *node;
meillo@0 342
meillo@10 343 g_print("starting res_init()\n");
meillo@10 344 g_print("retrans = %d, retry = %d\n", _res.retrans, _res.retry);
meillo@10 345 if (res_init() == 0) {
meillo@10 346 addr_list = resolve_dns_a(NULL, argv[1]);
meillo@10 347 g_print("A:\n");
meillo@10 348 foreach(addr_list, node) {
meillo@10 349 mxip_addr *p_mxip = (mxip_addr *) (node->data);
meillo@10 350 printf("name = %s\n IP = %s\n", p_mxip->name, inet_ntoa(*(struct in_addr *) &(p_mxip->ip)));
meillo@10 351 }
meillo@10 352 addr_list = resolve_dns_mx(NULL, argv[1]);
meillo@10 353 g_print("MX:\n");
meillo@10 354 foreach(addr_list, node) {
meillo@10 355 mxip_addr *p_mxip = (mxip_addr *) (node->data);
meillo@15 356 printf("name = %s\n IP = %s pref = %d\n", p_mxip->name,
meillo@15 357 inet_ntoa(*(struct in_addr *) &(p_mxip->ip)), p_mxip->pref);
meillo@10 358 }
meillo@10 359 {
meillo@10 360 guint32 ip;
meillo@10 361 dns_look_ip(argv[1], &ip);
meillo@10 362 printf("dns_look_ip: %s\n", inet_ntoa(*((struct in_addr *) (&ip))));
meillo@10 363 }
meillo@10 364 } else
meillo@10 365 printf("res_init() failed.\n");
meillo@0 366 }
meillo@0 367 #endif