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