void initstatus() { if(lseek(status, 0, SEEK_SET) != 0) { fprintf(Log, "%s: Seek error on status file\n", thedate()); return; } if(read(status, Status, 16*16*sizeof(STATUS)) != 16*16*sizeof(STATUS)) { fprintf(Log, "%s: Read error on status file\n", thedate()); return; } }
void quit(void) { reply("221 2.0.0 Successful termination\r\n"); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# %d sent 221 reply to QUIT %s\n", getpid(), thedate()); } close(0); exits(0); }
void checkpoint_status() { int h, i, k, offset; offset = 0; for(h = 0; h < 16; h++) { for(i = 0; i < 16; i++) { if(Status[h][i].changed) { if(lseek(status, offset, SEEK_SET) != offset) { fprintf(Log, "%s: Seek error on status file\n", thedate()); } else { if(write(status, &Status[h][i], sizeof(STATUS)) != sizeof(STATUS)) { fprintf(Log, "%s: Write error on status file\n", thedate()); } } Status[h][i].changed = 0; for(k = 0; k < MAXMON; k++) { if(Monitor[k].inuse && Monitor[k].house == h && Monitor[k].unit == i) { /* * Arrange to catch SIGPIPE in case client has gone away. */ extern int client; extern void clientgone(); void (*prev)(); client = k; prev = signal(SIGPIPE, clientgone); printstatus(Monitor[k].user, &Status[h][i]); fflush(Monitor[k].user); signal(SIGPIPE, prev); } } } offset += sizeof(STATUS); } } }
extern int default_from(message *mp) { char *cp, *lp; cp = getenv("upasname"); lp = getlog(); if(lp == nil) return -1; if(cp && *cp) s_append(mp->sender, cp); else s_append(mp->sender, lp); s_append(mp->date, thedate()); return 0; }
void main(int argc, char **argv) { int i, ok, rcvrs; char *addr, *rv, *trv, *host, *domain; char **errs; char hellodomain[256]; String *from, *fromm, *sender; alarmscale = 60*1000; /* minutes */ quotefmtinstall(); fmtinstall('[', encodefmt); errs = malloc(argc*sizeof(char*)); reply = s_new(); host = 0; ARGBEGIN{ case 'a': tryauth = 1; if(trysecure == 0) trysecure = 1; break; case 'A': /* autistic: won't talk to us until we talk (Verizon) */ autistic = 1; break; case 'b': if (bustedmx >= Maxbustedmx) sysfatal("more than %d busted mxs given", Maxbustedmx); bustedmxs[bustedmx++] = EARGF(usage()); break; case 'd': debug = 1; break; case 'f': filter = 1; break; case 'g': gdomain = EARGF(usage()); break; case 'h': host = EARGF(usage()); break; case 'i': insecure = 1; break; case 'p': alarmscale = 10*1000; /* tens of seconds */ ping = 1; break; case 's': if(trysecure == 0) trysecure = 1; break; case 't': trysecure = 2; break; case 'u': user = EARGF(usage()); break; default: usage(); break; }ARGEND; Binit(&berr, 2, OWRITE); Binit(&bfile, 0, OREAD); /* * get domain and add to host name */ if(*argv && **argv=='.') { domain = *argv; argv++; argc--; } else domain = domainname_read(); if(host == 0) host = sysname_read(); strcpy(hostdomain, domainify(host, domain)); strcpy(hellodomain, domainify(sysname_read(), domain)); /* * get destination address */ if(*argv == 0) usage(); addr = *argv++; argc--; farend = addr; /* * get sender's machine. * get sender in internet style. domainify if necessary. */ if(*argv == 0) usage(); sender = unescapespecial(s_copy(*argv++)); argc--; fromm = s_clone(sender); rv = strrchr(s_to_c(fromm), '!'); if(rv) *rv = 0; else *s_to_c(fromm) = 0; from = bangtoat(s_to_c(sender)); /* * send the mail */ if(filter){ Binit(&bout, 1, OWRITE); rv = data(from, &bfile); if(rv != 0) goto error; exits(0); } /* mxdial uses its own timeout handler */ if((rv = connect(addr)) != 0) exits(rv); /* 10 minutes to get through the initial handshake */ atnotify(timeout, 1); alarm(10*alarmscale); if((rv = hello(hellodomain, 0)) != 0) goto error; alarm(10*alarmscale); if((rv = mailfrom(s_to_c(from))) != 0) goto error; ok = 0; rcvrs = 0; /* if any rcvrs are ok, we try to send the message */ for(i = 0; i < argc; i++){ if((trv = rcptto(argv[i])) != 0){ /* remember worst error */ if(rv != Giveup) rv = trv; errs[rcvrs] = strdup(s_to_c(reply)); removenewline(errs[rcvrs]); } else { ok++; errs[rcvrs] = 0; } rcvrs++; } /* if no ok rcvrs or worst error is retry, give up */ if(ok == 0 || rv == Retry) goto error; if(ping){ quit(0); exits(0); } rv = data(from, &bfile); if(rv != 0) goto error; quit(0); if(rcvrs == ok) exits(0); /* * here when some but not all rcvrs failed */ fprint(2, "%s connect to %s:\n", thedate(), addr); for(i = 0; i < rcvrs; i++){ if(errs[i]){ syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]); fprint(2, " mail to %s failed: %s", argv[i], errs[i]); } } exits(Giveup); /* * here when all rcvrs failed */ error: removenewline(s_to_c(reply)); syslog(0, "smtp.fail", "%s to %s failed: %s", ping ? "ping" : "delivery", addr, s_to_c(reply)); fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply)); if(!filter) quit(rv); exits(rv); }
void data(void) { int status, nbytes; char *cp, *ep; char errx[ERRMAX]; Link *l; String *cmd, *err; if(rejectcheck()) return; if(senders.last == 0){ reply("503 2.5.2 Data without MAIL FROM:\r\n"); rejectcount++; return; } if(rcvers.last == 0){ reply("503 2.5.2 Data without RCPT TO:\r\n"); rejectcount++; return; } if(!trusted && sendermxcheck()){ rerrstr(errx, sizeof errx); if(strncmp(errx, "rejected:", 9) == 0) reply("554 5.7.1 %s\r\n", errx); else reply("450 4.7.0 %s\r\n", errx); for(l=rcvers.first; l; l=l->next) syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(l->p), errx); rejectcount++; return; } cmd = startcmd(); if(cmd == 0) return; reply("354 Input message; end with <CRLF>.<CRLF>\r\n"); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# sent 354; accepting DATA %s\n", thedate()); } /* * allow 145 more minutes to move the data */ alarm(145*60*1000); status = pipemsg(&nbytes); /* * read any error messages */ err = s_new(); if (debug) { stamp(); fprint(2, "waiting for upas/send to close stderr\n"); } while(s_read_line(pp->std[2]->fp, err)) ; alarm(0); atnotify(catchalarm, 0); if (debug) { stamp(); fprint(2, "waiting for upas/send to exit\n"); } status |= proc_wait(pp); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# %d upas/send status %#ux at %s\n", getpid(), status, thedate()); if(*s_to_c(err)) fprint(2, "# %d error %s\n", getpid(), s_to_c(err)); } /* * if process terminated abnormally, send back error message */ if(status){ int code; char *ecode; if(strstr(s_to_c(err), "mail refused")){ syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), firstline(s_to_c(err))); code = 554; ecode = "5.0.0"; } else { syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), piperror? "error during pipemsg: ": "", piperror? piperror: "", piperror? "; ": "", pp->waitmsg->msg, firstline(s_to_c(err))); code = 450; ecode = "4.0.0"; } for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){ *ep++ = 0; reply("%d-%s %s\r\n", code, ecode, cp); } reply("%d %s mail process terminated abnormally\r\n", code, ecode); } else { /* * if a message appeared on stderr, despite good status, * log it. this can happen if rewrite.in contains a bad * r.e., for example. */ if(*s_to_c(err)) syslog(0, "smtpd", "%s returned good status, but said: %s", s_to_c(mailer), s_to_c(err)); if(filterstate == BLOCKED) reply("554 5.7.1 we believe this is spam. " "we don't accept it.\r\n"); else if(filterstate == DELAY) reply("450 4.3.0 There will be a delay in delivery " "of this message.\r\n"); else { reply("250 2.5.0 sent\r\n"); logcall(nbytes); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# %d sent 250 reply %s\n", getpid(), thedate()); } } } proc_free(pp); pp = 0; s_free(cmd); s_free(err); listfree(&senders); listfree(&rcvers); }
void main(int argc, char **argv) { char *netdir; char buf[1024]; netdir = nil; quotefmtinstall(); fmtinstall('I', eipfmt); starttime = time(0); ARGBEGIN{ case 'a': authenticate = 1; break; case 'c': tlscert = EARGF(usage()); break; case 'D': Dflag++; break; case 'd': debug++; break; case 'f': /* disallow relaying */ fflag = 1; break; case 'g': gflag = 1; break; case 'h': /* default domain name */ dom = EARGF(usage()); break; case 'k': /* prohibited ip address */ addbadguy(EARGF(usage())); break; case 'm': /* set mail command */ mailer = mailerpath(EARGF(usage())); break; case 'n': /* log peer ip address */ netdir = EARGF(usage()); break; case 'p': passwordinclear = 1; break; case 'r': rflag = 1; /* verify sender's domain */ break; case 's': /* save blocked messages */ sflag = 1; 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: usage(); }ARGEND; nci = getnetconninfo(netdir, 0); if(nci == nil) sysfatal("can't get remote system's address: %r"); parseip(rsysip, nci->rsys); if(mailer == nil) mailer = mailerpath("send"); if(debug){ snprint(buf, sizeof buf, "%s/smtpdb/%ld", UPASLOG, time(0)); close(2); if (create(buf, OWRITE | OEXCL, 0662) >= 0) { seek(2, 0, 2); fprint(2, "%d smtpd %s\n", getpid(), thedate()); } else debug = 0; } getconf(); if (isbadguy()) exits("banned"); Binit(&bin, 0, OREAD); if (chdir(UPASLOG) < 0) syslog(0, "smtpd", "no %s: %r", UPASLOG); me = sysname_read(); if(dom == 0 || dom[0] == 0) dom = domainname_read(); if(dom == 0 || dom[0] == 0) dom = me; parseinit(); sayhi(); /* allow 45 minutes to parse the header */ atnotify(catchalarm, 1); alarm(45*60*1000); zzparse(); exits(0); }
/* * pipe message to mailer with the following transformations: * - change \r\n into \n. * - add sender's domain to any addrs with no domain * - add a From: if none of From:, Sender:, or Replyto: exists * - add a Received: line */ int pipemsg(int *byteswritten) { int n, nbytes, sawdot, status, nonhdr, bpr; char *cp; Field *f; Link *l; Node *p; String *hdr, *line; pipesig(&status); /* set status to 1 on write to closed pipe */ sawdot = 0; status = 0; /* * add a 'From ' line as envelope */ nbytes = 0; nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n", s_to_c(senders.first->p), thedate()); /* * add our own Received: stamp */ nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him); if(nci->rsys) nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys); nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate()); /* * read first 16k obeying '.' escape. we're assuming * the header will all be there. */ line = s_new(); hdr = s_new(); while(sawdot == 0 && s_len(hdr) < 16*1024){ n = getcrnl(s_reset(line), &bin); /* eof or error ends the message */ if(n <= 0) break; /* a line with only a '.' ends the message */ cp = s_to_c(line); if(n == 2 && *cp == '.' && *(cp+1) == '\n'){ sawdot = 1; break; } s_append(hdr, *cp == '.' ? cp+1 : cp); } /* * parse header */ yyinit(s_to_c(hdr), s_len(hdr)); yyparse(); /* * Look for masquerades. Let Sender: trump From: to allow mailing list * forwarded messages. */ if(fflag) nbytes += forgedheaderwarnings(); /* * add an orginator and/or destination if either is missing */ if(originator == 0){ if(senders.last == nil || senders.last->p == nil) nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him); else nbytes += Bprint(pp->std[0]->fp, "From: %s\n", s_to_c(senders.last->p)); } if(destination == 0){ nbytes += Bprint(pp->std[0]->fp, "To: "); for(l = rcvers.first; l; l = l->next){ if(l != rcvers.first) nbytes += Bprint(pp->std[0]->fp, ", "); nbytes += Bprint(pp->std[0]->fp, "%s", s_to_c(l->p)); } nbytes += Bprint(pp->std[0]->fp, "\n"); } /* * add sender's domain to any domainless addresses * (to avoid forging local addresses) */ cp = s_to_c(hdr); for(f = firstfield; cp != nil && f; f = f->next){ for(p = f->node; cp != 0 && p; p = p->next) { bpr = 0; cp = bprintnode(pp->std[0]->fp, p, &bpr); nbytes += bpr; } if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){ piperror = "write error"; status = 1; } nbytes++; /* for newline */ } if(cp == nil){ piperror = "sender domain"; status = 1; } /* write anything we read following the header */ nonhdr = s_to_c(hdr) + s_len(hdr) - cp; if(status == 0 && Bwrite(pp->std[0]->fp, cp, nonhdr) < 0){ piperror = "write error 2"; status = 1; } nbytes += nonhdr; s_free(hdr); /* * pass rest of message to mailer. take care of '.' * escapes. */ while(sawdot == 0){ n = getcrnl(s_reset(line), &bin); /* eof or error ends the message */ if(n <= 0) break; /* a line with only a '.' ends the message */ cp = s_to_c(line); if(n == 2 && *cp == '.' && *(cp+1) == '\n'){ sawdot = 1; break; } if(cp[0] == '.'){ cp++; n--; } nbytes += n; if(status == 0 && Bwrite(pp->std[0]->fp, cp, n) < 0){ piperror = "write error 3"; status = 1; } } s_free(line); if(sawdot == 0){ /* message did not terminate normally */ snprint(pipbuf, sizeof pipbuf, "network eof: %r"); piperror = pipbuf; if (pp->pid > 0) { syskillpg(pp->pid); /* none can't syskillpg, so try a variant */ sleep(500); syskill(pp->pid); } status = 1; } if(status == 0 && Bflush(pp->std[0]->fp) < 0){ piperror = "write error 4"; status = 1; } if (debug) { stamp(); fprint(2, "at end of message; %s .\n", (sawdot? "saw": "didn't see")); } stream_free(pp->std[0]); pp->std[0] = 0; *byteswritten = nbytes; pipesigoff(); if(status && !piperror) piperror = "write on closed pipe"; return status; }
void clientgone() { fprintf(Log, "%s: Deleting monitor table entry %d, client gone\n", thedate(), client); fclose(Monitor[client].user); Monitor[client].inuse = 0; }