1 /*
2 * Copyright (c) 1997-2012 Motonori Nakamura <motonori@wide.ad.jp>
3 * Copyright (c) 1997-2012 WIDE Project
4 * Copyright (c) 1997-2003 Kyoto University
5 * Copyright (c) 2012 National Institute of Informatics
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by WIDE Project and
20 * its contributors.
21 * 4. Neither the name of the Project, the University nor the names of
22 * its contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #ifndef lint
39 static char *_id_ = "$Id: address.c,v 1.44 2012/06/07 07:51:35 motonori Exp $";
40 #endif
41
42 # include "common.h"
43 # include "extern.h"
44 # include "smtp.h"
45
46 int
addmyalias(name)47 addmyalias(name)
48 char *name;
49 {
50 struct hostalias *ha;
51 int len;
52 char *p;
53
54 while (name != NULL && *name != '\0')
55 {
56 p = strchr(name, ',');
57 if (p != NULL)
58 *p++ = '\0';
59 ha = (struct hostalias *)MALLOC(sizeof(struct hostalias));
60 if (ha == NULL)
61 {
62 logg(LOG_NOTICE, "out of memory (addmyalias)");
63 return -1;
64 }
65 /* bzero(ha, sizeof(struct hostalias)); */
66 if (cnf.debug & DEBUG_ADDRESS)
67 logg(LOG_DEBUG, "adding alias: %s", name);
68 len = strlen(name);
69 /* strip trailing dot */
70 while (len > 0 && name[len-1] == '.')
71 name[--len] = '\0';
72 ha->name = newstr(name);
73 /* strlower(ha->name); */
74 ha->next = myaliases;
75 myaliases = ha;
76 name = p;
77 }
78 return 0;
79 }
80
81 int
isamyalias(name)82 isamyalias(name)
83 char *name;
84 {
85 struct hostalias *ha;
86
87 for (ha = myaliases; ha != NULL; ha = ha->next)
88 {
89 if (strcasecmp(name, ha->name) == 0)
90 {
91 return 1;
92 }
93 }
94 return 0;
95 }
96
97 int
addinetaddress(mxp,inet_domain,len,addr)98 addinetaddress(mxp, inet_domain, len, addr)
99 struct mx *mxp;
100 int inet_domain, len;
101 u_char *addr;
102 {
103 struct host *hostp, **hashp;
104 struct inetaddr *iap, *tailp = NULL;
105
106 if (mxp->host == NULL)
107 {
108 hostp = hash_host_lookup(mxp->name, &hashp);
109 if (hostp != NULL)
110 {
111 mxp->host = hostp;
112 }
113 else
114 {
115 hostp = (struct host *)MALLOC(sizeof(struct host));
116 if (hostp == NULL)
117 {
118 logg(LOG_NOTICE,
119 "out of memory (addinetaddress)");
120 return -1;
121 }
122 bzero(hostp, sizeof(struct host));
123 mxp->host = hostp;
124 if (mxp->domain != NULL)
125 {
126 hostp->name = mxp->name;
127 }
128 else
129 {
130 /* mxp is a dummy,
131 just for address registration */
132 hostp->name = newstr(mxp->name);
133 }
134
135 hostp->next = host_list;
136 host_list = hostp;
137 #if 0
138 hash_host_enter(mxp->name, hostp);
139 #else
140 hostp->hash = *hashp;
141 *hashp = hostp;
142 #endif
143 }
144 if (mxp->domain != NULL)
145 {
146 /* insert this MX at top of MX reference list */
147 mxp->mx_ref = hostp->mx_ref;
148 hostp->mx_ref = mxp;
149 }
150 }
151 else
152 hostp = mxp->host;
153
154 if (addr == NULL) /* just link from mxp */
155 {
156 if (cnf.debug & DEBUG_ADDRESS)
157 logg(LOG_DEBUG, "address info for %s linked", mxp->name);
158 return 0;
159 }
160
161 if (mxp->host->state == STAT_FAIL)
162 {
163 /* reset host state if new address found */
164 char buf[64];
165 struct in_addr sin;
166
167 mxp->host->state = STAT_CLOSED;
168 mxp->host->stat = 0;
169
170 switch (inet_domain)
171 {
172 case AF_INET:
173 bcopy(addr, &sin, sizeof(sin));
174 logg(LOG_INFO, "got new address %s for %s, state reset",
175 inet_ntoa(sin), mxp->name);
176 break;
177 #if INET6
178 case AF_INET6:
179 inet_ntop(AF_INET6, addr, buf, sizeof(buf));
180 logg(LOG_INFO, "got new address %s for %s, state reset",
181 buf, mxp->name);
182
183 break;
184 #endif
185 }
186 }
187
188 if (cnf.debug & DEBUG_ADDRESS)
189 {
190 char buf[64];
191 struct in_addr sin;
192
193 switch (inet_domain)
194 {
195 case AF_INET:
196 bcopy(addr, &sin, sizeof(sin));
197 logg(LOG_DEBUG, "DNS: registering %s=%s",
198 mxp->name, inet_ntoa(sin));
199 break;
200 #if INET6
201 case AF_INET6:
202 inet_ntop(AF_INET6, addr, buf, sizeof(buf));
203 logg(LOG_DEBUG, "DNS: registering %s=%s",
204 mxp->name, buf);
205
206 break;
207 #endif
208 default:
209 logg(LOG_NOTICE,
210 "DNS: invalid inet_domain %d at addinetaddress",
211 inet_domain);
212 }
213 }
214
215 for (iap = hostp->addr; iap != NULL; iap = iap->next)
216 {
217 if (iap->domain == inet_domain
218 && iap->len == len
219 && bcmp(iap->address, addr, len) == 0)
220 {
221 /* already have it */
222 return 0;
223 }
224 tailp = iap; /* get tail of list */
225 }
226
227 iap = (struct inetaddr *)MALLOC(sizeof(struct inetaddr));
228 if (iap == NULL)
229 {
230 logg(LOG_NOTICE, "out of memory (addinetaddress)");
231 return -1;
232 }
233 bzero(iap, sizeof(struct inetaddr));
234 iap->domain = inet_domain;
235 iap->len = len;
236 iap->address = (char *)MALLOC(len);
237 if (iap->address == NULL)
238 {
239 logg(LOG_NOTICE, "out of memory (addinetaddress)");
240 return -1;
241 }
242 bcopy(addr, iap->address, len);
243 #if INET6
244 if (((cnf.inetdom & SMTP_V6_FIRST) && inet_domain == AF_INET6)
245 || ((cnf.inetdom & SMTP_V4_FIRST) && inet_domain == AF_INET)
246 || tailp == NULL)
247 {
248 iap->next = hostp->addr;
249 hostp->current = hostp->addr = iap;
250 }
251 else
252 {
253 tailp->next = iap;
254 iap->next = NULL;
255 }
256 #else
257 iap->next = hostp->addr;
258 hostp->current = hostp->addr = iap;
259 #endif
260 return 0;
261 }
262
263 char *
parse_address(str,err,addr,dom)264 parse_address(str, err, addr, dom)
265 char *str;
266 char **err, **addr, **dom;
267 {
268 static char addrbuf[MAXLINE], domain[MAXLINE];
269 char *p, *d;
270 int inquote = 0;
271 int incomment = 0;
272 int indomain = 0;
273 int numerical = 0;
274 int addrquote = 0;
275 int gotpure = 0;
276 int routeaddr = 0;
277 int len;
278
279 len = strlen(str);
280 if (len >= MAXLINE)
281 return NULL;
282
283 /* skip leading spaces */
284 while (*str != '\0' && isspace(*str))
285 str++;
286
287 *err = NULL; /* error message */
288 p = addrbuf + 1;
289 addrbuf[0] = '<';
290 addrbuf[1] = '\0';
291 *domain = '\0';
292 d = NULL;
293
294 while (*str != '\0')
295 {
296 if (inquote)
297 {
298 switch (*str)
299 {
300 case '"':
301 inquote = 0;
302 break;
303 case '\\':
304 *p++ = *str++;
305 break;
306 }
307 *p++ = *str++;
308 continue;
309 }
310 if (incomment)
311 {
312 switch (*str++)
313 {
314 case '(':
315 incomment++;
316 break;
317 case ')':
318 incomment--;
319 break;
320 case '\\':
321 str++;
322 break;
323 }
324 continue;
325 }
326 if (indomain)
327 {
328 if (*str == '[' && d == domain)
329 {
330 *d++ = *p++ = *str++;
331 numerical = 1;
332 continue;
333 }
334 if (numerical && *str == ']')
335 {
336 *d++ = *p++ = *str++;
337 numerical = 0;
338 indomain = 0;
339 continue;
340 }
341 if (isalnum(*str)
342 #if 1 /* allow underscore in domain part */
343 || *str == '_'
344 #endif
345 || *str == '.' || *str == '-')
346 {
347 *d++ = *p++ = *str++;
348 continue;
349 }
350 if (*str == '(')
351 {
352 incomment++;
353 str++;
354 continue;
355 }
356 indomain = 0;
357 }
358 if (isspace(*str))
359 break;
360 if (isalnum(*str)
361 || strchr("!.#$%&^*-+/=?_~{}'`|", *str) != NULL)
362 {
363 if (gotpure)
364 str++;
365 else
366 *p++ = *str++;
367 continue;
368 }
369 switch (*str)
370 {
371 case '"':
372 inquote = 1;
373 *p++ = *str++;
374 break;
375 case '(':
376 incomment++;
377 str++;
378 break;
379 case ')':
380 *err = "Unbalanced comment parenthesis '(', ')'";
381 return NULL;
382 case '<':
383 gotpure = 0;
384 p = addrbuf + 1; /* reset buffer */
385 addrquote++;
386 str++;
387 break;
388 case '>':
389 gotpure = 1;
390 addrquote--;
391 str++;
392 break;
393 case '\\':
394 *p++ = *str++;
395 if (*str != '\0')
396 *p++ = *str++;
397 break;
398 case '@':
399 if (p == addrbuf + 1)
400 {
401 d = domain;
402 routeaddr = 1;
403 indomain = 1;
404 numerical = 0;
405 }
406 if (!routeaddr)
407 {
408 d = domain;
409 indomain = 1;
410 numerical = 0;
411 }
412 *p++ = *str++;
413 break;
414 case ':':
415 case ',':
416 *p++ = *str++;
417 break;
418 default:
419 *err = "invalid character in address expression";
420 return NULL;
421 }
422 }
423
424 if (addrquote)
425 {
426 *err = "Unbalanced address quotes ('<', '>')";
427 return NULL;
428 }
429 if (inquote)
430 {
431 *err = "Unbalanced quotes ('\"')";
432 return NULL;
433 }
434 if (incomment)
435 {
436 *err = "Unbalanced comment parenthesis ('(', ')')";
437 return NULL;
438 }
439
440 *p++ = '>';
441 *p = '\0';
442 if (d != NULL)
443 *d = '\0';
444 if (addr != NULL)
445 *addr = addrbuf;
446 if (dom != NULL)
447 {
448 /* strip trailing dot */
449 len = strlen(domain);
450 while (len > 0 && domain[len-1] == '.')
451 domain[--len] = '\0';
452 /* strlower(domain); */
453 *dom = domain;
454 }
455 return str; /* rest of string */
456 }
457
458 int
addrecipient(addr,domain,notify,orcpt)459 addrecipient(addr, domain, notify, orcpt)
460 char *addr, *domain, *notify, *orcpt;
461 {
462 struct recipient *rcpt;
463 struct domain *d, **hashp;
464
465 if (cnf.debug & DEBUG_LMTP)
466 logg(LOG_DEBUG, "RCPT TO: %s notify=%s orcpt=%s", addr,
467 (notify == NULL)?"":notify, (orcpt == NULL)?"":orcpt);
468 rcpt = (struct recipient *)MALLOC(sizeof(struct recipient));
469 if (rcpt == NULL)
470 {
471 logg(LOG_NOTICE, "out of memory (addrecipient)");
472 return -1;
473 }
474 bzero(rcpt, sizeof(struct recipient));
475 rcpt->address = newstr(addr);
476 if (rcpt->address == NULL)
477 {
478 return -1;
479 }
480 if (notify != NULL)
481 {
482 rcpt->notify = newstr(notify);
483 if (rcpt->notify == NULL)
484 return -1;
485 }
486 if (orcpt != NULL)
487 {
488 rcpt->orcpt = newstr(orcpt);
489 if (rcpt->orcpt == NULL)
490 return -1;
491 }
492
493 d = hash_domain_lookup(domain, &hashp);
494 if (d == NULL)
495 {
496 if (cnf.debug & DEBUG_LMTP)
497 logg(LOG_DEBUG, "new domain: %s", domain);
498 rcpt->domain = (struct domain *)MALLOC(sizeof(struct domain));
499 bzero(rcpt->domain, sizeof(struct domain));
500 rcpt->domain->name = newstr(domain);
501 if (rcpt->domain->name == NULL)
502 return -1;
503 rcpt->domain->rcpts = hash_domain_rcpts_lookup(domain);
504 rcpt->domain->rcpt_top = rcpt;
505 rcpt->domain->rcpt_tail = rcpt;
506 rcpt->domain->next = domain_list;
507 domain_list = rcpt->domain;
508 #if 0
509 hash_domain_enter(domain, rcpt->domain);
510 #else
511 rcpt->domain->hash = *hashp;
512 *hashp = rcpt->domain;
513 #endif
514 rcpt->domain->map_arg = host_map_lookup(rcpt->domain->name);
515 }
516 else
517 {
518 rcpt->domain = d;
519 if (rcpt->domain->rcpt_tail == NULL)
520 {
521 rcpt->domain->rcpt_top = rcpt;
522 rcpt->domain->rcpt_tail = rcpt;
523 }
524 else
525 {
526 rcpt->domain->rcpt_tail->dom_chain = rcpt;
527 rcpt->domain->rcpt_tail = rcpt;
528 }
529 }
530 rcpt->dom_chain = NULL;
531
532 if (env.rcpt_list == NULL)
533 {
534 env.resp_ptr = env.rcpt_list = env.rcpt_tail = rcpt;
535 }
536 else
537 {
538 env.rcpt_tail->next = rcpt;
539 env.rcpt_tail = rcpt;
540 }
541 return 0;
542 }
543
544 void
dnsstatus(rcpt,rcode,response)545 dnsstatus(rcpt, rcode, response)
546 struct recipient *rcpt;
547 int rcode;
548 char *response;
549 {
550 logg(LOG_INFO, "(%d+%d+%d+%d/%d) relay=%s to=%s proto=%s delay=%d code=%d (%s)",
551 sti.nsent, sti.ndeferred, sti.nnsfailed,
552 sti.nsmtpfailed, sti.nrcpt, "unknown",
553 rcpt->address, "unknown", 0, rcode,
554 (response==NULL)?"?":response);
555 }
556
557 void
finalstatus()558 finalstatus()
559 {
560 struct recipient *rcpt;
561 struct domain *dom;
562
563 for (rcpt = env.rcpt_list; rcpt != NULL; rcpt = rcpt->next)
564 {
565 if (rcpt->result != 0)
566 /* have already result status */
567 continue;
568 if ((dom = rcpt->domain) == NULL)
569 {
570 rcpt->stat = RCPT_DONE;
571 rcpt->result = SMTP_TEMPFAIL(51);
572 rcpt->response = "Unknown system error (domain)";
573 dnsstatus(rcpt, rcpt->result, rcpt->response);
574 continue;
575 }
576 switch (dom->stat)
577 {
578 case EX_OK:
579 break;
580 case EX_TEMPFAIL:
581 rcpt->stat = RCPT_DONE;
582 rcpt->result = SMTP_TEMPFAIL(52);
583 rcpt->response = dom->response;
584 dnsstatus(rcpt, rcpt->result, rcpt->response);
585 continue;
586 case EX_NOHOST:
587 sti.nnsfailed++;
588 rcpt->stat = RCPT_DONE;
589 rcpt->result = SMTP_ERROR(50);
590 rcpt->response = dom->response;
591 dnsstatus(rcpt, rcpt->result, rcpt->response);
592 continue;
593 case EX_CONFIG:
594 sti.nnsfailed++;
595 rcpt->stat = RCPT_DONE;
596 rcpt->result = SMTP_ERROR(54);
597 rcpt->response = dom->response;
598 dnsstatus(rcpt, rcpt->result, rcpt->response);
599 continue;
600 case EX_OSERR:
601 default:
602 rcpt->stat = RCPT_DONE;
603 rcpt->result = SMTP_TEMPFAIL(51);
604 rcpt->response = "Unknown system error (domain)";
605 dnsstatus(rcpt, rcpt->result, rcpt->response);
606 continue;
607 }
608 if (dom->firstmx == NULL)
609 {
610 rcpt->stat = RCPT_DONE;
611 rcpt->result = SMTP_TEMPFAIL(51);
612 rcpt->response = "Unknown system error (MX)";
613 dnsstatus(rcpt, rcpt->result, rcpt->response);
614 continue;
615 }
616 }
617 }
618