masqmail

annotate src/lookup.c @ 222:8cddc65765bd

added support for STARTTLS wrappers added the route config option `instant_helo' which causes masqmail, as SMTP client, not to wait for the server's 220 greeting. Instead if says EHLO right at once. You'll need this for STARTTLS wrappers that usually eat the greeting line.
author meillo@marmaro.de
date Fri, 23 Jul 2010 10:57:53 +0200
parents 138e66e1a61f
children 996b53a50f55
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
meillo@0 26 #ifdef ENABLE_RESOLVER
meillo@0 27
meillo@0 28 static union {
meillo@10 29 HEADER hdr;
meillo@10 30 unsigned char buf[PACKETSZ];
meillo@0 31 } response;
meillo@0 32 static unsigned char *resp_end;
meillo@0 33 static unsigned char *resp_pos;
meillo@0 34
meillo@0 35 static int num_answers;
meillo@0 36 static char name[MAX_DNSNAME];
meillo@0 37
meillo@0 38 unsigned short rr_type;
meillo@0 39 unsigned short rr_dlen;
meillo@0 40
meillo@10 41 static unsigned short
meillo@10 42 getshort(unsigned char *c)
meillo@0 43 {
meillo@10 44 unsigned short u;
meillo@10 45 u = c[0];
meillo@10 46 return (u << 8) + c[1];
meillo@0 47 }
meillo@0 48
meillo@10 49 static int
meillo@10 50 dns_resolve(char *domain, int type, gboolean do_search)
meillo@0 51 {
meillo@10 52 int n;
meillo@10 53 int i;
meillo@207 54 int resp_len;
meillo@0 55
meillo@10 56 DEBUG(5) debugf("DNS: before res_search()\n");
meillo@10 57 if (do_search)
meillo@10 58 resp_len = res_search(domain, C_IN, type, response.buf, sizeof(response));
meillo@10 59 else
meillo@10 60 resp_len = res_query(domain, C_IN, type, response.buf, sizeof(response));
meillo@10 61 DEBUG(5) debugf("DBG: after res_search()\n");
meillo@0 62
meillo@10 63 if (resp_len <= 0) {
meillo@10 64 /*
meillo@10 65 if (errno == ECONNREFUSED) return DNS_SOFT;
meillo@10 66 if (h_errno == TRY_AGAIN) return DNS_SOFT;
meillo@10 67 return DNS_HARD;
meillo@10 68 */
meillo@10 69 return -1;
meillo@10 70 }
meillo@10 71 if (resp_len >= sizeof(response))
meillo@10 72 resp_len = sizeof(response);
meillo@0 73
meillo@10 74 resp_end = response.buf + resp_len;
meillo@10 75 resp_pos = response.buf + sizeof(HEADER);
meillo@10 76 n = ntohs(response.hdr.qdcount);
meillo@0 77
meillo@10 78 while (n-- > 0) {
meillo@10 79 i = dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME);
meillo@10 80 if (i < 0)
meillo@10 81 return -1;
meillo@10 82 DEBUG(5) debugf("DBG: resolve name = %s\n", name);
meillo@10 83 resp_pos += i;
meillo@10 84 i = resp_end - resp_pos;
meillo@10 85 if (i < QFIXEDSZ)
meillo@10 86 return -1;
meillo@10 87 resp_pos += QFIXEDSZ;
meillo@10 88 }
meillo@10 89 num_answers = ntohs(response.hdr.ancount);
meillo@0 90
meillo@10 91 return 0;
meillo@0 92 }
meillo@0 93
meillo@10 94 static int
meillo@10 95 dns_next()
meillo@0 96 {
meillo@10 97 int i;
meillo@0 98
meillo@10 99 if (num_answers <= 0)
meillo@10 100 return 2;
meillo@10 101 num_answers--;
meillo@0 102
meillo@10 103 if (resp_pos == resp_end)
meillo@10 104 return -1; /* soft */
meillo@0 105
meillo@10 106 i = dn_expand(response.buf, resp_end, resp_pos, name, 256);
meillo@10 107 if (i < 0)
meillo@10 108 return -1; /* soft */
meillo@10 109 resp_pos += i;
meillo@0 110
meillo@10 111 i = resp_end - resp_pos;
meillo@10 112 if (i < 4 + 3 * 2)
meillo@10 113 return -1; /* soft */
meillo@0 114
meillo@10 115 rr_type = getshort(resp_pos);
meillo@10 116 rr_dlen = getshort(resp_pos + 8);
meillo@10 117 resp_pos += 10;
meillo@10 118
meillo@10 119 return 0;
meillo@0 120 }
meillo@0 121
meillo@10 122 static int
meillo@10 123 dns_getip(guint32 * ip)
meillo@0 124 {
meillo@10 125 int ret;
meillo@0 126
meillo@10 127 if ((ret = dns_next()))
meillo@10 128 return ret;
meillo@0 129
meillo@10 130 if (rr_type == T_A) {
meillo@10 131 if (rr_dlen < 4)
meillo@10 132 return -1; /* soft */
meillo@10 133 *ip = *(guint32 *) (resp_pos);
meillo@10 134 DEBUG(5) debugf("DNS: dns_getip(): ip = %s\n", inet_ntoa(*(struct in_addr *) ip));
meillo@10 135 resp_pos += rr_dlen;
meillo@0 136
meillo@10 137 return 1;
meillo@10 138 }
meillo@10 139 resp_pos += rr_dlen;
meillo@10 140 return 0;
meillo@0 141 }
meillo@0 142
meillo@10 143 static int
meillo@10 144 dns_getmx(int *pref)
meillo@0 145 {
meillo@10 146 int ret;
meillo@0 147
meillo@10 148 if ((ret = dns_next()))
meillo@10 149 return ret;
meillo@0 150
meillo@10 151 if (rr_type == T_MX) {
meillo@10 152 if (rr_dlen < 3)
meillo@10 153 return -1; /* soft */
meillo@0 154
meillo@10 155 *pref = (resp_pos[0] << 8) + resp_pos[1];
meillo@10 156 if (dn_expand(response.buf, resp_end, resp_pos + 2, name, MAX_DNSNAME) < 0)
meillo@10 157 return -1;
meillo@0 158
meillo@10 159 resp_pos += rr_dlen;
meillo@10 160
meillo@10 161 return 1;
meillo@10 162 }
meillo@10 163 resp_pos += rr_dlen;
meillo@10 164 return 0;
meillo@0 165 }
meillo@0 166
meillo@200 167 int
meillo@10 168 dns_look_ip(gchar * domain, guint32 * ip)
meillo@0 169 {
meillo@10 170 gchar *n = domain;
meillo@0 171
meillo@10 172 while (TRUE) {
meillo@200 173 if (dns_resolve(n, T_A, FALSE) != 0) {
meillo@200 174 return -1;
meillo@200 175 }
meillo@0 176
meillo@200 177 dns_next();
meillo@200 178 if (rr_type == T_A) {
meillo@200 179 if (rr_dlen < 4) {
meillo@200 180 return -1; /* soft */
meillo@200 181 }
meillo@200 182 *ip = *(guint32 *) (resp_pos);
meillo@10 183
meillo@200 184 DEBUG(5) debugf("DNS: dns_look_ip(): ip = %s\n", inet_ntoa(*(struct in_addr *) ip));
meillo@10 185
meillo@200 186 resp_pos += rr_dlen;
meillo@200 187 return 0;
meillo@200 188 } else if (rr_type == T_CNAME) {
meillo@200 189 if (dn_expand(response.buf, resp_end, resp_pos, name, MAX_DNSNAME) < 0) {
meillo@200 190 return -1;
meillo@200 191 }
meillo@10 192
meillo@200 193 DEBUG(5) debugf("DNS: (CNAME) dns_look_ip(): name = %s\n", name);
meillo@200 194
meillo@200 195 resp_pos += rr_dlen;
meillo@200 196 n = name;
meillo@200 197 } else {
meillo@10 198 return -1;
meillo@200 199 }
meillo@10 200 }
meillo@0 201 }
meillo@0 202
meillo@10 203 GList*
meillo@10 204 resolve_dns_a(GList * list, gchar * domain)
meillo@0 205 {
meillo@10 206 int ret;
meillo@0 207
meillo@10 208 DEBUG(5) debugf("DNS: resolve_dns_a entered\n");
meillo@0 209
meillo@10 210 if (dns_resolve(domain, T_A, TRUE) == 0) {
meillo@10 211 mxip_addr mxip;
meillo@10 212 while ((ret = dns_getip(&(mxip.ip))) != 2) {
meillo@10 213 if (ret == 1) {
meillo@10 214 mxip.name = g_strdup(name);
meillo@10 215 mxip.pref = 0;
meillo@10 216 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 217 }
meillo@10 218 }
meillo@10 219 }
meillo@10 220 return list;
meillo@0 221 }
meillo@0 222
meillo@10 223 static gint
meillo@10 224 _mx_sort_func(gconstpointer aa, gconstpointer bb)
meillo@0 225 {
meillo@10 226 const mxip_addr *a = (mxip_addr *) aa;
meillo@10 227 const mxip_addr *b = (mxip_addr *) bb;
meillo@0 228
meillo@10 229 if (a->pref == b->pref)
meillo@10 230 return a->ip - b->ip;
meillo@10 231 else
meillo@10 232 return a->pref - b->pref;
meillo@0 233 }
meillo@0 234
meillo@10 235 GList*
meillo@10 236 resolve_dns_mx(GList * list, gchar * domain)
meillo@0 237 {
meillo@10 238 GList *node;
meillo@10 239 int ret;
meillo@10 240 int cnt = 0;
meillo@0 241
meillo@10 242 DEBUG(5) debugf("DNS: resolve_dns_mx entered\n");
meillo@0 243
meillo@10 244 if (dns_resolve(domain, T_MX, TRUE) == 0) {
meillo@10 245 GList *node_next;
meillo@10 246 mxip_addr mxip;
meillo@10 247 while ((ret = dns_getmx(&(mxip.pref))) != 2) {
meillo@10 248 if (ret == 1) {
meillo@10 249 mxip.name = g_strdup(name);
meillo@10 250 mxip.ip = rand();
meillo@10 251 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 252 cnt++;
meillo@10 253 }
meillo@10 254 }
meillo@0 255
meillo@10 256 DEBUG(5) debugf("DNS: found %d mx records\n", cnt);
meillo@0 257
meillo@10 258 /* to randomize sequences with equal pref values,
meillo@10 259 we temporarily 'misused' the ip field and
meillo@10 260 put a random number in it as a secondary sort key.
meillo@10 261 */
meillo@10 262 list = g_list_sort(list, _mx_sort_func);
meillo@0 263
meillo@10 264 /* CNAME resolving has to be added as well. */
meillo@0 265
meillo@10 266 for (node = g_list_first(list); node != NULL; node = node_next) {
meillo@0 267
meillo@10 268 mxip_addr *p_mxip = (mxip_addr *) (node->data);
meillo@10 269 node_next = g_list_next(node);
meillo@0 270
meillo@10 271 if (dns_look_ip(p_mxip->name, &(p_mxip->ip)) != 0) {
meillo@10 272 DEBUG(1) debugf("DNS: could not resolve target of mx %s\n", p_mxip->name);
meillo@10 273 list = g_list_remove_link(list, node);
meillo@10 274 g_free(node->data);
meillo@10 275 g_list_free_1(node);
meillo@10 276 }
meillo@10 277 }
meillo@10 278 }
meillo@10 279 return list;
meillo@0 280 }
meillo@0 281
meillo@0 282 #endif
meillo@0 283
meillo@0 284 /* now something completely different... */
meillo@0 285
meillo@10 286 GList*
meillo@10 287 resolve_byname(GList * list, gchar * domain)
meillo@0 288 {
meillo@10 289 struct hostent *hent;
meillo@0 290
meillo@10 291 DEBUG(5) debugf("DNS: resolve_byname entered\n");
meillo@0 292
meillo@10 293 if ((hent = gethostbyname(domain))) {
meillo@10 294 char *haddr;
meillo@10 295 int i = 0;
meillo@10 296 while ((haddr = hent->h_addr_list[i++])) {
meillo@10 297 mxip_addr mxip;
meillo@10 298 mxip.ip = *(guint32 *) (haddr);
meillo@10 299 mxip.pref = 0;
meillo@10 300 mxip.name = g_strdup(hent->h_name);
meillo@10 301 list = g_list_append(list, g_memdup(&mxip, sizeof(mxip)));
meillo@10 302 }
meillo@10 303 }
meillo@10 304 return list;
meillo@0 305 }