/* File: smtp.c Copyright (C) 1999,2000,2003,2004 by Wolfgang Zekoll This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This source is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smtp.h" #include "acp.h" #include "ip-lib.h" #include "lib.h" /* * I/O functions - taken from ftp.proxy */ int getc_fd(smtp_t *x, int fd, int nooptime) { int c; bio_t *bio; if (fd == 0) bio = &x->cbuf; else if (fd == x->sin) bio = &x->sbuf; else { syslog(LOG_NOTICE, "-PROXY: internal bio/fd error"); exit (1); } if (bio->here >= bio->len) { int rc, max, bytes; struct timeval tov; fd_set available, fdset; bio->len = bio->here = 0; FD_ZERO(&fdset); FD_SET(fd, &fdset); max = fd; bytes = 0; while (1) { available = fdset; tov.tv_sec = x->config->timeout; tov.tv_usec = 0; if (debug >= 2) fprintf (stderr, "select max= %d\n", max); rc = select(max + 1, &available, (fd_set *) NULL, (fd_set *) NULL, &tov); if (rc < 0) { syslog(LOG_NOTICE, "-PROXY: select() error: %s", strerror(errno)); break; } else if (rc == 0) { syslog(LOG_NOTICE, "-ERR: connection timed out: client= %s, server= %s", x->client, x->servername); return (-1); } if (FD_ISSET(fd, &available)) { if ((bytes = read(fd, bio->buffer, sizeof(bio->buffer) - 2)) <= 0) { if (debug != 0) { if (bytes == 0) fprintf (stderr, "received zero bytes on fd %d\n", fd); else fprintf (stderr, "received %d bytes on fd %d, errno= %d, error= %s\n", bytes, fd, errno, strerror(errno)); } return (-1); } break; } } bio->len = bytes; bio->here = 0; } if (bio->here >= bio->len) return (-1); c = (unsigned char) bio->buffer[bio->here++]; return (c); } char *readline_fd(smtp_t *x, int fd, char *line, int size, int nooptime) { int c, k; *line = 0; size = size - 2; c = getc_fd(x, fd, nooptime); if (c < 0) return (NULL); else if (c == 1) { strcpy(line, "\001"); return (line); } k = 0; while (c > 0 && c != '\n' && c != 0) { if (k < size) line[k++] = c; c = getc_fd(x, fd, nooptime); } line[k] = 0; noctrl(line); return (line); } char *cfgets(smtp_t *x, char *line, int size, int nooptime) { char *p; *line = 0; if ((p = readline_fd(x, 0, line, size, nooptime)) == NULL) return (NULL); else if (debug != 0) fprintf (stderr, "CLI >>>: %s\n", p); return (line); } int cfputs(smtp_t *x, char *line) { if (debug) fprintf (stderr, ">>> CLI: %s\n", line); /* * Something to do: handler "\r\n" the same way as for the * server. */ write(1, line, strlen(line)); write(1, "\r\n", 2); return (0); } char *sfgets(smtp_t *x, char *line, int size) { char *p; *line = 0; if ((p = readline_fd(x, x->sin, line, size, 0)) == NULL) return (NULL); else if (debug != 0) fprintf (stderr, "SVR >>>: %s\n", p); return (line); } int sfputs(smtp_t *x, char *format, ...) { char buffer[4096]; /* Puffer auf 4k gesetzt, wegen langen Zeilen */ va_list ap; va_start(ap, format); vsnprintf (buffer, sizeof(buffer) - 10, format, ap); va_end(ap); if (debug) fprintf (stderr, ">>> SVR: %s\n", buffer); strcat(buffer, "\r\n"); write(x->sout, buffer, strlen(buffer)); return (0); } int sfputc(smtp_t *x, char *command, char *parameter, char *line, int size, char **here) { int rc; char *p, buffer[300]; if (command != NULL && *command != 0) { if (parameter != NULL && *parameter != 0) snprintf (buffer, sizeof(buffer) - 2, "%s %s", command, skip_ws(parameter)); else copy_string(buffer, command, sizeof(buffer)); sfputs(x, "%s", buffer); } if (sfgets(x, line, size) == NULL) { if (debug != 0) fprintf (stderr, "server disappered in sfputc(), pos #1\n"); return (-1); } rc = strtol(line, &p, 10); p = skip_ws(p); if (*p == '-') { cfputs(x, line); while (1) { if (sfgets(x, line, size) == NULL) { if (debug != 2) fprintf (stderr, "server disappered in sfputc(), pos #2\n"); return (-1); } p = noctrl(line); if (*p == ' ') /* weiter */ ; else if (atoi(line) == rc) { if (strlen(line) == 3) break; else if (strlen(line) > 3 && line[3] == ' ') break; } cfputs(x, line); } } p = skip_ws(p); if (here != NULL) *here = p; return (rc); } int reset_connection(smtp_t *x) { x->state = WAITING; x->nrcpt = 0; x->rcpt.here = 0; /* *x->rcpt.list = 0; */ *x->sender = 0; x->nrcpt = 0; *x->jobid = 0; *x->msgid = 0; x->size = 0; return (0); } char *get_emailadr(char *envelope, char *email, int size) { char *p, *r; *email = 0; /* * Sonderfall: Es ist moeglich, dass die envelope Adresse `<>' * ist. In diesem liefern wir sie unveraendert zurueck. Das * beisst sich nicht mit Adressen der Form `<<>>', da geschachtelte * spitze Klammern nicht unterstuetzt werden. */ if (strcmp(envelope, "<>") == 0) { strcpy(email, "<>"); return (email); } p = envelope; if (*p != '<') return (email); p++; if ((r = strchr(p, '>')) == NULL) return (email); else if (r[1] != 0) return (email); get_quoted(&p, '>', email, size); return (email); } int check_emailadr(char *emailadr) { char *domain; if (strcmp(emailadr, "<>") == 0) return (1); else if (*emailadr == 0) return (0); /* else if (*emailadr == '@') * return (0); */ else if ((domain = strchr(emailadr, '@')) == NULL) return (0); /* else if (domain[1] == '0') * return (0); */ else if (strchr(&domain[1], '@') != NULL) return (0); else if (strchr(emailadr, '!') != NULL /* || strchr(emailadr, '%') != NULL */) return (0); else if (strchr(emailadr, ':') != NULL || strchr(emailadr, ',') != NULL) return (0); return (1); } int search_allowlist(char *emailadr, char *list) { char *p, *domain, pattern[200]; if (strcmp(emailadr, "<>") == 0) return (1); domain = strchr(emailadr, '@'); if (domain == NULL || *domain == 0) { /* * Das kann eigentlich nicht passieren, die E-Mail Adresse * wurde schon auf genau ein @-Zeichen getestet. */ return (0); } else if (list == NULL || *list == 0) { /* * Kann eigentlich auch nicht vorkommen. */ return (0); } p = list; while ((p = skip_ws(p)), *get_quoted(&p, ',', pattern, sizeof(pattern)) != 0) { noctrl(pattern); if (*pattern == '@' && strpcmp(domain, pattern) == 0) return (1); else if (strpcmp(emailadr, pattern) == 0) return (1); } return (0); } int addrcpt(smtp_t *x, char *rcpt) { int len; char adr[200]; /* * Remove angle brackets from e-mail address ... */ copy_string(adr, &rcpt[1], sizeof(adr)); len = strlen(adr); if (len > 0) adr[len-1] = 0; /* * ... and add it to the list. */ len = strlen(rcpt = adr); if ((x->rcpt.here + len + 10) >= x->rcpt.max) { x->rcpt.max += 1024 + len; x->rcpt.list = reallocate(x->rcpt.list, x->rcpt.max); } if (x->rcpt.here > 0) x->rcpt.list[x->rcpt.here++] = ' '; strcpy(&x->rcpt.list[x->rcpt.here], rcpt); x->rcpt.here += len; return (0); } /* * spooldata() - liest entweder vom client (bei "fp == NULL") * oder aus der angegebenen Datei. Das Ausgabeziel der Daten * ist in beiden Faellen der SMTP-Server. */ int spooldata(smtp_t *x, FILE *fp) { int lineno, isheader; char *p, line[2048]; lineno = 0; isheader = 1; while (1) { if (fp != NULL) { if (fgets(line, sizeof(line), fp) == NULL) { /* * Provide the end-of-data line and jump. */ strcpy(line, "."); goto end; } } else { if (cfgets(x, line, sizeof(line), x->config->timeout) == NULL) { syslog(LOG_NOTICE, "-CLIENT: client terminated while sending data"); return (-1); } } noctrl(line); /* * Insert Received header line. */ if (lineno == 0) { if (x->config->droppath != 2) { char buffer[1024]; snprintf (buffer, sizeof(buffer) - 2, "Received: from %s (%s) by %s", x->client, x->ipnum, x->hostname); sfputs(x, "%s", buffer); } } /* * If we are in the mail header we (1) try to read the * mail's message-id and (2) remove the recevied-path * from the header if removal is configured. */ if (isheader == 1) { if (*line == 0) { isheader = 0; if (*x->msgid == 0) { unsigned long now; now = time(NULL); snprintf (x->msgid, sizeof(x->msgid) - 2, "%lu.%d.%d", now, getpid(), x->mailcount); syslog(LOG_NOTICE, "no message id, assuming %s, client= %s", x->msgid, x->client); } } else { char *p, word[80]; p = line; get_word(&p, word, sizeof(word)); strlwr(word); if (strcmp(word, "message-id:") == 0) { if (*x->msgid != 0) { syslog(LOG_NOTICE, "duplicate message id, client= %s", x->client); continue; } else { if ((p = strchr(p, '<')) != NULL) { p++; get_quoted(&p, '>', x->msgid, sizeof(x->msgid)); } } } else if (strcmp(word, "received:") == 0) { if (x->config->droppath != 0) continue; } } } /* * Send current line to SMTP server. Notice: If we are * reading from a file we have to provide an additional * dot `.' if the line starts with a dot. */ p = line; if (fp != NULL) { if (*line == '.') { char buffer[2050]; snprintf (p = buffer, sizeof(buffer), ".%s", line); } } end: if (sfputs(x, "%s", p) != 0) { syslog(LOG_NOTICE, "-SERVER: server terminated while receiving data"); return (-1); } /* * Did we reach the end of the mail? */ if (line[0] == '.' && line[1] == 0) break; x->size += strlen(p) + 2; lineno++; } return (0); } int proxy_request(config_t *config) { int rc; char *p, command[10], word[200], line[2048]; smtp_t *x; x = allocate(sizeof(smtp_t)); get_client_info(0, x->ipnum, x->client); x->mailcount = 0; syslog(LOG_NOTICE, "connected to client: %s", x->ipnum); if (*config->clientdir != 0) { char logfile[200]; unsigned long now; struct stat sbuf; now = time(NULL); snprintf (logfile, sizeof(logfile) - 2, "%s/%s", config->clientdir, x->ipnum); if (stat(logfile, &sbuf) != 0 || (now - sbuf.st_mtime) >= config->accepttime) { printf ("421 service unavailable - authenticate with POP3 first\r\n"); syslog(LOG_NOTICE, "-ERR: client not permitted: %s", x->ipnum); goto end; } } if (*config->server != 0) { unsigned int port; char server[200]; copy_string(server, config->server, sizeof(server)); port = get_port(server, 25); if ((x->sin = openip(server, port)) < 0) { printf ("451 Service unavailable\r\n"); syslog(LOG_NOTICE, "-ERR: can't connect to server: %s:%u, %s", server, port, strerror(errno)); exit (1); } x->sout = x->sin; copy_string(x->servername, config->server, sizeof(x->servername)); } else if (config->argv != NULL) { int pid, pin[2], pout[2]; if (pipe(pin) != 0 || pipe(pout) != 0) { printf ("451 Service unavailable\r\n"); syslog(LOG_NOTICE, "-PROXY: can't pipe(): %m"); exit (-1); } else if ((pid = fork()) < 0) { printf ("451 Service unavailable\r\n"); syslog(LOG_NOTICE, "-PROXY: can't fork(): %m"); exit (-1); } else if (pid == 0) { dup2(pin[1], 1); close (pin[0]); dup2(pout[0], 0); close(pout[1]); close (2); execvp(config->argv[0], config->argv); printf ("451 Service unavailable\r\n"); syslog(LOG_NOTICE, "-PROXY: can't execute: %s: %m", config->argv[0]); exit (1); } else { x->sin = pin[0]; close(pin[1]); x->sout = pout[1]; close(pout[0]); copy_string(x->servername, config->argv[0], sizeof(x->servername)); } } else { printf ("451 Service unavailable\r\n"); syslog(LOG_NOTICE, "-PROXY: no server specified"); exit (1); } syslog(LOG_NOTICE, "connected to server: %s", x->servername); /* Konfiguration uebernehmen */ x->config = config; /* Hostnamen holen */ gethostname(word, sizeof(word)); getdomainname(line, sizeof(line)); snprintf (x->hostname, sizeof(x->hostname) - 2, word, line); /* Greeting Message vom Sendmail Server lesen, und an * Client schicken. */ sfgets(x, line, sizeof(line)); while (line[3] != ' ') { if (sfgets(x, line, sizeof(line)) == NULL) { syslog(LOG_NOTICE, "-SERVER: lost server while reading server greeting"); exit (1); } } cfputs(x, line); /* Wir stellen uns beim lokalen Sendmail Server vor. Die * EHLO-replys werden 'verschluckt' und durch einen eigenen * ersetzt. */ sfputs(x, "EHLO localhost"); while (sfgets(x, line, sizeof(line)) != NULL) { if (line[3] == ' ') break; } rc = atol(line); if (rc != 250) { syslog(LOG_NOTICE, "-SERVER: server HELO: status is not 250: %s", line); cfputs(x, "421 service unavailable"); exit (1); } /* * ** S M T P M A I N L O O P */ x->state = WAITING; while (1) { rc = 0; /* Server response code loeschen */ /* * Naechstes Kommando vom Client holen. */ if (cfgets(x, line, sizeof(line), 0) == NULL) { syslog(LOG_NOTICE, "-CLIENT: client closed connection"); break; } /* Kommando isolieren */ p = noctrl(line); get_word(&p, command, sizeof(command)); strupr(command); p = skip_ws(p); /* QUIT ist immer moeglich. */ if (strcmp(command, "QUIT") == 0) { rc = sfputc(x, "QUIT", "", line, sizeof(line), NULL); cfputs(x, line); x->state = SEND_QUIT; break; } /* HELP */ else if (strcmp(command, "HELP") == 0) { cfputs(x, "503 no help available"); } /* NOOP */ else if (strcmp(command, "NOOP") == 0) { rc = sfputc(x, "NOOP", "", line, sizeof(line), NULL); cfputs(x, line); } /* RSET */ else if (strcmp(command, "RSET") == 0) { rc = sfputc(x, "RSET", "", line, sizeof(line), NULL); cfputs(x, line); reset_connection(x); syslog(LOG_NOTICE, "RSET command, client= %s", x->client); } /* ETRN */ else if (strcmp(command, "ETRN") == 0) { if (x->config->etrn == 0) { cfputs(x, "500 unrecognized command"); syslog(LOG_NOTICE, "-ERR: ETRN request rejected: client= %s", x->client); } else { if (*get_word(&p, word, sizeof(word)) == 0) cfputs(x, "500 ETRN needs parameter"); else { rc = sfputc(x, "ETRN", word, line, sizeof(line), NULL); cfputs(x, line); if (rc != 250) syslog(LOG_NOTICE, "-ERR: ETRN rejected by server, client= %s", x->client); } } } /* HELO und EHLO sind auch immer verfuegbar, aber nur * einmal. */ else if (strcmp(command, "HELO") == 0 || strcmp(command, "EHLO") == 0) { if (x->helloseen != 0) cfputs(x, "503 duplicate HELO/EHLO"); else if (*get_word(&p, word, sizeof(word)) == 0) { snprintf (line, sizeof(line) - 2, "501 %s requires domain name", command); cfputs(x, line); } else { if (strcmp(command, "HELO") == 0) { snprintf (line, sizeof(line) - 2, "250 SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum); cfputs(x, line); } else { snprintf (line, sizeof(line) - 2, "250-SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum); cfputs(x, line); cfputs(x, "250-8BITMIME"); if (x->config->etrn != 0) cfputs(x, "250-ETRN"); cfputs(x, "250 HELP"); } x->helloseen = 1; } } /* MAIL, SEND, SOML, SAML * * Laut RFC 821 kann das MAIL Kommando jederzeit abgesetzt * werden, es macht dabei einen impliziten SMTP-Reset. Der * real existierende Sendmail will davon aber nichts wissen. */ else if (strcmp(command, "MAIL") == 0 || strcmp(command, "SEND") == 0 || strcmp(command, "SOML") == 0 || strcmp(command, "SAML") == 0) { get_quoted(&p, ':', word, sizeof(word)); if (strcasecmp(word, "FROM") != 0) cfputs(x, "500 syntax error"); else if (*x->sender != 0) cfputs(x, "503 sender already specified"); else { int allowed; char sender[200], emailadr[200]; p = skip_ws(p); get_word(&p, sender, sizeof(sender)); strlwr(sender); /* * Wir machen ein paar grundsaetzliche Tests mit * der Absenderadresse: * * - Ist die Adresse von spitzen Klammern * umgeben? * ... */ allowed = 1; get_emailadr(sender, emailadr, sizeof(emailadr)); if (*emailadr == 0) allowed = 0; /* * ... * - Enthaelt die Adresse mindestens ein @-Zeichen? * - Enthaelt die Adresse genau ein @-Zeichen? * - Ist in der Adresse kein !- und kein %-Zeichen * enthalten. * ... */ else if (check_emailadr(emailadr) == 0) allowed = 0; /* * ... * - Schliesslich wird ggf. noch getestet, * ob die Absenderadresse auch auf der * allow-Liste steht. * * Mit den Empfaengeradressen werden die gleichen Tests * durchgefuehrt. */ else if ((p = x->config->senderlist) == NULL || *p == 0) allowed = 1; /* kein Adresstest */ else allowed = search_allowlist(emailadr, x->config->senderlist); if (allowed == 0) { char line[300]; snprintf (line, sizeof(line) - 2, "550 not allowed: %s", sender); cfputs(x, line); syslog(LOG_NOTICE, "-FROM: sender not allowed: %s, client= %s", sender, x->client); } else { char smtpcmd[20]; /* * Der Absender ist soweit ok. Was wir jetzt ggf. machen muessen * ist die Absenderdomain neu schreiben. */ copy_string(x->origsender, sender, sizeof(x->origsender)); if (*x->config->senderdomain != 0) { char *p, newadr[80]; if ((p = strchr(sender, '@')) != NULL) *p = 0; p = sender; if (*p == '<') /* Sollte eigentlich immer der Fall sein */ p++; snprintf (newadr, sizeof(newadr) - 2, "<%s@%s>", p, x->config->senderdomain); copy_string(sender, newadr, sizeof(sender)); } snprintf (smtpcmd, sizeof(smtpcmd) - 2, "%s FROM:", command); rc = sfputc(x, smtpcmd, sender, line, sizeof(line), NULL); cfputs(x, line); if (rc == 250) { copy_string(x->sender, sender, sizeof(sender)); x->state = MAIL_SEEN; syslog(LOG_NOTICE, "sender: %s", x->sender); } else { syslog(LOG_NOTICE, "-FROM: sender rejected: %s, client= %s, resp= %s", sender, x->client, line); } } } } /* RCPT */ else if (strcmp(command, "RCPT") == 0) { get_quoted(&p, ':', word, sizeof(word)); if (strcasecmp(word, "TO") != 0) cfputs(x, "500 syntax error"); else if (x->state != MAIL_SEEN && x->state != RCPT_SEEN) cfputs(x, "503 specify sender first"); else { int allowed; char rcpt[200], emailadr[200]; p = skip_ws(p); get_word(&p, rcpt, sizeof(rcpt)); strlwr(rcpt); get_emailadr(rcpt, emailadr, sizeof(emailadr)); if (*emailadr == 0) allowed = 0; else if (check_emailadr(emailadr) == 0) allowed = 0; else if ((p = x->config->rcptlist) == NULL || *p == 0) allowed = 1; else allowed = search_allowlist(emailadr, x->config->rcptlist); if (allowed == 0) { char line[300]; snprintf (line, sizeof(line) - 2, "550 no such user: %s", rcpt); cfputs(x, line); syslog(LOG_NOTICE, "-RCPT: recipient not allowed: %s, client= %s", rcpt, x->client); } else { rc = sfputc(x, "RCPT TO:", rcpt, line, sizeof(line), NULL); cfputs(x, line); if (rc == 250 || rc == 251) { addrcpt(x, rcpt); x->nrcpt++; x->state = RCPT_SEEN; syslog(LOG_NOTICE, "rcpt: %s", rcpt); } else { syslog(LOG_NOTICE, "-RCPT: recipient rejected: %s, client= %s, resp= %s", rcpt, x->client, line); } } } } /* DATA */ else if (strcmp(command, "DATA") == 0) { x->mailcount++; if (x->state != RCPT_SEEN) cfputs(x, "503 specify recipients first"); else { FILE *fp; if (*x->config->acp != 0) { run_acp(x, line, sizeof(line)); rc = atoi(line); if (rc == 250 || rc == 354) /* ok for us, continue processing */ ; else if (rc >= 400 && rc < 600) { syslog(LOG_NOTICE, "-DATA: acp status: %s", line); cfputs(x, line); sfputc(x, "RSET", "", line, sizeof(line), NULL); reset_connection(x); if (rc == 421) break; continue; } else { syslog(LOG_NOTICE, "-PROXY: acp status error: %s", line); cfputs(x, "421 server error"); break; } } if (*x->config->ccp == 0) { rc = sfputc(x, "DATA", "", line, sizeof(line), NULL); cfputs(x, line); } else { /* * Ok, in case that we have a command control * program (a ccp) we have interesting things * to do. * * First we create a temporary spool file to * store the mail data from the client ... */ gettmpfile(x->spoolfile, sizeof(x->spoolfile)); cfputs(x, "354 send data"); if ((rc = receivemail(x)) < 0) break; else if (rc != 0) copy_string(line, "421 server error", sizeof(line)); else { /* * ... and then we call the ccp and look at * it's SMTP compatible return code. */ run_ccp(x, line, sizeof(line)); } rc = atoi(line); if (rc == 250 || rc == 354) /* ok for us, let's pass the e-mail to the server */ ; else if (rc >= 400 && rc < 600) { /* * Somethings wrong with the e-mail. Log the event, * pass the message to the client, reset the connection * to the server and continue. */ syslog(LOG_NOTICE, "-DATA: ccp status: %s", line); cfputs(x, line); sfputc(x, "RSET", "", line, sizeof(line), NULL); reset_connection(x); if (rc == 421) break; /* * ... and stop processing this e-mail */ continue; } else { /* * Something is wrong with the ccp. We terminate * with a 421 response code. */ syslog(LOG_NOTICE, "-PROXY: ccp status error: %s", line); cfputs(x, "421 server error"); break; } /* * If we made it here we have to send the DATA * command to the server. Response checking is * below. */ rc = sfputc(x, "DATA", "", line, sizeof(line), NULL); } if (rc == 354) { fp = NULL; if (*x->spoolfile != 0) { if ((fp = fopen(x->spoolfile, "r")) == NULL) { cfputs(x, "421 server error"); syslog(LOG_NOTICE, "-PROXY: can't open spoolfile: %s, error= %s", x->spoolfile, strerror(errno)); exit (1); } } if ((rc = spooldata(x, fp)) == 0) { sfgets(x, line, sizeof(line)); cfputs(x, line); rc = atoi(line); if (rc == 250) { p = line; get_word(&p, word, sizeof(word)); get_word(&p, x->jobid, sizeof(x->jobid)); } } if (fp != NULL) { fclose (fp); unlink(x->spoolfile); } } syslog(LOG_NOTICE, "+MAIL: server= %s, client= %s, sender= %s, nrcpt= %d, size= %ld, message-id= <%s>, resp= %s", x->servername, x->client, x->sender, x->nrcpt, x->size, x->msgid, line); reset_connection(x); x->state = WAITING; } } /* * Other commands are not supported. */ else { cfputs(x, "500 unrecognized command"); syslog(LOG_NOTICE, "-SMTP: received unknown command: %s", command); } /* * Final status code checking - this assumes that the `rc' * variable holds the current server status response code. */ if (rc == 421) { syslog(LOG_NOTICE, "-ERR: sendmail returned 421, state= %d, command= %s", x->state, command); break; } else if (rc == -1) { syslog(LOG_NOTICE, "terminating (sendmail terminated)"); x->state = NO_SENDMAIL; break; } } if (x->state != SEND_QUIT && x->state != NO_SENDMAIL) { sfputs(x, "QUIT", ""); sfgets(x, line, sizeof(line)); rc = atoi(line); } end: syslog(LOG_NOTICE, "+OK: client %s disconnecting, %d mails", x->client, x->mailcount); return (0); }