Began removing $Id$ tags. This will be an ongoing process.
[citadel.git] / citadel / domain.c
1 /*
2  * DNS lookup for SMTP sender
3  *
4  * Copyright (c) 1987-2009 by the citadel.org team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "sysdep.h"
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27
28 #ifdef HAVE_RESOLV_H
29 #include <arpa/nameser.h>
30 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
31 #include <arpa/nameser_compat.h>
32 #endif
33 #include <resolv.h>
34 #endif
35 #include <libcitadel.h>
36 #include "sysdep_decls.h"
37 #include "citadel.h"
38 #include "domain.h"
39 #include "server.h"
40 #include "internet_addressing.h"
41
42
43 /*
44  * get_hosts() checks the Internet configuration for various types of
45  * entries and returns them in the same format as getmx() does -- fill the
46  * buffer with a delimited list of hosts and return the number of hosts.
47  * 
48  * This is used to fetch MX smarthosts, SpamAssassin hosts, etc.
49  */
50 int get_hosts(char *mxbuf, char *rectype) {
51         int config_lines;
52         int i;
53         char buf[256];
54         char host[256], type[256];
55         int total_smarthosts = 0;
56
57         if (inetcfg == NULL) return(0);
58         strcpy(mxbuf, "");
59
60         config_lines = num_tokens(inetcfg, '\n');
61         for (i=0; i<config_lines; ++i) {
62                 extract_token(buf, inetcfg, i, '\n', sizeof buf);
63                 extract_token(host, buf, 0, '|', sizeof host);
64                 extract_token(type, buf, 1, '|', sizeof type);
65
66                 if (!strcasecmp(type, rectype)) {
67                         strcat(mxbuf, host);
68                         strcat(mxbuf, "|");
69                         ++total_smarthosts;
70                 }
71         }
72
73         return(total_smarthosts);
74 }
75
76
77 /*
78  * Compare the preference of two MX records.  First check by the actual
79  * number listed in the MX record.  If they're identical, randomize the
80  * result.
81  */
82 int mx_compare_pref(const void *mx1, const void *mx2) {
83         int pref1;
84         int pref2;
85
86         pref1 = ((const struct mx *)mx1)->pref;
87         pref2 = ((const struct mx *)mx2)->pref;
88
89         if (pref1 > pref2) {
90                 return(1);
91         }
92         else if (pref1 < pref2) {
93                 return(0);
94         }
95         else {
96                 return(rand() % 2);
97         }
98 }
99
100
101 /* 
102  * getmx()
103  *
104  * Return one or more MX's for a mail destination.
105  *
106  * Upon success, it fills 'mxbuf' with one or more MX hosts, separated by
107  * vertical bar characters, and returns the number of hosts as its return
108  * value.  If no MX's are found, it returns 0.
109  *
110  */
111 int getmx(char *mxbuf, char *dest) {
112
113 #ifdef HAVE_RESOLV_H
114         union {
115                         u_char bytes[1024];
116                         HEADER header;
117     } answer;
118 #endif
119
120         int ret;
121         unsigned char *startptr, *endptr, *ptr;
122         char expanded_buf[1024];
123         unsigned short pref, type;
124         int n = 0;
125         int qdcount;
126
127         struct mx *mxrecs = NULL;
128         int num_mxrecs = 0;
129         
130         /* If we're configured to send all mail to a smart-host, then our
131          * job here is really easy.
132          */
133         n = get_hosts(mxbuf, "smarthost");
134         if (n > 0) return(n);
135
136         /*
137          * No smart-host?  Look up the best MX for a site.
138          * Make a call to the resolver library.
139          */
140
141         ret = res_query(
142                 dest,
143                 C_IN, T_MX, (unsigned char *)answer.bytes, sizeof(answer)  );
144
145         if (ret < 0) {
146                 mxrecs = malloc(sizeof(struct mx));
147                 mxrecs[0].pref = 0;
148                 strcpy(mxrecs[0].host, dest);
149                 num_mxrecs = 1;
150         }
151         else {
152
153                 /* If we had to truncate, shrink the number to avoid fireworks */
154                 if (ret > sizeof(answer))
155                         ret = sizeof(answer);
156         
157                 startptr = &answer.bytes[0];            /* start and end of buffer */
158                 endptr = &answer.bytes[ret];
159                 ptr = startptr + HFIXEDSZ;      /* advance past header */
160         
161                 for (qdcount = ntohs(answer.header.qdcount); qdcount--; ptr += ret + QFIXEDSZ) {
162                         if ((ret = dn_skipname(ptr, endptr)) < 0) {
163                                 CtdlLogPrintf(CTDL_DEBUG, "dn_skipname error\n");
164                                 return(0);
165                         }
166                 }
167         
168                 while(1) {
169                         memset(expanded_buf, 0, sizeof(expanded_buf));
170                         ret = dn_expand(startptr,
171                                         endptr,
172                                         ptr,
173                                         expanded_buf,
174                                         sizeof(expanded_buf)
175                                         );
176                         if (ret < 0) break;
177                         ptr += ret;
178         
179                         GETSHORT(type, ptr);
180                         ptr += INT16SZ + INT32SZ;
181                         GETSHORT(n, ptr);
182         
183                         if (type != T_MX) {
184                                 ptr += n;
185                         }
186         
187                         else {
188                                 GETSHORT(pref, ptr);
189                                 ret = dn_expand(startptr,
190                                                 endptr,
191                                                 ptr,
192                                                 expanded_buf,
193                                                 sizeof(expanded_buf)
194                                                 );
195                                 ptr += ret;
196         
197                                 ++num_mxrecs;
198                                 if (mxrecs == NULL) {
199                                         mxrecs = malloc(sizeof(struct mx));
200                                 }
201                                 else {
202                                         mxrecs = realloc(mxrecs,
203                                             (sizeof(struct mx) * num_mxrecs) );
204                                 }
205         
206                                 mxrecs[num_mxrecs - 1].pref = pref;
207                                 strcpy(mxrecs[num_mxrecs - 1].host,
208                                        expanded_buf);
209                         }
210                 }
211         }
212
213         /* Sort the MX records by preference */
214         if (num_mxrecs > 1) {
215                 qsort(mxrecs, num_mxrecs, sizeof(struct mx), mx_compare_pref);
216         }
217
218         strcpy(mxbuf, "");
219         for (n=0; n<num_mxrecs; ++n) {
220                 strcat(mxbuf, mxrecs[n].host);
221                 strcat(mxbuf, "|");
222         }
223         free(mxrecs);
224
225         /* Append any fallback smart hosts we have configured.
226          */
227         num_mxrecs += get_hosts(&mxbuf[strlen(mxbuf)], "fallbackhost");
228         return(num_mxrecs);
229 }