* More work on fake resolver
[citadel.git] / citadel / domain.c
1 /*
2  * $Id$
3  *
4  * DNS lookup for SMTP sender
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <netinet/in.h>
12 #include <stdio.h>
13
14 #ifdef HAVE_RESOLV_H
15 #include <arpa/nameser.h>
16 #include <resolv.h>
17 #endif
18
19 #include "sysdep_decls.h"
20 #include "citadel.h"
21 #include "domain.h"
22 #include "server.h"
23 #include "tools.h"
24 #include "internet_addressing.h"
25
26
27 /*
28  * get_hosts() checks the Internet configuration for various types of
29  * entries and returns them in the same format as getmx() does -- fill the
30  * buffer with a delimited list of hosts and return the number of hosts.
31  * 
32  * This is used to fetch MX smarthosts, SpamAssassin hosts, etc.
33  */
34 int get_hosts(char *mxbuf, char *rectype) {
35         int config_lines;
36         int i;
37         char buf[SIZ];
38         char host[SIZ], type[SIZ];
39         int total_smarthosts = 0;
40
41         if (inetcfg == NULL) return(0);
42         strcpy(mxbuf, "");
43
44         config_lines = num_tokens(inetcfg, '\n');
45         for (i=0; i<config_lines; ++i) {
46                 extract_token(buf, inetcfg, i, '\n');
47                 extract_token(host, buf, 0, '|');
48                 extract_token(type, buf, 1, '|');
49
50                 if (!strcasecmp(type, rectype)) {
51                         strcat(mxbuf, host);
52                         strcat(mxbuf, "|");
53                         ++total_smarthosts;
54                 }
55         }
56
57         return(total_smarthosts);
58 }
59
60
61 /*
62  * Compare the preference of two MX records.  First check by the actual
63  * number listed in the MX record.  If they're identical, randomize the
64  * result.
65  */
66 inline int mx_compare_pref(int pref1, int pref2) {
67         if (pref1 > pref2) {
68                 return(1);
69         }
70         else if (pref1 < pref2) {
71                 return(0);
72         }
73         else {
74                 return(rand() % 2);
75         }
76 }
77
78
79 /*
80  * sort_mxrecs()
81  *
82  * Sort a pile of MX records (struct mx, definted in domain.h) by preference
83  *
84  */
85 void sort_mxrecs(struct mx *mxrecs, int num_mxrecs) {
86         int a, b;
87         struct mx hold1, hold2;
88
89         if (num_mxrecs < 2) return;
90
91         /* do the sort */
92         for (a = num_mxrecs - 2; a >= 0; --a) {
93                 for (b = 0; b <= a; ++b) {
94                         if (mx_compare_pref(mxrecs[b].pref,mxrecs[b+1].pref)) {
95                                 memcpy(&hold1, &mxrecs[b], sizeof(struct mx));
96                                 memcpy(&hold2, &mxrecs[b+1], sizeof(struct mx));
97                                 memcpy(&mxrecs[b], &hold2, sizeof(struct mx));
98                                 memcpy(&mxrecs[b+1], &hold1, sizeof(struct mx));
99                         }
100                 }
101         }
102 }
103
104
105
106 /* 
107  * getmx()
108  *
109  * Return one or more MX's for a mail destination.
110  *
111  * Upon success, it fills 'mxbuf' with one or more MX hosts, separated by
112  * vertical bar characters, and returns the number of hosts as its return
113  * value.  If no MX's are found, it returns 0.
114  *
115  */
116 int getmx(char *mxbuf, char *dest) {
117
118 #ifdef HAVE_RESOLV_H
119         union {
120                         u_char bytes[1024];
121                         HEADER header;
122     } answer;
123 #endif
124
125         int ret;
126         unsigned char *startptr, *endptr, *ptr;
127         char expanded_buf[1024];
128         char buf[SIZ];
129         FILE *fp;
130         unsigned short pref, type;
131         int n = 0;
132         int qdcount;
133
134         struct mx *mxrecs = NULL;
135         int num_mxrecs = 0;
136         
137         /* If we're configured to send all mail to a smart-host, then our
138          * job here is really easy.
139          */
140         n = get_hosts(mxbuf, "smarthost");
141         if (n > 0) return(n);
142
143
144         /*
145          * No smart-host?  Look up the best MX for a site.
146          */
147
148 #ifndef HAVE_RESOLV_H
149
150         /*
151          * On systems with b0rken or non-standard resolver libraries, learn
152          * the MX records by calling "nslookup" from the command line.
153          */
154         sprintf(buf, "nslookup -query=mx %s", dest);
155         fp = popen(buf, "r");
156         if (fp == NULL) return(0);
157         while (fgets(buf, sizeof buf, fp) != NULL) {
158                 buf[strlen(buf) - 1] = 0;
159                 lprintf(9, "RESOLV: %s\n", buf);
160         }
161         pclose(fp);
162         return(0);      /* FIXME */
163
164 #else /* HAVE_RESOLV_H */
165
166         /*
167          * Make a call to the standard resolver library.
168          */
169
170         ret = res_query(
171                 dest,
172                 C_IN, T_MX, (unsigned char *)answer.bytes, sizeof(answer)  );
173
174         if (ret < 0) {
175                 mxrecs = mallok(sizeof(struct mx));
176                 mxrecs[0].pref = 0;
177                 strcpy(mxrecs[0].host, dest);
178                 num_mxrecs = 1;
179         }
180         else {
181
182                 /* If we had to truncate, shrink the number to avoid fireworks */
183                 if (ret > sizeof(answer))
184                         ret = sizeof(answer);
185         
186                 startptr = &answer.bytes[0];            /* start and end of buffer */
187                 endptr = &answer.bytes[ret];
188                 ptr = startptr + HFIXEDSZ;      /* advance past header */
189         
190                 for (qdcount = ntohs(answer.header.qdcount); qdcount--; ptr += ret + QFIXEDSZ) {
191                         if ((ret = dn_skipname(ptr, endptr)) < 0) {
192                                 lprintf(9, "dn_skipname error\n");
193                                 return(0);
194                         }
195                 }
196         
197                 while(1) {
198                         memset(expanded_buf, 0, sizeof(expanded_buf));
199                         ret = dn_expand(startptr,
200                                         endptr,
201                                         ptr,
202                                         expanded_buf,
203                                         sizeof(expanded_buf)
204                                         );
205                         if (ret < 0) break;
206                         ptr += ret;
207         
208                         GETSHORT(type, ptr);
209                         ptr += INT16SZ + INT32SZ;
210                         GETSHORT(n, ptr);
211         
212                         if (type != T_MX) {
213                                 ptr += n;
214                         }
215         
216                         else {
217                                 GETSHORT(pref, ptr);
218                                 ret = dn_expand(startptr,
219                                                 endptr,
220                                                 ptr,
221                                                 expanded_buf,
222                                                 sizeof(expanded_buf)
223                                                 );
224                                 ptr += ret;
225         
226                                 ++num_mxrecs;
227                                 if (mxrecs == NULL) {
228                                         mxrecs = mallok(sizeof(struct mx));
229                                 }
230                                 else {
231                                         mxrecs = reallok(mxrecs,
232                                             (sizeof(struct mx) * num_mxrecs) );
233                                 }
234         
235                                 mxrecs[num_mxrecs - 1].pref = pref;
236                                 strcpy(mxrecs[num_mxrecs - 1].host,
237                                        expanded_buf);
238                         }
239                 }
240         }
241 #endif /* HAVE_RESOLV_H */
242
243         sort_mxrecs(mxrecs, num_mxrecs);
244
245         strcpy(mxbuf, "");
246         for (n=0; n<num_mxrecs; ++n) {
247                 strcat(mxbuf, mxrecs[n].host);
248                 strcat(mxbuf, "|");
249         }
250         phree(mxrecs);
251         return(num_mxrecs);
252 }