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