📄 smtpd.c
字号:
#include "common.h"#include "smtpd.h"#include "smtp.h"#include <ctype.h>#include <ip.h>#include <ndb.h>#include <mp.h>#include <libsec.h>#include <auth.h>#include "../smtp/y.tab.h"#define DBGMX 1char *me;char *him="";char *dom;process *pp;String *mailer;NetConnInfo *nci;int filterstate = ACCEPT;int trusted;int logged;int rejectcount;int hardreject;Biobuf bin;int debug;int Dflag;int fflag;int gflag;int rflag;int sflag;int authenticate;int authenticated;int passwordinclear;char *tlscert;List senders;List rcvers;char pipbuf[ERRMAX];char *piperror;int pipemsg(int*);String* startcmd(void);int rejectcheck(void);String* mailerpath(char*);static intcatchalarm(void *a, char *msg){ int rv = 1; USED(a); /* log alarms but continue */ if(strstr(msg, "alarm")){ if(senders.first && rcvers.first) syslog(0, "smtpd", "note: %s->%s: %s", s_to_c(senders.first->p), s_to_c(rcvers.first->p), msg); else syslog(0, "smtpd", "note: %s", msg); rv = 0; } /* kill the children if there are any */ if(pp) syskillpg(pp->pid); return rv;} /* override string error functions to do something reasonable */voids_error(char *f, char *status){ char errbuf[Errlen]; errbuf[0] = 0; rerrstr(errbuf, sizeof(errbuf)); if(f && *f) reply("452 out of memory %s: %s\r\n", f, errbuf); else reply("452 out of memory %s\r\n", errbuf); syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys); exits(status);}voidmain(int argc, char **argv){ char *p, buf[1024]; char *netdir; netdir = nil; quotefmtinstall(); ARGBEGIN{ case 'D': Dflag++; break; case 'd': debug++; break; case 'n': /* log peer ip address */ netdir = ARGF(); break; case 'f': /* disallow relaying */ fflag = 1; break; case 'g': gflag = 1; break; case 'h': /* default domain name */ dom = ARGF(); break; case 'k': /* prohibited ip address */ p = ARGF(); if (p) addbadguy(p); break; case 'm': /* set mail command */ p = ARGF(); if(p) mailer = mailerpath(p); break; case 'r': rflag = 1; /* verify sender's domain */ break; case 's': /* save blocked messages */ sflag = 1; break; case 'a': authenticate = 1; break; case 'p': passwordinclear = 1; break; case 'c': tlscert = ARGF(); break; case 't': fprint(2, "%s: the -t option is no longer supported, see -c\n", argv0); tlscert = "/sys/lib/ssl/smtpd-cert.pem"; break; default: fprint(2, "usage: smtpd [-dfhrs] [-n net] [-c cert]\n"); exits("usage"); }ARGEND; nci = getnetconninfo(netdir, 0); if(nci == nil) sysfatal("can't get remote system's address"); if(mailer == nil) mailer = mailerpath("send"); if(debug){ close(2); snprint(buf, sizeof(buf), "%s/smtpd.db", UPASLOG); if (open(buf, OWRITE) >= 0) { seek(2, 0, 2); fprint(2, "%d smtpd %s\n", getpid(), thedate()); } else debug = 0; } getconf(); Binit(&bin, 0, OREAD); chdir(UPASLOG); me = sysname_read(); if(dom == 0 || dom[0] == 0) dom = domainname_read(); if(dom == 0 || dom[0] == 0) dom = me; sayhi(); parseinit(); /* allow 45 minutes to parse the header */ atnotify(catchalarm, 1); alarm(45*60*1000); zzparse(); exits(0);}voidlistfree(List *l){ Link *lp; Link *next; for(lp = l->first; lp; lp = next){ next = lp->next; s_free(lp->p); free(lp); } l->first = l->last = 0;}voidlistadd(List *l, String *path){ Link *lp; lp = (Link *)malloc(sizeof(Link)); lp->p = path; lp->next = 0; if(l->last) l->last->next = lp; else l->first = lp; l->last = lp;}#define SIZE 4096intreply(char *fmt, ...){ char buf[SIZE], *out; va_list arg; int n; va_start(arg, fmt); out = vseprint(buf, buf+SIZE, fmt, arg); va_end(arg); n = (long)(out-buf); if(debug) { seek(2, 0, 2); write(2, buf, n); } write(1, buf, n); return n;}voidreset(void){ if(rejectcheck()) return; listfree(&rcvers); listfree(&senders); if(filterstate != DIALUP){ logged = 0; filterstate = ACCEPT; } reply("250 ok\r\n");}voidsayhi(void){ reply("220 %s SMTP\r\n", dom);}voidhello(String *himp, int extended){ char **mynames; him = s_to_c(himp); syslog(0, "smtpd", "%s from %s as %s", extended ? "ehlo" : "helo", nci->rsys, him); if(rejectcheck()) return; if(strchr(him, '.') && nci && !trusted && fflag && strcmp(nci->rsys, nci->lsys) != 0){ /* * We don't care if he lies about who he is, but it is * not okay to pretend to be us. Many viruses do this, * just parroting back what we say in the greeting. */ if(strcmp(him, dom) == 0) goto Liarliar; for(mynames=sysnames_read(); mynames && *mynames; mynames++){ if(cistrcmp(*mynames, him) == 0){ Liarliar: syslog(0, "smtpd", "Hung up on %s; claimed to be %s", nci->rsys, him); reply("554 Liar!\r\n"); exits("client pretended to be us"); return; } } } /* * it is never acceptable to claim to be "localhost", * "localhost.localdomain" or "localhost.example.com"; only spammers * do this. it is also unacceptable to claim any string that doesn't * look like a domain name (e.g., has at least one dot in it), but * Microsoft mail client software gets this wrong, so let trusted * (local) clients get it wrong. */ if (!trusted && strchr(him, '.') == nil || strcmp(him, "localhost.localdomain") == 0 || strcmp(him, "localhost.example.com") == 0) goto Liarliar; /* * similarly, if the claimed domain is not an address-literal, * require at least one letter, which there will be in * at least the last component (e.g., .com, .net) if it's real. * this rejects non-address-literal IP addresses, * among other bogosities. */ if (!trusted && him[0] != '[') { char *p; for (p = him; *p != '\0'; p++) if (isascii(*p) && isalpha(*p)) break; if (*p == '\0') goto Liarliar; } if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil) him = nci->rsys; if(Dflag) sleep(15*1000); reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him); if (extended) { if(tlscert != nil) reply("250-STARTTLS\r\n"); if (passwordinclear) reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n"); else reply("250 AUTH CRAM-MD5\r\n"); }}voidsender(String *path){ String *s; static char *lastsender; if(rejectcheck()) return; if (authenticate && !authenticated) { rejectcount++; reply("530 Authentication required\r\n"); return; } if(him == 0 || *him == 0){ rejectcount++; reply("503 Start by saying HELO, please.\r\n", s_to_c(path)); return; } /* don't add the domain onto black holes or we will loop */ if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){ s = s_new(); s_append(s, him); s_append(s, "!"); s_append(s, s_to_c(path)); s_terminate(s); s_free(path); path = s; } if(shellchars(s_to_c(path))){ rejectcount++; reply("503 Bad character in sender address %s.\r\n", s_to_c(path)); return; } /* * if the last sender address resulted in a rejection because the sending * domain didn't exist and this sender has the same domain, reject immediately. */ if(lastsender){ if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){ filterstate = REFUSED; rejectcount++; reply("554 Sender domain must exist: %s\r\n", s_to_c(path)); return; } free(lastsender); /* different sender domain */ lastsender = 0; } /* * see if this ip address, domain name, user name or account is blocked */ filterstate = blocked(path); logged = 0; listadd(&senders, path); reply("250 sender is %s\r\n", s_to_c(path));}enum { Rcpt, Domain, Ntoks };typedef struct Sender Sender;struct Sender { Sender *next; char *rcpt; char *domain;};static Sender *sendlist, *sendlast;static uchar rsysip[IPaddrlen];static intrdsenders(void){ int lnlen, nf, ok = 1; char *line, *senderfile; char *toks[Ntoks]; Biobuf *sf; Sender *snd; static int beenhere = 0; if (beenhere) return 1; beenhere = 1; fmtinstall('I', eipfmt); parseip(rsysip, nci->rsys); /* * we're sticking with a system-wide sender list because * per-user lists would require fully resolving recipient * addresses to determine which users they correspond to * (barring syntactic conventions). */ senderfile = smprint("%s/senders", UPASLIB); sf = Bopen(senderfile, OREAD); free(senderfile); if (sf == nil) return 1; while ((line = Brdline(sf, '\n')) != nil) { if (line[0] == '#' || line[0] == '\n') continue; lnlen = Blinelen(sf); line[lnlen-1] = '\0'; /* clobber newline */ nf = tokenize(line, toks, nelem(toks)); if (nf != nelem(toks)) continue; /* malformed line */ snd = malloc(sizeof *snd); if (snd == nil) sysfatal("out of memory: %r"); memset(snd, 0, sizeof *snd); snd->next = nil; if (sendlast == nil) sendlist = snd; else sendlast->next = snd; sendlast = snd; snd->rcpt = strdup(toks[Rcpt]); snd->domain = strdup(toks[Domain]); } Bterm(sf); return ok;}/* * read (recipient, sender's DNS) pairs from /mail/lib/senders. * Only allow mail to recipient from any of sender's IPs. * A recipient not mentioned in the file is always permitted. */static intsenderok(char *rcpt){ int mentioned = 0, matched = 0; uchar dnsip[IPaddrlen]; Sender *snd; Ndbtuple *nt, *next, *first; rdsenders(); for (snd = sendlist; snd != nil; snd = snd->next) { if (strcmp(rcpt, snd->rcpt) != 0) continue; /* * see if this domain's ips match nci->rsys. * if not, perhaps a later entry's domain will. */ mentioned = 1; if (parseip(dnsip, snd->domain) != -1 && memcmp(rsysip, dnsip, IPaddrlen) == 0) return 1; /* * NB: nt->line links form a circular list(!). * we need to make one complete pass over it to free it all. */ first = nt = dnsquery(nci->root, snd->domain, "ip"); if (first == nil) continue; do { if (strcmp(nt->attr, "ip") == 0 && parseip(dnsip, nt->val) != -1 && memcmp(rsysip, dnsip, IPaddrlen) == 0) matched = 1; next = nt->line; free(nt); nt = next; } while (nt != first); } if (matched) return 1; else return !mentioned;}voidreceiver(String *path){ char *sender, *rcpt; if(rejectcheck()) return; if(him == 0 || *him == 0){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -