/* * Copyright (c) 1998-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* * sSMTP sendmail -- send messages via smtp to a mailhub for local delivery * or forwarding. This program is used in place of /usr/lib/sendmail, * called by /bin/mail (et all). sSMTP does a selected subset of * sendmail's standard tasks (including exactly one rewriting task), and * explains if you ask it to do something it can't. It then sends * the mail to the mailhub via an smtp connection. Believe it or not, * this is nothing but a filter. You can receive mail with inetd, an * inverse filter and /bin/mail -d. * * October 1998-2003 ArkanoiD * - Heavy modifications for smapd operation * * September 1997 Christoph Lameter * - Fixed up to use more modern C (attempt to fix problems) * - Fixed scores of bugs (I doubt it ever worked before) * - Made it work under Debian/Linux * - Add support for -t option. Limit header to 4K. * * October 1997 Hugo Haas * - Added the reverse aliases process for the From: field * - Send the required headers at the beginning * - Send only one recipient at a time * - Changed the header parsing to avoid a bug due to mailx * * November 1997 Hugo Haas * - Changed the RCPT stuff which was wrong for arguments with "<>" * * December 1997 Hugo Haas * - Changed the MAIL FROM command to be RFC821 compliant (Debian Bug#15690) * - Modified the recordReciepient function: no memory was allocated for * the last recipient * - Added the sending of the recorded recipients (Debian Bug#15690) * The old way to do it was wrong. (Removed the argv=reciepients stuff) * * January 1998 Hugo Haas * - Changed the header parsing because it gobbled pseudo-header lines * (Debian Bug#17240) * - Changed the RewriteDomain option * * January 1998 Hugo Haas * - Changed the configuration parsing (Debian Bug#17470) * - Changed the logging: verbosity reduced * * March 1998 Hugo Haas * - No more adding "To: postmaster" (qmail and sendmail do this) * - Improved "-f", "-F" and "-r" options * * March 1998 Hugo Haas * - Moved the configuration files to /etc/ssmtp * * April 1998 Hugo Haas * - Removed quote in the From: line * * April 1998 Hugo Haas * - Removed awful getDate() method; replaced by get_arpadate() * * April 1998 Hugo Haas * - Now ignoring -R keyword, -N dsn stuff * * April 1998 Hugo Haas * - Made 'Root' option work * - Handled the case when the user has no name (do not send "(null)") */ #include #include #include /* For getpwent. */ #include /* For sockets. */ #include #include #include #include /* For logging. */ #include /* For the timer and signals. */ #include #include #include #include #include #include #include #include #include "ssmtp.h" #include "patchlevel.h" #include "firewall.h" #include "fwfunc.h" #include "firewall2.h" #include "newio.h" #include "ci_milter.h" static char* moduleId ATTR_UNUSED = "$Id: ssmtp.c,v 1.22 2007/09/22 14:35:25 arkenoi Exp $"; #define QTEMPLATE "/tmp/ssmtp.XXXXXXXXXX" #ifndef MAILHUB #define MAILHUB "mailhost" /* A surprisingly usefull default. */ #endif #define PORTNUMBER 25 /* From the Assigned Numbers RFC. */ #if defined(linux) || defined (SOLARIS) extern void initsetproctitle(int,char**); #endif char *Version = VERSION; /* The version of the program. */ char *MailHub = MAILHUB; /* The place to send the mail. */ char HostName[MAXLINE]; /* Our name, unless overridden. */ char fromname[1024] = "null"; struct passwd *Sender = NULL; /* The person sending the mail. */ int Verbose = NO; /* Tell the user what's happening. */ int Toption = NO; /* Was a T option given? */ int LogLevel = /* Tell the log what's happening. */ #ifdef DEBUG 1; #else 0; #endif static int hide_intranet = 0; static int from_intranet = 0; static int permit_routed = 0; static int log_headers = 0; static int dscp = 0; static int tfd; static char **rcpts = NULL; static char *baddir = NULL; static char *mfrom; static char riaddr[512] = ""; char *headers; /* Pointer to beginning of headers */ char *headere; /* End of header */ #define CHUNK 32000 /* Sizeof allocation */ #define ARPADATE_LENGTH 60 static char *qf = NULL; /* Current date in RFC format. */ #include "arpadate.c" char DateString[ARPADATE_LENGTH]; char *fromLine(void); int getOkFromSmtp(int,char *); int getFromSmtp(int,char *); extern int milter_inspect_mail(char*,char*,char*,char*,char*,char*,char***); void flush(void); /* * die -- say something and exit with a non-zero return code. * Save the message on stdin in dead.letter. */ void die(int ex,char *format,...) { va_list ap; (void) fprintf(stderr,"%s: ",proxy_name); va_start(ap,format); (void) vfprintf(stderr,format,ap); (void) vsyslog(LLEV,format,ap); va_end(ap); (void) putc('\n',stderr); flush(); /* Send message to dead.letter */ if (qf) unlink(qf); exit(ex); } /* * putToSmtp -- a printf to an fd, which appends TCP/IP . */ void putToSmtp(int fd, char *format, ...) { va_list args; char line[MAXLINE]; int x; va_start(args, format); (void) vsnprintf(line,MAXLINE-3,format,args); if (LogLevel > 0) { syslog(LLEV,"Sent \"%s\" to smtp port.\n",line); } (void) strlcat(line,"\r\n",MAXLINE); x = strlen(line); if ((sowrite(fd,line,x)!=x)) die(EX_IOERR,"error writing to relay"); proxy_stats.outbytes += x; proxy_update_status(); va_end(args); } void putToQf(int fd, char *format, ...) { va_list args; char line[MAXLINE]; int x; va_start(args, format); (void) vsnprintf(line,MAXLINE-3,format,args); (void) strlcat(line,"\r\n",MAXLINE); x = strlen(line); if ((sowrite(fd,line,x)!=x)) die(EX_IOERR,"error writing to temporary file"); va_end(args); } void rrec(char *re) { char *p,*r,*ptr; if (!*re) return; if ((p=index(re,'<'))) { ptr=p+1; if ((p=index(ptr,'>'))) { r=malloc(p-ptr+1); r[p-ptr]=0; memcpy(r,ptr,p-ptr); addlist(r,&rcpts); free(r); } else die (EX_USAGE,"Syntax error in recipient %s",p); } else addlist(re,&rcpts); } void recordReciepient(char *line) { char *p,*q,*r; q=p=line; do { while (*q==' ' || *q==',') q++; if (!*q) return; p=q; while (*p && *p!=',') p++; if (*p) { r=xmalloc(p-q+1); memcpy(r,q,p-q); r[p-q]=0; rrec(r); free(r); q=++p; } } while (*p); r=xstrdup(q); rrec(r); free(r); } /* ** Supporting libraries -- header insertion. */ static int hasFrom = NO, hasDate = NO; /* * recordRequiredHeaders -- note which ones we've seen. */ void recordRequiredHeaders(char *line) { if (*line == ' ' || *line == '\t') { return; } else if (strncasecmp(line,"From:",5)==0) { hasFrom = YES; } else if (strncasecmp(line,"Date:",5)==0) { hasDate = YES; } if (Toption) { /* Need to figure out reciepients from the e-mail */ if (strncasecmp(line,"To:",3)==0) recordReciepient(line+4); else if (strncasecmp(line,"Bcc:",4)==0) recordReciepient(line+5); else if (strncasecmp(line,"CC:",3)==0) recordReciepient(line+4); } } /* * addRequiredHeaders -- add ones that have been missed. */ void addRequiredHeaders(int fd) { if (hasFrom == NO) { putToQf(fd,"From: %s", fromLine()); } if (hasDate == NO) { putToQf(fd,"Date: %s",DateString); } } /* * addInitialHeaders -- prepend prerequisite timstamp * and actual date lines. */ void addInitialHeaders(int fd) { putToQf(fd,"Received: by %s (ssmtp %s); %s", HostName, Version, DateString); } /* * fromLine -- generate a from line in standard format. Used whenever * we need one in standard format, "Real Name " or "id@site" */ char* fromLine(void) { static char buffer[MAXLINE]; char *phrase; if (strcmp(fromname,"null")) { return fromname; } if ((phrase = strtok(Sender->pw_gecos, ",;")) != NULL) (void) snprintf(buffer,MAXLINE-4,"%s <%s@%s>", phrase, Sender->pw_name, HostName); else (void) snprintf(buffer,MAXLINE-4,"%s@%s", Sender->pw_name, HostName); return buffer; } /* * standardize -- trim off '\n's, double leading dots. */ void standardize(char *p) { char *q; if (*p == '.' && *(p+1) == '\n') { /* Double it, in hopes smtp will single it. */ *(p+1) = '.'; return; } for (q=p; *q; q++) ; *--q = '\0'; } int is_intranet_relay(char *hdrline) { char *foo,*t,*x; int i; Cfg *cf; foo = strdup(hdrline); if (strncmp(foo,"Received: ",10)) return(-1); if ((t = strchr(foo,'['))) { x = strchr(t,']'); if (!x) die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); else { *x = '\0'; if (strlen(t) >16) die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); strncpy(riaddr,t+1,strlen(t)); } } else die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); cf = cfg_get("intranet-hosts",proxy_confp); while(cf != (Cfg *)0) { if(cf->argc < 1) goto skip; for(i = 0; i < cf->argc && cf->argv[i][0] != '-'; i++) { if(hostmatch(cf->argv[i],riaddr)) return(1); } skip: cf = cfg_get("intranet-hosts",(Cfg*)0); } return(0); } void get_header(void) { int i; char foo[MAXLINE],bar[MAXLINE]; int firstline = 1; Cfg *cf; headere=headers=xmalloc(CHUNK); while(fgets(bar,sizeof(bar),stdin) && (bar[0]!='\n')) { standardize(bar); recordRequiredHeaders(bar); if (firstline) { from_intranet = is_intranet_relay(bar); hide_intranet &= (from_intranet > 0) ? YES : NO; if (hide_intranet) { strlcpy(foo,bar,MAXLINE); fgets(bar,sizeof(bar),stdin); if (!bar || (strlen(bar+1) > 128)) die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); syslog(LLEV,"intranet route removed for message smap %.128s", bar+1); if (log_headers) { syslog(LLEV,"log-headers: %.512s",foo); syslog(LLEV,"log-headers: %.512s",bar); } snprintf(foo,MAXLINE-1,"Received: from undisclosed-intranet-sender %.128s",bar+1); strlcpy(bar,foo,MAXLINE); standardize(bar); } } else { if (hide_intranet) { if (!strncmp("Received: ",bar,10)) { if (log_headers) syslog(LLEV,"log-headers: %.512s",bar); while(fgets(bar,sizeof(bar),stdin) && ((bar[0]==' ')||(bar[0]=='\t'))) { if (bar[0] == '\n') return; if (log_headers) syslog(LLEV,"log-headers: %.512s",bar); } standardize(bar); recordRequiredHeaders(bar); } } } i = strlen(bar) + 1; if (headere+i-headers >CHUNK) die (EX_SOFTWARE,"Header too large Max is %d characters",CHUNK); strlcpy(headere,bar,CHUNK-(headere-headers+i)); headere+=i; if (*bar == '\0') break; firstline = 0; } permit_routed = 0; cf = cfg_get("permit-routed",proxy_confp); if (cf != (Cfg *)0) { if (cf->argc != 1) { die(EX_CONFIG,"fwtkcfgerr: permit-routed must have one parameter, line %d",cf->ln); } if (!strcmp(cf->argv[0],"any")) permit_routed = 1; else if (!strcmp(cf->argv[0],"intranet")) permit_routed = (from_intranet > 0) ? YES : NO; } return; } void send_body(int fd) { char bar[MAXLINE]; int barsize; FILE *tf; if (!(tf = fopen(qf,"r"))) die(EX_IOERR,"error reading temporary file"); while(fgets(bar,sizeof(bar),tf)) { barsize = strlen(bar); if (sowrite(fd,bar,barsize)!=barsize) die(EX_IOERR,"error writing to relay"); proxy_stats.outbytes+=barsize; proxy_update_status(); } fclose(tf); unlink(qf); } /* ** emergency exit functions. */ /* * flush -- save stdin to dead.letter, if you can. */ void flush(void) { char line[MAXLINE]; FILE *fp; if (!baddir) return; if (isatty(fileno(stdin))) { if (LogLevel > 0) { syslog(LLEV,"stdin appears to be a terminal. Not saving to dead.letter."); } return; } if (*riaddr) return; (void) snprintf(line,MAXLINE-1,"%s/dead.letter",baddir); if ((fp= fopen(line,"a")) == NULL) { /* Perhaps the person doesn't have a homedir... */ if (LogLevel > 0) { syslog(LLEV,"Can't open %s, failing horribly.", line); } return; } (void) putc('\n',fp); /* Make sure we start on a new line, */ (void) putc('\n',fp); /* with a blank line separating messages. */ while (fgets(line,sizeof line,stdin)) { (void) fputs(line,fp); } if (fclose(fp) == ERR) { if (LogLevel > 0) { syslog(LLEV,"Can't close %s/dead.letter, possibly truncated.", baddir); } } } /* ** Local/peculiar string manipulation. */ int getConfig(void) { Cfg *cf; char *dscpname; sotimeout(proxy_timeout = proxy_conf_timeout(proxy_confp)); if (!(MailHub = proxy_conf_string(proxy_confp,"default-relay"))) { syslog(LLEV,"fwtkcfgerr: no default relay specified"); exit(EX_CONFIG); } dscpname = proxy_conf_string(proxy_confp,"default-dscp"); if (dscpname) dscp = proxy_conf_diffserv_codepoint(proxy_confp,dscpname); if ((cf = cfg_get("hostname",proxy_confp)) != (Cfg *)0) { if (cf->argc != 1) { syslog(LLEV,"fwtkcfgerr: hostname must have one parameter, line %d",cf->ln); exit(EX_CONFIG); } strlcpy(HostName,cf->argv[0], MAXLINE); } if ((cf = cfg_get("hide-intranet",proxy_confp)) != (Cfg *)0) { if ((cf->argc == 1)) log_headers |= !strcasecmp(cf->argv[0],"-log-headers"); hide_intranet = 1; } return YES; } /* * doOptions -- pull the options out of the command-line, process them * (and special-case calls to mailq, etc), and return the rest. */ void doOptions(int argc, char *argv[]) { int i, newArgC; newArgC = 0; if (strstr(argv[0],"mailq") != NULL) { /* Someone wants to know the queue state... */ (void) printf("Mail queue is empty.\n"); exit(0); } else if (strstr(argv[0],"newalias") != NULL) { /* Someone wanted to recompile aliases. */ /* This is slightly more like to be a human... */ die(EX_USAGE,"newalias is meaningless to sSMTP: it doesn't do aliases."); } for (i=1; i < argc; i++) { if (argv[i][0] != '-') { addlist(argv[i],&rcpts); newArgC++; continue; } switch(argv[i][1]) { case 'b': switch (argv[i][2]) { case 'a': /* ARPANET mode. */ die(EX_USAGE,"-ba is not supported by sSMTP sendmail, nor is -t."); case 'd': /* Run as a daemon. */ die(EX_USAGE,"-bd is not supported by sSMTP sendmail. Use rSMTP under inetd instead."); case 'i': /* Initialize aliases. */ continue; case 'm': /* Default addr processing. */ continue; case 'p': /* Print mailqueue. */ die(0,"Mail queue is empty."); case 's': /* Read smtp from stdin. */ die(EX_USAGE,"-bs is not supported by sSMTP sendmail."); case 't': /* Test mode. */ die(EX_USAGE,"-bt is meaningless to sSMTP sendmail. It doesn't route."); case 'v': /* Verify names only. */ die(EX_USAGE,"-bv is meaningless to sSMTP sendmail. It doesn't route."); case 'z': /* Create freeze file. */ die(EX_USAGE,"-bz is meaningless to sSMTP sendmail. It isn't programmable."); } case 'C': /* Configfile name. */ continue; case 'd': /* Debug. */ LogLevel = 1; Verbose = YES; /* Almost the same thing... */ break; case 'E': /* insecure channel, don't trust userid. */ continue; case 'R': /* amount of the message to be returned */ /* Process queue for recipient. */ case 'F': /* fullname of sender. */ case 'f': /* Set from/sender address. */ case 'r': /* Obsolete -f flag. */ if (argv[i][2]) { strncpy(fromname,argv[i]+2,sizeof(fromname)); } else { i++; if (i>argc) continue; if (argv[i][1]!='-') { strncpy(fromname,argv[i],sizeof(fromname)); } } continue; /* Should I support these??? When? */ case 'h': /* Set hopcount. */ continue; case 'm': /* Ignore originator in adress list. */ continue; case 'M': /* USe specified message-id. */ continue; case 'N': /* dsn options */ i++; continue; case 'n': /* No aliasing. */ continue; case 'o': switch (argv[i][2]) { case 'A': /* Alternate aliases file. */ continue; case 'c': /* Delay connections. */ continue; case 'D': /* Run newaliases if rqd. */ continue; case 'd': /* Deliver now, in background or queue. */ /* This may warrant a diagnostic for b or q. */ continue; case 'e': /* Errors: mail, write or none. */ continue; case 'F': /* Set tempfile mode. */ continue; case 'f': /* Save ``From ' lines. */ continue; case 'g': /* Set group id. */ continue; case 'H': /* Helpfile name. */ continue; case 'i': /* DATA ends at EOF, not \n.\n */ continue; case 'L': /* Log level. */ continue; case 'm': /* Send to me if in the list. */ continue; case 'o': /* Old headers, spaces between adresses. */ die(EX_USAGE,"-oo (old header format) is not supported by sSMTP sendmail."); case 'Q': /* Queue dir. */ continue; case 'r': /* Read timeout. */ continue; case 's': /* Always init the queue. */ continue; case 'S': /* Stats file. */ continue; case 'T': /* Queue timeout. */ continue; case 't': /* Set timezone. */ continue; case 'u': /* Set uid. */ continue; case 'v': /* Set verbose flag. */ Verbose = YES; continue; } break; case 'q': /* Process the queue [at time] */ die(0,"Mail queue is empty."); case 't': /* Read message's To/Cc/Bcc lines. */ Toption = YES; continue; case 'v': /* Verbose (ditto -ov). */ Verbose = YES; break; case 'V': /* Say version and quit. */ /* Similar as die, but no logging */ fprintf(stderr, "sSMTP version %s (not sendmail at all)\n", Version); flush(); /* Send message to dead.letter */ exit(0); break; } } if (newArgC < 1 && ! Toption) { die(EX_USAGE,"no recipients supplied: no mail will be sent."); } if (newArgC >= 1 && Toption) die (EX_USAGE,"Recipientlist with -t option not supported."); return; } void get_email(buffer,eaddress) char *buffer; char *eaddress; { char *t,*x; if ((t = strchr(buffer,'<'))) { x = strchr(t,'>'); if(!x) { die(EX_DATAERR,"email address is malfored, no trailing bracket"); } else { *x = '\0'; strlcpy(eaddress,t+1,MAXLINE); } } else strlcpy(eaddress,buffer,MAXLINE); return; } void get_relay(email,myrelay) char *email; char *myrelay; { Cfg *cf; char *thost; char foo[MAXLINE]; char z[MAXLINE]; fwparm options[] = { { FWPARM_STRING,"-via", myrelay }, { FWPARM_DSCP, "-server-dscp",(char*)&dscp }, { FWPARM_DSCP, "-dscp", (char*)&dscp }, { 0, 0, 0 }}; strlcpy(foo,email,MAXLINE); get_email(foo,z); if (strtok(z,"@") != NULL) thost=strtok(NULL,"\0"); else { strlcpy(myrelay,MailHub,MAXLINE); return; } if (!thost) { strlcpy(myrelay,MailHub,MAXLINE); return; } cf=cfg_get("relay", proxy_confp); while (cf != (Cfg *)0) { int i; if((cf->argc < 1) || (cf->argv[0][0] == '-')) { die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln); } for (i = 0; i < cf->argc && cf->argv[i][0] != '-'; ++i) { if (!(strcasecmp(cf->argv[i],thost))) { proxy_parse_options(cf,options); return; } } cf=cfg_get("relay",(Cfg *)0); } strlcpy(myrelay,MailHub,MAXLINE); return; } static void prepare_message() { char *p; char bar[MAXLINE]; int barsize; Cfg *cf; mfrom = xmalloc(MAXLINE); qf = xstrdup(QTEMPLATE); if ((tfd = mkstemp(qf)) == -1) die(EX_IOERR,"error creating temporary file"); get_header(); get_email(fromLine(),mfrom); addInitialHeaders(tfd); addRequiredHeaders(tfd); for(p=headers;pargc < 1) || (cf->argv[0][0] == '-')) { die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln); } if (((mfstate = milter_inspect_mail("ssmtp",riaddr,riaddr, cf->argv[0], qf,mfrom,&rcpts)) & MS_ER)) die(EX_TEMPFAIL,"milter failed"); if (mfstate & MS_RJ) die(EX_OK,"message rejected by milter"); cf=cfg_get("milter",(Cfg *)0); } if (from_intranet) { cf=cfg_get("intranet-milter", proxy_confp); while (cf != (Cfg *)0) { int mfstate; proxy_update_operation("MILTER"); if((cf->argc < 1) || (cf->argv[0][0] == '-')) { die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln); } if (((mfstate = milter_inspect_mail("ssmtp",riaddr, riaddr, cf->argv[0], qf,mfrom,&rcpts)) & MS_ER)) die(EX_TEMPFAIL,"milter failed"); if (mfstate & MS_RJ) die(EX_OK,"message rejected by milter"); cf=cfg_get("intranet-milter",(Cfg *)0); } } else { cf=cfg_get("extranet-milter", proxy_confp); while (cf != (Cfg *)0) { int mfstate; proxy_update_operation("MILTER"); if((cf->argc < 1) || (cf->argv[0][0] == '-')) { die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln); } if (((mfstate = milter_inspect_mail("ssmtp",riaddr, riaddr, cf->argv[0], qf,mfrom,&rcpts)) & MS_ER)) die(EX_TEMPFAIL,"milter failed"); if (mfstate & MS_RJ) die(EX_OK,"message rejected by milter"); cf=cfg_get("extranet-milter",(Cfg *)0); } } } /* * ssmtp -- send the message (exactly one) from stdin to the smtp * port on the mailhub. */ int ssmtp() { char buffer[MAXLINE], relay[MAXLINE],crelay[MAXLINE],*p,c[MAXLINE]; int fd, i, newArgC =0; char **newrcpts = NULL; int rc; get_relay(rcpts[0],relay); syslog(LLEV,"relay to %.128s",relay); strncpy(proxy_stats.dst,relay,sizeof(proxy_stats.dst)); proxy_update_operation("CONNECT"); /* Now to the delivery of the message */ if ((fd=conn_server(relay,PORTNUMBER,0,buffer)) < 0) { die(EX_IOERR,"can't open the smtp port (%d) on %s: %s.", PORTNUMBER,relay,buffer); } if (dscp) proxy_set_dscp(fd,dscp); if (getOkFromSmtp(fd,buffer) == NO) { die(EX_IOERR,"didn't get initial OK message from smtp server."); } if (Verbose) { (void) fprintf(stderr,"Connected to smtp server %s\n",MailHub); } if (LogLevel > 0) { syslog(LLEV,"Connected to smtp server %s\n",MailHub); } /* Send "HELO", hostname. */ proxy_update_operation("HELO"); putToSmtp(fd,"HELO %s",HostName); if (getOkFromSmtp(fd,buffer) == NO) { die(EX_NOHOST,"server didn't accept hostname %s, replied \"%s\".", HostName,buffer); } proxy_update_operation("MAIL FROM"); putToSmtp(fd,"MAIL FROM:<%s>",mfrom); if (getOkFromSmtp(fd,buffer) == NO) { die(1,"smtp server didn't accept MAIL From, replied \"%s\".", buffer); } if (Verbose) { (void) fprintf(stderr,"Server accepted MAIL FROM: %s line.\n", fromLine()); } if (LogLevel > 0) { syslog(LLEV,"Server accepted MAIL FROM: %s line.\n", fromLine()); } /* Send all the To: adresses. */ /* Either we're using the -t option, or we're using the arguments */ rc = EX_NOUSER; for (i=0; rcpts[i] != NULL; i++) { p=strtok(rcpts[i],","); while(p) { get_relay(p,crelay); if (!strcmp(relay,crelay)) { get_email(p,c); if (permit_routed || !index(c,'%')) { proxy_update_operation("RCPT TO"); putToSmtp(fd,"RCPT TO:<%s>",c); if (getOkFromSmtp(fd,buffer) == NO) { syslog(LLEV,"smtp server didn't accept RCPT To: command, replied \"%s\".", buffer); } else rc = 0; } else syslog(LLEV,"routed delivery for %.256s denied",c); } else { syslog(LLEV,"relay=%s/%s for %s",relay,crelay,p); newArgC++; addlist(p,&newrcpts); } p=strtok(NULL,","); } } if (rc) { putToSmtp(fd,"QUIT"); unlink(qf); die(rc,"no valid receipients for this message"); } if (Verbose) { (void) fprintf(stderr,"Server accepted To: line(s).\n"); } if (LogLevel > 0) { syslog(LLEV,"Server accepted To: line(s).\n"); } /* Send DATA. */ proxy_update_operation("DATA"); putToSmtp(fd,"DATA"); if (getFromSmtp(fd,buffer) != 3) { /* Oops, we were expecting "354 send your data". */ die(EX_PROTOCOL,"smtp server didn't accept DATA, replied \"%s\".", buffer); } if (Verbose) { (void) fprintf(stderr,"Message body transmission started.\n"); } /* Send headers, with optional From: rewriting. */ send_body(fd); putToSmtp(fd,"."); proxy_update_operation("QUIT"); if (getOkFromSmtp(fd,buffer) == NO) { die(EX_PROTOCOL,"smtp server wouldn't accept message, replied \"%s\".", buffer); } if (Verbose) { (void) fprintf(stderr,"Message body transmission complete.\n"); } /* Close conection. */ (void) signal(SIGALRM,SIG_IGN); putToSmtp(fd,"QUIT"); (void) getOkFromSmtp(fd,buffer); (void) close(fd); if (newArgC) { freelist(&rcpts); rcpts = newrcpts; ssmtp(); } return rc; } /* ** Supporting libraries -- i/o. */ /* * getOkFromSmtp -- get a line and test the three-number string * at the beginning. If it starts with a 2, it's OK. */ int getOkFromSmtp(int fd, char *response) { return (getFromSmtp(fd,response) == 2)? YES: NO; } /* * getFromSmtp -- get a line and return the initial digit. Deal with * continuation lines by reading to the last (non-continuation) line. */ int getFromSmtp(int fd, char *response) { do { if (sogets(fd,response,MAXLINE) <= 0 ) { *response = '\0'; return NO; } } while (response[3] == '-'); if (LogLevel > 0) { syslog(LLEV,"Received \"%s\" from smtp port.\n",response); } return atoi(response) / 100; } /* * main -- make the program behave like sendmail, then call ssmtp. */ int main(int argc, char *argv[]) { /* Try to be bulletproof (:-)) */ (void) signal(SIGHUP,SIG_IGN); (void) signal(SIGINT,SIG_IGN); (void) signal(SIGTTIN,SIG_IGN); (void) signal(SIGTTOU,SIG_IGN); #ifndef LOG_DAEMON openlog("ssmtp",LOG_PID); #else openlog("ssmtp",LOG_PID|LOG_NDELAY,LFAC); #endif #if defined(linux) || defined (SOLARIS) initsetproctitle(argc,argv); #endif if((proxy_confp = cfg_read("ssmtp")) == (Cfg *)-1) exit(EX_CONFIG); proxy_conf_userid(proxy_confp); proxy_conf_groupid(proxy_confp); proxy_conf_chroot(proxy_confp); proxy_chroot_setugid(); baddir = proxy_conf_string(proxy_confp,"baddir"); strncpy(proxy_name,argv[0],sizeof(proxy_name) - 1); proxy_update_operation("INIT"); if (gethostname(HostName,sizeof(HostName)) == ERR) { die(EX_OSERR,"fwtksyserr: can't find the name of this host, %s, exiting.", "(an impossible condition)"); } if ((Sender= getpwuid(getuid())) == NULL) { die(EX_OSERR,"couldn't find password entry for sender (uid %d).", getuid()); } strlcpy(DateString, arpadate(NULL), sizeof(DateString)); doOptions(argc,argv); getConfig(); if (Toption) rcpts = NULL; prepare_message(); exit(ssmtp()); }