/* * Copyright (c) 1997-2003 Motonori Nakamura * Copyright (c) 1997-2003 WIDE Project * Copyright (c) 1997-2003 Kyoto University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by WIDE Project and * its contributors. * 4. Neither the name of the Project, the University nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char *_id_ = "$Id: address.c,v 1.42 2003/08/20 05:12:58 motonori Exp $"; #endif # include "common.h" # include "extern.h" # include "smtp.h" int addmyalias(name) char *name; { struct hostalias *ha; int len; char *p; while (name != NULL && *name != '\0') { p = strchr(name, ','); if (p != NULL) *p++ = '\0'; ha = (struct hostalias *)MALLOC(sizeof(struct hostalias)); if (ha == NULL) { log(LOG_NOTICE, "out of memory (addmyalias)"); return -1; } /* bzero(ha, sizeof(struct hostalias)); */ if (cnf.debug & DEBUG_ADDRESS) log(LOG_DEBUG, "adding alias: %s", name); len = strlen(name); /* strip trailing dot */ while (len > 0 && name[len-1] == '.') name[--len] = '\0'; ha->name = newstr(name); /* strlower(ha->name); */ ha->next = myaliases; myaliases = ha; name = p; } return 0; } int isamyalias(name) char *name; { struct hostalias *ha; for (ha = myaliases; ha != NULL; ha = ha->next) { if (strcasecmp(name, ha->name) == 0) { return 1; } } return 0; } int addinetaddress(mxp, inet_domain, len, addr) struct mx *mxp; int inet_domain, len; u_char *addr; { struct host *hostp, **hashp; struct inetaddr *iap, *tailp = NULL; if (mxp->host == NULL) { hostp = hash_host_lookup(mxp->name, &hashp); if (hostp != NULL) { mxp->host = hostp; } else { hostp = (struct host *)MALLOC(sizeof(struct host)); if (hostp == NULL) { log(LOG_NOTICE, "out of memory (addinetaddress)"); return -1; } bzero(hostp, sizeof(struct host)); mxp->host = hostp; if (mxp->domain != NULL) { hostp->name = mxp->name; } else { /* mxp is a dummy, just for address registration */ hostp->name = newstr(mxp->name); } hostp->next = host_list; host_list = hostp; #if 0 hash_host_enter(mxp->name, hostp); #else hostp->hash = *hashp; *hashp = hostp; #endif } if (mxp->domain != NULL) { /* insert this MX at top of MX reference list */ mxp->mx_ref = hostp->mx_ref; hostp->mx_ref = mxp; } } else hostp = mxp->host; if (addr == NULL) /* just link from mxp */ { if (cnf.debug & DEBUG_ADDRESS) log(LOG_DEBUG, "address info for %s linked", mxp->name); return 0; } if (mxp->host->state == STAT_FAIL) { /* reset host state if new address found */ char buf[64]; struct in_addr sin; mxp->host->state = STAT_CLOSED; mxp->host->stat = 0; switch (inet_domain) { case AF_INET: bcopy(addr, &sin, sizeof(sin)); log(LOG_INFO, "got new address %s for %s, state reset", inet_ntoa(sin), mxp->name); break; #if INET6 case AF_INET6: inet_ntop(AF_INET6, addr, buf, sizeof(buf)); log(LOG_INFO, "got new address %s for %s, state reset", buf, mxp->name); break; #endif } } if (cnf.debug & DEBUG_ADDRESS) { char buf[64]; struct in_addr sin; switch (inet_domain) { case AF_INET: bcopy(addr, &sin, sizeof(sin)); log(LOG_DEBUG, "DNS: registering %s=%s", mxp->name, inet_ntoa(sin)); break; #if INET6 case AF_INET6: inet_ntop(AF_INET6, addr, buf, sizeof(buf)); log(LOG_DEBUG, "DNS: registering %s=%s", mxp->name, buf); break; #endif default: log(LOG_NOTICE, "DNS: invalid inet_domain %d at addinetaddress", inet_domain); } } for (iap = hostp->addr; iap != NULL; iap = iap->next) { if (iap->domain == inet_domain && iap->len == len && bcmp(iap->address, addr, len) == 0) { /* already have it */ return 0; } tailp = iap; /* get tail of list */ } iap = (struct inetaddr *)MALLOC(sizeof(struct inetaddr)); if (iap == NULL) { log(LOG_NOTICE, "out of memory (addinetaddress)"); return -1; } bzero(iap, sizeof(struct inetaddr)); iap->domain = inet_domain; iap->len = len; iap->address = (char *)MALLOC(len); if (iap->address == NULL) { log(LOG_NOTICE, "out of memory (addinetaddress)"); return -1; } bcopy(addr, iap->address, len); #if INET6 if (((cnf.inetdom & SMTP_V6_FIRST) && inet_domain == AF_INET6) || ((cnf.inetdom & SMTP_V4_FIRST) && inet_domain == AF_INET) || tailp == NULL) { iap->next = hostp->addr; hostp->current = hostp->addr = iap; } else { tailp->next = iap; iap->next = NULL; } #else iap->next = hostp->addr; hostp->current = hostp->addr = iap; #endif return 0; } char * parse_address(str, err, addr, dom) char *str; char **err, **addr, **dom; { static char addrbuf[MAXLINE], domain[MAXLINE]; char *p, *d; int inquote = 0; int incomment = 0; int indomain = 0; int numerical = 0; int addrquote = 0; int gotpure = 0; int routeaddr = 0; int len; len = strlen(str); if (len >= MAXLINE) return NULL; /* skip leading spaces */ while (*str != '\0' && isspace(*str)) str++; *err = NULL; /* error message */ p = addrbuf + 1; addrbuf[0] = '<'; addrbuf[1] = '\0'; *domain = '\0'; d = NULL; while (*str != '\0') { if (inquote) { switch (*str) { case '"': inquote = 0; break; case '\\': *p++ = *str++; break; } *p++ = *str++; continue; } if (incomment) { switch (*str++) { case '(': incomment++; break; case ')': incomment--; break; case '\\': str++; break; } continue; } if (indomain) { if (*str == '[' && d == domain) { *d++ = *p++ = *str++; numerical = 1; continue; } if (numerical && *str == ']') { *d++ = *p++ = *str++; numerical = 0; indomain = 0; continue; } if (isalnum(*str) #if 1 /* allow underscore in domain part */ || *str == '_' #endif || *str == '.' || *str == '-') { *d++ = *p++ = *str++; continue; } if (*str == '(') { incomment++; str++; continue; } indomain = 0; } if (isspace(*str)) break; if (isalnum(*str) || strchr("!.#$%&^*-+/=?_~{}'`|", *str) != NULL) { if (gotpure) str++; else *p++ = *str++; continue; } switch (*str) { case '"': inquote = 1; *p++ = *str++; break; case '(': incomment++; str++; break; case ')': *err = "Unbalanced comment parenthesis '(', ')'"; return NULL; case '<': gotpure = 0; p = addrbuf + 1; /* reset buffer */ addrquote++; str++; break; case '>': gotpure = 1; addrquote--; str++; break; case '\\': *p++ = *str++; if (*str != '\0') *p++ = *str++; break; case '@': if (p == addrbuf + 1) { d = domain; routeaddr = 1; indomain = 1; numerical = 0; } if (!routeaddr) { d = domain; indomain = 1; numerical = 0; } *p++ = *str++; break; case ':': case ',': *p++ = *str++; break; default: *err = "invalid character in address expression"; return NULL; } } if (addrquote) { *err = "Unbalanced address quotes ('<', '>')"; return NULL; } if (inquote) { *err = "Unbalanced quotes ('\"')"; return NULL; } if (incomment) { *err = "Unbalanced comment parenthesis ('(', ')')"; return NULL; } *p++ = '>'; *p = '\0'; if (d != NULL) *d = '\0'; if (addr != NULL) *addr = addrbuf; if (dom != NULL) { /* strip trailing dot */ len = strlen(domain); while (len > 0 && domain[len-1] == '.') domain[--len] = '\0'; /* strlower(domain); */ *dom = domain; } return str; /* rest of string */ } int addrecipient(addr, domain, notify, orcpt) char *addr, *domain, *notify, *orcpt; { struct recipient *rcpt; struct domain *d, **hashp; if (cnf.debug & DEBUG_LMTP) log(LOG_DEBUG, "RCPT TO: %s notify=%s orcpt=%s", addr, (notify == NULL)?"":notify, (orcpt == NULL)?"":orcpt); rcpt = (struct recipient *)MALLOC(sizeof(struct recipient)); if (rcpt == NULL) { log(LOG_NOTICE, "out of memory (addrecipient)"); return -1; } bzero(rcpt, sizeof(struct recipient)); rcpt->address = newstr(addr); if (rcpt->address == NULL) { return -1; } if (notify != NULL) { rcpt->notify = newstr(notify); if (rcpt->notify == NULL) return -1; } if (orcpt != NULL) { rcpt->orcpt = newstr(orcpt); if (rcpt->orcpt == NULL) return -1; } d = hash_domain_lookup(domain, &hashp); if (d == NULL) { if (cnf.debug & DEBUG_LMTP) log(LOG_DEBUG, "new domain: %s", domain); rcpt->domain = (struct domain *)MALLOC(sizeof(struct domain)); bzero(rcpt->domain, sizeof(struct domain)); rcpt->domain->name = newstr(domain); if (rcpt->domain->name == NULL) return -1; rcpt->domain->rcpts = hash_domain_rcpts_lookup(domain); rcpt->domain->rcpt_top = rcpt; rcpt->domain->rcpt_tail = rcpt; rcpt->domain->next = domain_list; domain_list = rcpt->domain; #if 0 hash_domain_enter(domain, rcpt->domain); #else rcpt->domain->hash = *hashp; *hashp = rcpt->domain; #endif rcpt->domain->map_arg = host_map_lookup(rcpt->domain->name); } else { rcpt->domain = d; if (rcpt->domain->rcpt_tail == NULL) { rcpt->domain->rcpt_top = rcpt; rcpt->domain->rcpt_tail = rcpt; } else { rcpt->domain->rcpt_tail->dom_chain = rcpt; rcpt->domain->rcpt_tail = rcpt; } } rcpt->dom_chain = NULL; if (env.rcpt_list == NULL) { env.resp_ptr = env.rcpt_list = env.rcpt_tail = rcpt; } else { env.rcpt_tail->next = rcpt; env.rcpt_tail = rcpt; } return 0; } void dnsstatus(rcpt, rcode, response) struct recipient *rcpt; int rcode; char *response; { log(LOG_INFO, "(%d+%d+%d+%d/%d) relay=%s to=%s proto=%s delay=%d code=%d (%s)", sti.nsent, sti.ndeferred, sti.nnsfailed, sti.nsmtpfailed, sti.nrcpt, "unknown", rcpt->address, "unknown", 0, rcode, (response==NULL)?"?":response); } void finalstatus() { struct recipient *rcpt; struct domain *dom; for (rcpt = env.rcpt_list; rcpt != NULL; rcpt = rcpt->next) { if (rcpt->result != 0) /* have already result status */ continue; if ((dom = rcpt->domain) == NULL) { rcpt->stat = RCPT_DONE; rcpt->result = SMTP_TEMPFAIL(51); rcpt->response = "Unknown system error (domain)"; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; } switch (dom->stat) { case EX_OK: break; case EX_TEMPFAIL: rcpt->stat = RCPT_DONE; rcpt->result = SMTP_TEMPFAIL(52); rcpt->response = dom->response; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; case EX_NOHOST: sti.nnsfailed++; rcpt->stat = RCPT_DONE; rcpt->result = SMTP_ERROR(50); rcpt->response = dom->response; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; case EX_CONFIG: sti.nnsfailed++; rcpt->stat = RCPT_DONE; rcpt->result = SMTP_ERROR(54); rcpt->response = dom->response; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; case EX_OSERR: default: rcpt->stat = RCPT_DONE; rcpt->result = SMTP_TEMPFAIL(51); rcpt->response = "Unknown system error (domain)"; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; } if (dom->firstmx == NULL) { rcpt->stat = RCPT_DONE; rcpt->result = SMTP_TEMPFAIL(51); rcpt->response = "Unknown system error (MX)"; dnsstatus(rcpt, rcpt->result, rcpt->response); continue; } } }