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