/* * Copyright 1988 by Rayan S. Zachariassen, all rights reserved. * This will be free software, but only when it is finished. */ /* * Several extensive changes by Matti Aarnio * Copyright 1991-2003. */ /* * ZMailer SMTP server. */ #include "smtpserver.h" const char *VerbID = "ZMailer SMTP server %s"; const char *Copyright = "Copyright 1990 Rayan S. Zachariassen"; const char *Copyright2 = "Copyright 1991-2003 Matti Aarnio"; /* Timing parameters -- when expired, session is killed ... */ #include "identuser.h" #ifdef USE_TRANSLATION #include "libtrans.h" #endif /* USE_TRANSLATION */ #ifdef HAVE_WHOSON_H #include #endif /* * Early inetd's, which may be found on 4.2BSD based systems (e.g. * Sun OS 3.x), are incapable of passing a flag to indicate we are * being run by inetd rather than directly. Some hackery to detect * when we are being run by the 4.2 inetd is included by defining * CHECK42INETD. Should be deleted at the earliest possible opportunity. */ /* #define CHECK42INETD */ /* heuristics to detect the 4.2 inetd */ /* [mea@utu.fi] - 4-Feb-95, I disabled this.. */ struct command command_list[] = { {"EHLO", Hello2}, /* Normal stuff.. */ {"HELO", Hello}, {"LHLO", HelloL}, {"MAIL", Mail}, {"RCPT", Recipient}, {"DATA", Data}, {"BDAT", BData,}, {"RSET", Reset}, {"VRFY", Verify}, {"EXPN", Expand}, {"HELP", Help}, {"NOOP", NoOp}, {"QUIT", Quit}, /* ZMailer speciality, and an alias for it */ {"TURNME", Turnme}, {"ETRN", Turnme}, /* RFC 1985 */ /* SMTP AUTH -- NetScape Way.. (RFC 2554) */ {"AUTH", Auth}, /* sendmail extensions */ {"VERB", Verbose}, {"ONEX", NoOp}, /* Deprecated */ {"SEND", Send}, {"SOML", SendOrMail}, {"SAML", SendAndMail}, {"TURN", Turn}, /* bsmtp extensions */ {"TICK", Tick}, /* 8-bit smtp extensions -- deprecated */ {"EMAL", Mail2}, {"ESND", Send2}, {"ESOM", Send2}, {"ESAM", Send2}, {"EVFY", Verify2}, /* To fool loosers.. */ {"IDENT", DebugIdent}, {"DEBUG", DebugMode}, /* End of the list */ #ifdef HAVE_OPENSSL {"STARTTLS", StartTLS}, /* RFC 2487 */ #endif /* - HAVE_OPENSSL */ {"550", Silent}, /* Some Windows SMTP systems are mixing their threads - they send smtp server error messages to stream where they should be sending SMTP client verbs.. */ {0, Null} }; struct policytest *policydb = NULL; struct smtpconf *cfhead = NULL; struct smtpconf *cfinfo = NULL; const char *progname, *cmdline, *eocmdline, *logfile; char *routerprog = NULL; int logstyle = 0; /* 0: no suffix, 1: 'myhostname', 2: 'rhostname' */ int debug = 0; int skeptical = 1; int checkhelo = 0; int verbose = 0; int daemon_flg = 1; int pid, routerpid = -1; extern int contentpolicypid; int router_status = 0; FILE *logfp = NULL; int logfp_to_syslog = 0; int D_alloc = 0; int smtp_syslog = 0; #ifdef USE_TRANSLATION int X_translation = 0; int X_8bit = 0; int X_settrrc = 9; #endif /* USE_TRANSLATION */ int strict_protocol = 0; int mustexit = 0; int configuration_ok = 0; int gotalarm; int unknown_cmd_limit = 10; int sum_sizeoption_value = 0; int always_flush_replies = 0; char logtag[32]; time_t logtagepoch, now; sigjmp_buf jmpalarm; /* Return-frame for breaking smtpserver when timeout hits.. */ char *helplines[HELPMAX + 2] = {NULL,}; char *hdr220lines[HDR220MAX + 2] = {NULL, }; const char *m200 = "2.0.0"; const char *m400 = "4.0.0"; const char *m430 = "4.3.0"; const char *m431 = "4.3.1"; const char *m443 = "4.4.3"; const char *m454 = "4.5.4"; const char *m471 = "4.7.1"; const char *m513 = "5.1.3"; const char *m517 = "5.1.7"; const char *m530 = "5.3.0"; const char *m534 = "5.3.4"; const char *m540 = "5.4.0"; const char *m543 = "5.4.3"; const char *m550 = "5.5.0"; const char *m551 = "5.5.1"; const char *m552 = "5.5.2"; const char *m554 = "5.5.4"; const char *m571 = "5.7.1"; /* * The "style" variable controls when the router is interrogated about the * validity of something. It is a string of letter-flags: * f: check MAIL FROM addresses * t: check RCPT TO addresses * v: check VRFY command argument * e: check EXPN command argument * R: Demand strict conformance to RFC821/822 at address arguments */ const char *style = "ve"; long availspace = -1; /* available diskspace/2 in bytes */ long minimum_availspace = 5000; /* 5 million bytes free, AT LEAST */ long maxsize = 0; int ListenQueueSize = 20000; int TcpRcvBufferSize = 0; int TcpXmitBufferSize = 0; int MaxSameIpSource = 100; /* Max number of smtp connections in progress from same IP address -- this to detect systems sending lots of mail all in parallel smtp sessions -- also to detect some nutty Windows systems opening up bunches of connections to the remote system -- and to detect an attempt on creating a denial-of-service attach by opening lots and lots of connections to the remote SMTP server... */ int MaxParallelConnections = 800; /* Total number of childs allowed */ int MaxErrorRecipients = 3; /* Max number of recipients for a message that has a "box" ( "<>" ) as its source address. */ int percent_accept = -1; int maxloadavg = 999; /* Maximum load-average that is tolerated with smtp-server actively receiving.. Default value of 999 is high enough so that it will never block -- use "-L 10" to define lower limit (10) */ int allow_source_route = 0; /* When zero, do ignore source route address "@a,@b:c@d" by collapsing it into "c@d" */ int rcptlimitcnt = 10000; /* Allow up to 10 000 recipients for each MAIL FROM. -- or tune this.. */ int debugcmdok = 0; int expncmdok = 0; int vrfycmdok = 0; int use_ipv6 = 0; int ident_flag = 0; int do_whoson = 0; int pipeliningok = 1; int chunkingok = 1; int enhancedstatusok = 1; int multilinereplies = 1; int enable_router = 0; /* Off by default -- security */ int do_sasl = 0; int MaxSLBits = 1000000; /* a HIGH value */ int SASLOpts; int mime8bitok = 1; int dsn_ok = 1; int auth_ok = 0; int ehlo_ok = 1; int etrn_ok = 1; int starttls_ok = 0; int ssmtp_listen = 0; /* Listen on port TCP/465; deprecated SMTP in TLS */ int ssmtp_connected = 0; int submit_connected = 0; int msa_mode = 0; int deliverby_ok = -1; /* FIXME: RFC 2852 */ etrn_cluster_ent etrn_cluster[MAX_ETRN_CLUSTER_IDX] = { {NULL,}, }; const char *tls_cert_file = NULL; const char *tls_key_file = NULL; const char *tls_dcert_file = NULL; const char *tls_dkey_file = NULL; const char *tls_CAfile = NULL; const char *tls_CApath = NULL; const char *tls_dh1024_param = NULL; const char *tls_dh512_param = NULL; const char *tls_cipherlist = NULL; const char *tls_random_source = NULL; int tls_loglevel = 0; int tls_enforce_tls = 0; int tls_ccert_vd = 1; int tls_ask_cert = 0; int tls_req_cert = 0; int log_rcvd_whoson = 0; int log_rcvd_ident = 0; int log_rcvd_authuser = 0; int log_rcvd_tls_mode = 0; int log_rcvd_tls_peer = 0; int auth_login_without_tls = 0; char *smtpauth_via_pipe = NULL; Usockaddr *bindaddrs; int bindaddrs_count = 0; Usockaddr testaddr; int bindaddr_set = 0; int testaddr_set = 0; u_short bindport = 0; int bindport_set = 0; int use_tcpwrapper = 0; int tarpit_initial = 0; /* TARPIT is DISABLED, by default */ int tarpit_exponent = 0; int tarpit_toplimit = 0; int tarpit_cval = 0; int lmtp_mode = 0; /* A sort-of RFC 2033 LMTP mode ; this is MAINLY for debug purposes, NOT for real use! */ int detect_incorrect_tls_use; int force_rcpt_notify_never; #define LSOCKTYPE_SMTP 0 #define LSOCKTYPE_SSMTP 1 #define LSOCKTYPE_SUBMIT 2 #ifndef IDENT_TIMEOUT #define IDENT_TIMEOUT 5 #endif /* IDENT_TIMEOUT */ #if defined(AF_INET6) && defined(INET6) static const u_char zin6addrany[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const u_char zv4mapprefix[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0}; #endif static void setrfc1413ident __((SmtpState * SS)); static void setrhostname __((SmtpState *)); extern int pipeauthchild_pid; /* zpwmatch-pipe.c */ extern int pipeauthchild_status; static RETSIGTYPE reaper __((int sig)); static RETSIGTYPE timedout __((int sig)); static RETSIGTYPE sigterminator __((int sig)); static void smtpserver __((SmtpState *, int insecure)); const char *msg_toohighload = "421 Sorry, the system is too loaded for email reception at the moment\r\n"; /* XX: ??? */ extern void type220headers __((SmtpState *SS, const int identflg, const char *xlatelang, const char *curtime)); extern void openlogfp __((SmtpState * SS, int insecure)); extern const char taspid_encodechars[]; void openlogfp(SS, insecure) SmtpState *SS; int insecure; { /* opening the logfile should be done before we reset the uid */ struct tm *tt; time( & now ); tt = gmtime(&now); pid = getpid(); logtagepoch = now; /* %M%D%h%m%s_pid_ */ sprintf( logtag, "%c%c%c%c%c%c%c", taspid_encodechars[ tt->tm_mday-1 ], taspid_encodechars[ tt->tm_hour ], taspid_encodechars[ tt->tm_min ], taspid_encodechars[ tt->tm_sec ], taspid_encodechars[ (pid >> 12) & 63 ], taspid_encodechars[ (pid >> 6) & 63 ], taspid_encodechars[ (pid ) & 63 ] ); if (logfp != NULL) fclose(logfp); logfp = NULL; if (logfile != NULL) { char *fname; int len1 = strlen(logfile); int len2, fd; const char *s = ""; if (logstyle == 1) s = SS->myhostname; if (logstyle == 2) s = SS->rhostname; len2 = strlen(s); #ifdef HAVE_ALLOCA fname = (char*)alloca(len1 + 1 + len2 + 1); #else fname = malloc(len1 + 1 + len2 + 1); #endif if (logstyle != 0) sprintf(fname, "%s.%s", logfile, s); else strcpy(fname, logfile); fd = open(fname, O_CREAT | O_APPEND | O_WRONLY, 0644); if (fd < 0) { if (!insecure && daemon_flg) fprintf(stderr, "%s: cannot open logfile \"%s\": %s\n", progname, fname, strerror(errno)); } else { fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); logfp = fdopen(fd, "a"); setvbuf(logfp, NULL, _IOFBF, BUFSIZ); /* Line-buffered */ } #ifndef HAVE_ALLOCA if (logstyle != 0) free(fname); #endif } else logfp = NULL; } static void create_server_socket __((int *, int **, int **, int, int, Usockaddr * )); static void create_server_socket (lscnt_p, ls_p, lst_p, use_ipv6, portnum, bindaddr) int *lscnt_p, **ls_p, **lst_p; int use_ipv6, portnum; Usockaddr *bindaddr; { int s, i; s = socket( #ifdef PF_INET6 use_ipv6 ? PF_INET6 : PF_INET, #else PF_INET, #endif SOCK_STREAM, 0 /* IPPROTO_IP */ ); if (s < 0) { fprintf(stderr, "%s: socket(PF_INET%s, SOCK_STREAM): %s\n", progname, (use_ipv6 ? "6" : ""), strerror(errno)); return; } *ls_p = realloc( *ls_p, sizeof(int) * ((*lscnt_p) +2)); *lst_p = realloc( *lst_p, sizeof(int) * ((*lscnt_p) +2)); if (! *ls_p || ! *lst_p ) { fprintf(stderr, "%s: malloc() failure!\n", progname); exit(1); } (*ls_p )[ *lscnt_p ] = s; (*lst_p)[ *lscnt_p ] = LSOCKTYPE_SMTP; *lscnt_p += 1; i = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (caddr_t) & i, sizeof i) < 0) { fprintf(stderr, "%s: setsockopt(SO_REUSEADDR): %s\n", progname, strerror(errno)); } #ifdef SO_REUSEPORT if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (caddr_t) & i, sizeof i) < 0) { fprintf(stderr, "%s: setsockopt(SO_REUSEPORT): %s\n", progname, strerror(errno)); } #endif #ifdef SO_RCVBUF if (TcpRcvBufferSize > 0) if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &TcpRcvBufferSize, sizeof(TcpRcvBufferSize)) < 0) { fprintf(stderr, "%s: setsockopt(SO_RCVBUF): %s\n", progname, strerror(errno)); } #endif #ifdef SO_SNDBUF if (TcpXmitBufferSize > 0) if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &TcpXmitBufferSize, sizeof(TcpXmitBufferSize)) < 0) { fprintf(stderr, "%s: setsockopt(SO_SNDBUF): %s\n", progname, strerror(errno)); } #endif #if defined(AF_INET6) && defined(INET6) if (use_ipv6) { struct sockaddr_in6 si6; memset(&si6, 0, sizeof(si6)); si6.sin6_family = AF_INET6; si6.sin6_flowinfo = 0; si6.sin6_port = htons(portnum); memcpy( &si6.sin6_addr, zin6addrany, 16 ); if (bindaddr && bindaddr->v6.sin6_family == AF_INET6) memcpy(&si6.sin6_addr, &bindaddr->v6.sin6_addr, 16); i = bind(s, (struct sockaddr *) &si6, sizeof si6); if (i < 0) { fprintf(stderr, "%s: bind(IPv6): %s\n", progname, strerror(errno)); } } else #endif { struct sockaddr_in si4; memset(&si4, 0, sizeof(si4)); si4.sin_family = AF_INET; si4.sin_addr.s_addr = INADDR_ANY; si4.sin_port = htons(portnum); if (bindaddr && bindaddr->v4.sin_family == AF_INET) memcpy(&si4.sin_addr, &bindaddr->v4.sin_addr, 4); i = bind(s, (struct sockaddr *) &si4, sizeof si4); if (i < 0) { fprintf(stderr, "%s: bind(IPv4): %s\n", progname, strerror(errno)); } } /* Set the listen limit HIGH; there has been an active denial-of-service attack where people send faked SYNs to open a connection to our system who does not want to talk with us at all -- or whose traffic goes thru a routing black-hole, who just plain don't exist on some existing network. The net result being that our SYN-ACKs will never get ACKed, and no connection built... The SYN_RECV-queue can grow also due to a 'routing diode' behind of which some poor fellow is trying to get our attention, but our replies do not reach back to him/her. */ /* Often the system enforces some upper bound for this limit, and you can't exceed it -- but some new systems allow rather high limits, lets try to use it! (The classical default is: 5) */ fd_nonblockingmode(s); if (listen(s, ListenQueueSize) < 0) { fprintf(stderr, "%s: listen(smtp_sock,%d): %s\n", progname, ListenQueueSize, strerror(errno)); } } int main __((int, char **)); int main(argc, argv) int argc; char **argv; { int inetd, errflg, raddrlen, version, i = 0; const char *mailshare; char path[1024]; int force_ipv4 = 0; int localsocksize; char *cfgpath = NULL; char *pidfile = PID_SMTPSERVER; int pidfile_set = 0; const char *t, *syslogflg; SmtpState SS; progname = argv[0] ? argv[0] : "smtpserver"; cmdline = &argv[0][0]; eocmdline = argv[argc-1] + strlen(argv[argc-1]) + 1; setvbuf(stdout, NULL, _IOFBF, 8192); setvbuf(stderr, NULL, _IOLBF, 8192); memset(&SS, 0, sizeof(SS)); bindaddrs = NULL; SS.mfp = NULL; SS.style = "ve"; SS.state = Hello; SS.with_protocol = WITH_SMTP; SIGNAL_HANDLE(SIGPIPE, SIG_IGN); daemon_flg = 1; inetd = 0; errflg = 0; version = 0; logfile = NULL; logstyle = 0; *SS.myhostname = 0; if (getmyhostname(SS.myhostname, sizeof SS.myhostname) < 0) { fprintf(stderr, "%s: gethostname('%s'): %s\n", progname, SS.myhostname, strerror(errno)); exit(1); } /* optarg = NULL; */ while (1) { int c = getopt(argc, argv, #ifndef __STDC__ #if defined(AF_INET6) && defined(INET6) #ifdef USE_TRANSLATION "?46aBC:d:ighl:np:tI:L:M:P:R:s:S:T:VvwZ:X8" #else /* xlate */ "?46aBC:d:ighl:np:tI:L:M:P:R:s:S:T:VvwZ:" #endif /* xlate */ #else /* INET6 */ #ifdef USE_TRANSLATION "?4aBC:d:ighl:np:tI:L:M:P:R:s:S:T:VvwZ:X8" #else "?4aBC:d:ighl:np:tI:L:M:P:R:s:S:T:VvwZ:" #endif /* xlate */ #endif /* INET6 */ #else /* __STDC__ */ "?" "4" #if defined(AF_INET6) && defined(INET6) "6" #endif "aBC:d:ighl:n" "p:t" "I:L:M:P:R:s:S:T:VvwZ:" #ifdef USE_TRANSLATION "X8" #endif /* USE_TRANSLATION */ #endif ); if (c == EOF) break; if (c == '?') { ++errflg; break; } switch (c) { case '4': force_ipv4 = 1; break; #if defined(AF_INET6) && defined(INET6) case '6': #if AF_INET6 == 0 ::ERROR::ERROR: AF_INET6 has CPP value ZERO! #endif use_ipv6 = AF_INET6; break; #endif case 'a': ident_flag = 1; break; case 'B': SS.with_protocol = WITH_BSMTP; break; case 'C': cfgpath = optarg; break; case 'd': debug = atoi(optarg); break; case 'g': /* gullible */ skeptical = 0; break; case 'h': /* checkhelo */ checkhelo = 1; break; case 'i': /* interactive */ daemon_flg = 0; break; case 'I': /* PID file */ pidfile = optarg; pidfile_set = 1; break; case 'l': /* log file(prefix) */ if (strcmp(optarg,"SYSLOG")==0) { logfp_to_syslog = 1; break; } logfile = optarg; break; case 'L': /* Max LoadAverage */ maxloadavg = atoi(optarg); if (maxloadavg < 1) maxloadavg = 10; /* Humph.. */ break; case 'M': maxsize = atol(optarg); if (maxsize < 0) maxsize = 0; break; case 'n': /* running under inetd */ inetd = 1; break; case 'p': bindport = atoi(optarg); bindport_set = 1; break; case 'P': postoffice = strdup(optarg); break; case 'R': /* router binary used for verification */ routerprog = strdup(optarg); break; case 's': /* checking style */ if (strcmp(optarg,"strict")==0) strict_protocol = 1; else style = strdup(optarg); break; case 'S': /* Log-suffix style */ logstyle = 0; if (CISTREQ(optarg, "remote")) logstyle = 2; else if (CISTREQ(optarg, "local")) logstyle = 1; break; case 't': ssmtp_connected = 1; /* If this connection should immediately start the TLS negotiaion before SMTP greeting -- and only then do SMTP greet. */ break; case 'T': /* Enter in interactive mode claimed foreign source IPv4/IPv6 address, and then proceed to handle policy analysis as in normal operational case. */ memset(&testaddr, 0, sizeof(testaddr)); testaddr_set = 1; #if defined(AF_INET6) && defined(INET6) if (CISTREQN(optarg,"[ipv6 ",6) || CISTREQN(optarg,"[ipv6:",6) || CISTREQN(optarg,"[ipv6.",6)) { char *s = strchr(optarg,']'); if (s) *s = 0; if (inet_pton(AF_INET6, optarg+6, &testaddr.v6.sin6_addr) < 1) { /* False IPv6 number literal */ /* ... then we don't set the IP address... */ testaddr_set = 0; fprintf(stderr,"smtpserver: -T option argument is not valid IPv6 address: [ipv6.hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:1.2.3.4]\n"); ++errflg; } testaddr.v6.sin6_family = AF_INET6; } else #endif if (*optarg == '[') { char *s = strchr(optarg,']'); if (s) *s = 0; if (inet_pton(AF_INET, optarg+1, &testaddr.v4.sin_addr) < 1) { /* False IP(v4) number literal */ /* ... then we don't set the IP address... */ testaddr_set = 0; fprintf(stderr,"smtpserver: -T option argument is not valid IPv4 address: [1.2.3.4]\n"); ++errflg; } testaddr.v4.sin_family = AF_INET; } else { testaddr_set = 0; fprintf(stderr,"smtpserver: -T option argument must be wrapped inside brackets: [1.2.3.4]\n"); ++errflg; } break; case 'v': verbose = 1; /* in conjunction with -i */ break; case 'V': prversion("smtpserver"); exit(0); break; /* paranoia */ case 'w': /* Do Who-is-on query */ do_whoson = 1; break; #ifdef USE_TRANSLATION case 'X': X_translation = 1; break; case '8': X_8bit = 1; break; #endif /* USE_TRANSLATION */ case 'Z': if (readzenv(optarg) == 0) ++errflg; break; default: fprintf(stderr, "%s: Unknown option, c=%d ('%c')\n", progname, c, c); ++errflg; break; } } syslogflg = getzenv("SYSLOGFLG"); if (syslogflg == NULL) syslogflg = ""; t = syslogflg; for ( ; *t ; ++t ) { if (*t == 's' || *t == 'S') break; } smtp_syslog = *t; #ifdef CHECK42INETD /* * If no flags set and we have one argument, check * argument format to see if it's from the 4.2 inetd. */ if (!errflg && daemon_flg && skeptical && !inetd && port == 0 && optind == argc - 1) if (isit42inetd(argv[optind])) { inetd = 1; optind++; } #endif /* CHECK42INETD */ if (errflg || optind != argc) { fprintf(stderr, #ifndef __STDC__ "Usage: %s [-46aBignVvw]\ [-C cfgfile] [-s xx] [-L maxLoadAvg]\ [-M SMTPmaxsize] [-R rtrprog] [-p port#]\ [-P postoffice] [-l SYSLOG] [-l logfile] [-S 'local'|'remote']\ [-I pidfile] [-T test-net-addr] [-Z zenvfile]\n" #else /* __STDC__ */ "Usage: %s [-4" #if defined(AF_INET6) && defined(INET6) "6" #endif "aBignVvw" #ifdef USE_TRANSLATION "X8" #endif "] [-C cfgfile] [-s xx] [-L maxLoadAvg]" " [-M SMTPmaxsize] [-R rtrprog] [-p port#]" " [-P postoffice] [-l logfile] [-S 'local'|'remote']" " [-I pidfile] [-T test-net-addr] [-Z zenvfile]\n" #endif /* __STDC__ */ , progname); exit(1); } pid = getpid(); if (!logfp) openlogfp(&SS, daemon_flg); /* The automatic "system can do ipv6" testing controls "use_ipv6" variable, which in turn controls what may happen in readcffile() */ #if defined(AF_INET6) && defined(INET6) /* Perhaps the system can grok the IPv6 - at least the headers seem to indicate so, but like we know of Linux, the protocol might not be loaded in, or some such... If we are not explicitely told to use IPv6 only, we will try here to use IPv6, and if successfull, register it! */ if (!use_ipv6 && !force_ipv4) { int s = socket(PF_INET6, SOCK_STREAM, 0 /* IPPROTO_IPV6 */ ); if (s >= 0) { use_ipv6 = AF_INET6; /* We can do it! */ close(s); } } if (use_ipv6) { int s = socket(PF_INET6, SOCK_STREAM, 0 /* IPPROTO_IPV6 */ ); if (s < 0) { /* Fallback to the IPv4 mode .. */ s = socket(PF_INET, SOCK_STREAM, 0 /* IPPROTO_IP */ ); use_ipv6 = 0; } close(s); } #endif mailshare = getzenv("MAILSHARE"); if (mailshare == NULL) mailshare = MAILSHARE; if (cfgpath == NULL) { char *t = strrchr(progname, '/'); if (t != NULL) sprintf(path, "%s/%s.conf", mailshare, t + 1); else sprintf(path, "%s/%s.conf", mailshare, progname); } if (cfgpath == NULL) cfhead = readcffile(path); else cfhead = readcffile(cfgpath); if (daemon_flg) if (lmtp_mode && (!bindport_set || (bindport_set && bindport == 25))) lmtp_mode = 0; /* Disable LMTP mode unless we are bound at other than port 25. */ #ifdef HAVE_OPENSSL Z_init(); /* Some things for private processors */ #endif /* - HAVE_OPENSSL */ if (!allow_source_route) allow_source_route = (getzenv("ALLOWSOURCEROUTE") != NULL); SS.netconnected_flg = 0; if (!daemon_flg) { raddrlen = sizeof(SS.raddr); memset(&SS.raddr, 0, raddrlen); if (getpeername(SS.inputfd, (struct sockaddr *) &SS.raddr, &raddrlen)) { if (testaddr_set) { SS.netconnected_flg = 1; memcpy(&SS.raddr, &testaddr, sizeof(testaddr)); } } else { /* Got a peer name (it is a socket) */ SS.netconnected_flg = 1; if (SS.raddr.v4.sin_family != AF_INET #ifdef AF_INET6 && SS.raddr.v4.sin_family != AF_INET6 #endif ) { /* well, but somebody uses socketpair(2) which is an AF_UNIX thing and sort of full-duplex pipe(2)... */ SS.netconnected_flg = 0; } if (SS.netconnected_flg) { /* Lets figure-out who we are this time around -- we may be on a machine with multiple identities per multiple interfaces, or via virtual IP-numbers, or ... */ localsocksize = sizeof(SS.localsock); if (getsockname(FILENO(stdin), (struct sockaddr *) &SS.localsock, &localsocksize) != 0) { /* XX: ERROR! */ } } } strcpy(SS.rhostname, "stdin"); SS.rport = -1; SS.ihostaddr[0] = '\0'; sprintf(SS.ident_username, "uid#%d@localhost", (int)getuid()); /* INTERACTIVE */ s_setup(&SS, FILENO(stdin), FILENO(stdout)); smtpserver(&SS, 0); } else if (inetd) { #if 0 if (maxloadavg != 999 && maxloadavg < loadavg_current()) { write(1, msg_toohighload, strlen(msg_toohighload)); sleep(2); exit(1); } #endif raddrlen = sizeof(SS.raddr); memset(&SS.raddr, 0, raddrlen); if (getpeername(SS.inputfd, (struct sockaddr *) &SS.raddr, &raddrlen)) SS.netconnected_flg = 0; else SS.netconnected_flg = 1; #if defined(AF_INET6) && defined(INET6) if (SS.raddr.v6.sin6_family == AF_INET6) SS.rport = SS.raddr.v6.sin6_port; else #endif SS.rport = SS.raddr.v4.sin_port; setrhostname(&SS); /* Lets figure-out who we are this time around -- we may be on a machine with multiple identities per multiple interfaces, or via virtual IP-numbers, or ... */ localsocksize = sizeof(SS.localsock); if (getsockname(FILENO(stdin), (struct sockaddr *) &SS.localsock, &localsocksize) != 0) { /* XX: ERROR! */ } zopenlog("smtpserver", LOG_PID, LOG_MAIL); if (SS.netconnected_flg) s_setup(&SS, FILENO(stdin), FILENO(stdin)); else s_setup(&SS, FILENO(stdin), FILENO(stdout)); if (ident_flag != 0 && !daemon_flg) setrfc1413ident(&SS); else strcpy(SS.ident_username, "IDENT-NOT-QUERIED"); sprintf(SS.ident_username + strlen(SS.ident_username), " [port %d]", SS.rport); if (smtp_syslog && ident_flag) { zsyslog((LOG_INFO, "connection from %s@%s\n", SS.ident_username, SS.rhostname)); } pid = getpid(); settrusteduser(); /* dig out the trusted user ID */ openlogfp(&SS, daemon_flg); type(NULL,0,NULL,"connection from %s:%d ident: %s", SS.rhostname, SS.rport, SS.ident_username); #if 0 SIGNAL_HANDLE(SIGCHLD, SIG_DFL); #else SIGNAL_HANDLE(SIGCHLD, reaper); #endif SIGNAL_HANDLE(SIGALRM, timedout); SIGNAL_HANDLE(SIGHUP, SIG_IGN); SIGNAL_HANDLE(SIGTERM, SIG_DFL); smtpserver(&SS, 1); } else { /* Not from under the inetd -- standalone server */ int j, once; int childpid, sameipcount, childcnt; int listensocks_count = 0; int *listensocks = malloc( 3 * sizeof(int) ); int *listensocks_types = malloc( 3 * sizeof(int) ); int msgfd; if (postoffice == NULL && (postoffice = getzenv("POSTOFFICE")) == NULL) postoffice = POSTOFFICE; if (pidfile_set || (!bindport_set && !bindaddr_set)) { /* Kill possible previous smtpservers now! */ if (killprevious(SIGTERM, pidfile) != 0) { fprintf(stderr, "%s: Can't write my pidfile! Disk full ?\n", progname); exit(2); } fflush(stdout); fflush(stderr); } if (daemon_flg) { /* Daemon attaches the SHM block, and may complain, but will not give up.. instead uses builtin fallback */ int r = Z_SHM_MIB_Attach (1); /* R/W mode */ if (r < 0) { /* Error processing -- magic set of constants: */ switch (r) { case -1: /* fprintf(stderr, "No ZENV variable: SNMPSHAREDFILE\n"); */ break; case -2: perror("Failed to open for exclusively creating of the SHMSHAREDFILE"); break; case -3: perror("Failure during creation fill of SGMSHAREDFILE"); break; case -4: perror("Failed to open the SHMSHAREDFILE at all"); break; case -5: perror("The SHMSHAREDFILE isn't of proper size! "); break; case -6: perror("Failed to mmap() of SHMSHAREDFILE into memory"); break; case -7: fprintf(stderr, "The SHMSHAREDFILE has magic value mismatch!\n"); break; default: break; } /* return; NO giving up! */ } } settrusteduser(); /* dig out the trusted user ID */ zcloselog(); /* close the syslog too.. */ detach(); /* this must NOT close fd's */ /* Close fd's 0, 1, 2 now */ close(0); close(1); close(2); open("/dev/null", O_RDWR, 0); dup(0); dup(0); /* fd's 0, 1, 2 are in use again.. */ if (pidfile_set || (!bindport_set && !bindaddr_set)) { sleep(3); /* Give a moment to possible previous server to die away... */ killprevious(0, pidfile); /* deposit pid */ } if (bindport <= 0) { struct servent *service; #ifdef IPPORT_SMTP bindport = IPPORT_SMTP; #endif /* !IPPORT_SMTP */ service = getservbyname("smtp", "tcp"); if (service == NULL) { fprintf(stderr, "%s: no SMTP service entry, using default\n", progname); } else bindport = ntohs(service->s_port); } once = 1; for (j = 0; j < bindaddrs_count || once; ++j) { once = 0; create_server_socket( & listensocks_count, & listensocks, & listensocks_types, use_ipv6, bindport, bindaddrs ? &bindaddrs[i] : NULL); if (ssmtp_listen) create_server_socket( & listensocks_count, & listensocks, & listensocks_types, use_ipv6, 465, /* Deprecated SMTP/TLS WKS port */ bindaddrs ? &bindaddrs[i] : NULL); } MIBMtaEntry->sys.SmtpServerMasterPID = getpid(); MIBMtaEntry->sys.SmtpServerMasterStartTime = time(NULL); MIBMtaEntry->sys.SmtpServerMasterStarts += 1; MIBMtaEntry->ss.IncomingSMTPSERVERprocesses = 1; /* myself at first */ MIBMtaEntry->ss.IncomingParallelSMTPconnects = 0; MIBMtaEntry->ss.IncomingParallelSMTPSconnects = 0; MIBMtaEntry->ss.IncomingParallelSUBMITconnects = 0; /* MIBMtaEntry->ss.IncomingParallelLMTPconnects = 0; */ #if 1 pid = getpid(); openlogfp(&SS, daemon_flg); if (logfp != NULL) { char *cp, *ssmtps = ""; char *tt; #if 0 if (ssmtp >= 0) ssmtps = " including deprecated SMTP/TLS port TCP/465"; #endif time(&now); cp = rfc822date(&now); tt = strchr(cp, '\n'); if (tt) *tt = 0; zsyslog((LOG_INFO, "server started.")); fprintf(logfp, "00000000000#\tstarted server pid %d at %s%s\n", pid, cp, ssmtps); /*fprintf(logfp,"00000000000#\tfileno(logfp) = %d",fileno(logfp)); */ fclose(logfp); logfp = NULL; } #endif #if 0 SIGNAL_HANDLE(SIGCHLD, SIG_DFL); #else SIGNAL_HANDLE(SIGCHLD, reaper); #endif SIGNAL_HANDLE(SIGALRM, timedout); SIGNAL_HANDLE(SIGHUP, SIG_IGN); SIGNAL_HANDLE(SIGTERM, sigterminator); while (!mustexit) { fd_set rdset; int n; int socktag; _Z_FD_ZERO(rdset); n = 0; for (i = 0; i < listensocks_count; ++i) { _Z_FD_SET(listensocks[i],rdset); if (n < listensocks[i]) n = listensocks[i]; } ++n; n = select(n, &rdset, NULL, NULL, NULL); if (n == 0) /* Timeout can't really happen here.. */ continue; if (n < 0) { /* various interrupts can happen here.. */ if (errno == EBADF || errno == EINVAL) break; if (errno == ENOMEM) sleep(1); /* Wait a moment, then try again */ continue; } /* Ok, here the select() has reported that we have something appearing in the listening socket(s). We are simple, and try them in order.. */ for (i = 0; i < listensocks_count; ++i) { if (_Z_FD_ISSET(listensocks[i],rdset)) { n = listensocks[i]; socktag = listensocks_types[i]; raddrlen = sizeof(SS.raddr); msgfd = accept(n, (struct sockaddr *) &SS.raddr, &raddrlen); if (msgfd < 0) { int err = errno; switch (err) { case EINTR: /* very common.. */ continue; #if 0 case ECONNRESET: /* seen to happen! */ case ECONNABORTED: case ENETUNREACH: case ENETRESET: case ETIMEDOUT: /* unlikely.. */ case ECONNREFUSED: /* unlikely.. */ case EHOSTDOWN: case EHOSTUNREACH: case ECHILD: /* Seen Solaris to do this! */ #ifdef ENOSR case ENOSR: #endif #ifdef EPROTO case EPROTO: #endif continue; #endif default: break; } /* Ok, all the WEIRD errors, continue life after logging, NO exit(1) we used to do... */ time(&now); fprintf(stderr, "%s: accept(): %s; %s", progname, strerror(err), rfc822date(&now)); openlogfp(&SS, daemon_flg); zsyslog((LOG_INFO, "accept() error=%d (%s)", err, strerror(err))); if (logfp) { fprintf(logfp, "0000000000#\taccept(): %s; %s", strerror(err), (char *) rfc822date(&now)); fclose(logfp); logfp = NULL; } continue; } switch (socktag) { case LSOCKTYPE_SMTP: MIBMtaEntry->ss.IncomingSMTPconnects += 1; break; case LSOCKTYPE_SSMTP: MIBMtaEntry->ss.IncomingSMTPSconnects += 1; break; case LSOCKTYPE_SUBMIT: MIBMtaEntry->ss.IncomingSUBMITconnects += 1; break; default: break; } sameipcount = childsameip(&SS.raddr, &childcnt); SS.sameipcount = sameipcount; /* We query, and warn the remote when the count exceeds the limit, and we simply -- and FAST -- reject the remote when it exceeds 4 times the limit */ if (sameipcount > 4 * MaxSameIpSource) { close(msgfd); MIBMtaEntry->ss.MaxSameIpSourceCloses ++; continue; } if (childcnt > 100+MaxParallelConnections) { close(msgfd); MIBMtaEntry->ss.MaxParallelConnections ++; continue; } SIGNAL_HOLD(SIGCHLD); if ((childpid = fork()) < 0) { /* can't fork! */ close(msgfd); MIBMtaEntry->ss.ForkFailures ++; fprintf(stderr, "%s: fork(): %s\n", progname, strerror(errno)); sleep(5); continue; } else if (childpid > 0) { /* Parent! */ childregister(childpid, &SS.raddr, socktag); reaper(0); SIGNAL_RELEASE(SIGCHLD); close(msgfd); /* Child has it, close at parent */ } else { /* Child */ SIGNAL_RELEASE(SIGCHLD); SS.netconnected_flg = 1; switch (socktag) { case LSOCKTYPE_SMTP: break; case LSOCKTYPE_SSMTP: ssmtp_connected = 1; break; case LSOCKTYPE_SUBMIT: submit_connected = 1; break; default: break; } for (i = 0; i < listensocks_count; ++i) close(listensocks[i]); /* Close listening sockets */ pid = getpid(); if (msgfd != 0) dup2(msgfd, 0); dup2(0, 1); if (msgfd > 1) close(msgfd); msgfd = 0; if (logfp) /* Open the logfp later.. */ fclose(logfp); logfp = NULL; #if 0 if (maxloadavg != 999 && maxloadavg < loadavg_current()) { write(msgfd, msg_toohighload, strlen(msg_toohighload)); sleep(2); exit(1); } #endif SIGNAL_HANDLE(SIGTERM, SIG_IGN); #if defined(AF_INET6) && defined(INET6) if (SS.raddr.v6.sin6_family == AF_INET6) SS.rport = SS.raddr.v6.sin6_port; else #endif SS.rport = SS.raddr.v4.sin_port; setrhostname(&SS); /* Lets figure-out who we are this time around -- we may be on a machine with multiple identities per multiple interfaces, or via virtual IP-numbers, or ... */ localsocksize = sizeof(SS.localsock); if (getsockname(msgfd, (struct sockaddr *) &SS.localsock, &localsocksize) != 0) { /* XX: ERROR! */ } zopenlog("smtpserver", LOG_PID, LOG_MAIL); s_setup(&SS, msgfd, msgfd); if (ident_flag != 0) setrfc1413ident(&SS); else strcpy(SS.ident_username, "IDENT-NOT-QUERIED"); #ifdef HAVE_WHOSON_H if (do_whoson && SS.netconnected_flg) { char buf[64]; buf[0]='\0'; if (SS.raddr.v4.sin_family == AF_INET) { inet_ntop(AF_INET, (void *) &SS.raddr.v4.sin_addr, /* IPv4 */ buf, sizeof(buf) - 1); #if defined(AF_INET6) && defined(INET6) } else if (SS.raddr.v6.sin6_family == AF_INET6) { inet_ntop(AF_INET6, (void *) &SS.raddr.v6.sin6_addr, /* IPv6 */ buf, sizeof(buf) - 1); #endif } if ((SS.whoson_result = wso_query(buf, SS.whoson_data, sizeof(SS.whoson_data)))) { strcpy(SS.whoson_data,"-unregistered-"); } } else { strcpy(SS.whoson_data,"NOT-CHECKED"); SS.whoson_result = -1; } #endif /* HAVE_WHOSON_H */ if (smtp_syslog && ident_flag) { #ifdef HAVE_WHOSON_H zsyslog((LOG_INFO, "connection from %s@%s (whoson: %s)\n", SS.ident_username, SS.rhostname, SS.whoson_data)); #else /* WHOSON */ zsyslog((LOG_INFO, "connection from %s@%s\n", SS.ident_username, SS.rhostname)); #endif } pid = getpid(); openlogfp(&SS, daemon_flg); #ifdef HAVE_WHOSON_H type(NULL,0,NULL, "connection from %s %s:%d ipcnt %d childs %d ident: %s whoson: %s", SS.rhostname, SS.ihostaddr,SS.rport, sameipcount, childcnt, SS.ident_username, SS.whoson_data); #else type(NULL,0,NULL, "connection from %s %s:%d ipcnt %d childs %d ident: %s", SS.rhostname, SS.ihostaddr,SS.rport, sameipcount, childcnt, SS.ident_username); #endif /* if (logfp) type(NULL,0,NULL,"Input fd=%d",getpid(),msgfd); */ if (childcnt > MaxParallelConnections) { type(&SS, -450, NULL, "Too many simultaneous connections to this server (%d max %d)", childcnt, MaxParallelConnections); type(&SS, 450, NULL, "Come again later"); typeflush(&SS); MIBMtaEntry->ss.MaxParallelConnections ++; close(0); close(1); close(2); #if 1 sleep(2); /* Not so fast! We need to do this to avoid (as much as possible) the child to exit before the parent has called childregister() -- not so easy to be 100% reliable (this isn't!) :-( */ #endif exit(0); /* Now exit.. */ } if (sameipcount > MaxSameIpSource && sameipcount > 1) { type(&SS, -450, NULL, "Too many simultaneous connections from same IP address (%d max %d)", sameipcount, MaxSameIpSource); type(&SS, 450, NULL, "Come again later"); typeflush(&SS); MIBMtaEntry->ss.MaxSameIpSourceCloses ++; close(0); close(1); close(2); #if 1 sleep(2); /* Not so fast! We need to do this to avoid (as much as possible) the child to exit before the parent has called childregister() -- not so easy to be 100% reliable (this isn't!) :-( */ #endif exit(0); /* Now exit.. */ } smtpserver(&SS, 1); /* Expediated filehandle closes before the mandatory sleep(2) below. */ close(0); close(1); close(2); if (routerpid > 0) killr(&SS, routerpid); if (contentpolicypid > 1) killcfilter(&SS, contentpolicypid); if (SS.netconnected_flg) sleep(2); _exit(0); } /* .. end of child code */ } /* .. acceptance ok */ } /* .. listensocks */ } /* .. while (!mustexit) */ /* Stand-alone server, kill the pidfile at the exit! */ killpidfile(pidfile); openlogfp(&SS, daemon_flg); zsyslog((LOG_INFO, "killed server.")); if (logfp != NULL) { char *cp; time(&now); cp = rfc822date(&now); fprintf(logfp, "00000000000#\tkilled server pid %d at %s", pid, cp); fclose(logfp); logfp = NULL; } } /* stand-alone server */ if (routerpid > 0) killr(&SS, routerpid); if (contentpolicypid > 1) killcfilter(&SS, contentpolicypid); if (SS.netconnected_flg) sleep(2); exit(0); /* NOTREACHED */ return 0; } #ifdef CHECK42INETD /* * The 4.2 BSD inetd runs its servers with exactly one argument having * the form: * xxxxxxxx.dddd * * where xxxxxxxxx is the remote IP host address in hexadecimal and * dddd is the remote port number in decimal. While we don't use these * (the host has to support getpeername()), this routine checks for * the correct form of the argument. */ int isit42inetd(arg) char *arg; { register int i; for (i = 0; i < 8; i++) /* exactly 8 hex digits */ if (!isxdigit(arg[i])) return 0; if (arg[8] != '.') /* period next */ return 0; for (i = 9; arg[i] != '\0'; i++) /* now one or more decimal digits */ if (!isdigit(arg[i])) return 0; if (i == 9) return 0; return 1; /* okay! */ } #endif /* CHECK42INETD */ /* * set the (default) remote host name, possibly based on the remote IP * host address if we are feeling untrusting. */ static void setrhostname(SS) SmtpState *SS; { struct hostent *hp = NULL; if (SS->raddr.v4.sin_family == AF_INET) inet_ntop(AF_INET, (void *) &SS->raddr.v4.sin_addr, /* IPv4 */ SS->ihostaddr + 1, sizeof(SS->ihostaddr) - 2); #if defined(AF_INET6) && defined(INET6) else if (SS->raddr.v6.sin6_family == AF_INET6) { strcpy(SS->ihostaddr + 1, "IPv6:"); inet_ntop(AF_INET6, (void *) &SS->raddr.v6.sin6_addr, /* IPv6 */ SS->ihostaddr + 6, sizeof(SS->ihostaddr) - 7); } #endif else { ; /* XX: ??? Not AF_INET, nor AF_INET6 ??? */ } SS->ihostaddr[0] = '['; sprintf(SS->ihostaddr + strlen(SS->ihostaddr), "]"); if (skeptical) { if (SS->raddr.v4.sin_family == AF_INET) hp = gethostbyaddr((char *) &SS->raddr.v4.sin_addr, 4, AF_INET); #if defined(AF_INET6) && defined(INET6) else if (SS->raddr.v6.sin6_family == AF_INET6) { struct in6_addr *ip6 = &SS->raddr.v6.sin6_addr; /* If it is IPv4 mapped address to IPv6, then resolve the IPv4 address... */ if (memcmp((void *) ip6, zv4mapprefix, 12) == 0) hp = gethostbyaddr(((char *) ip6) + 12, 4, AF_INET); else hp = gethostbyaddr((char *) ip6, 16, AF_INET6); } #endif else { ; /* XX: ??? Not AF_INET, nor AF_INET6 ??? */ } if (hp != NULL) strcpy(SS->rhostname, hp->h_name); else strcpy(SS->rhostname, SS->ihostaddr); } else { strcpy(SS->rhostname, SS->ihostaddr); } } static RETSIGTYPE timedout(sig) int sig; { /* Return to the smtpserver's main-program. We are commiting a suicide, but we need data that exists only in that context... */ gotalarm = 1; mustexit = 1; siglongjmp(jmpalarm, 1); _exit(253); /* We did return ?!?! Boo!! */ } static RETSIGTYPE sigterminator(sig) int sig; { SIGNAL_HANDLE(sig, sigterminator); mustexit = 1; } static RETSIGTYPE reaper(sig) int sig; { int status; int lpid; #ifdef HAVE_WAITPID while ((lpid = waitpid(-1, &status, WNOHANG)) > 0) #else #ifdef HAVE_WAIT4 while ((lpid = wait4(-1, &status, WNOHANG, (struct rusage *) NULL)) > 0) #else #ifdef HAVE_WAIT3 while ((lpid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) #else /* ... plain simple waiting wait() ... */ /* This can freeze at wait() ? Who could test ? A system without wait3()/waitpid(), but with BSD networking ??? */ while ((lpid = wait(&status)) > 0) #endif /* WNOHANG */ #endif #endif { if (lpid == routerpid && routerpid > 0) { router_status = status; routerpid = -1; } if (lpid == contentpolicypid && contentpolicypid > 1) { contentpolicypid = -lpid; } if (lpid == pipeauthchild_pid && lpid > 0) { pipeauthchild_status = status; pipeauthchild_pid = -1; } childreap(lpid); } SIGNAL_HANDLE(SIGCHLD, reaper); } void reporterr(SS, tell, msg) SmtpState *SS; const long tell; const char *msg; { time( & now ); zsyslog((LOG_ERR, "%s%04d - aborted (%ld bytes) from %s/%d: %s", logtag, (int)(now-logtagepoch), tell, SS->rhostname, SS->rport, msg)); if (logfp != NULL && SS->tarpit > tarpit_initial ) { char *ts = rfc822date(&now); fprintf(logfp, "%s#\ttar_pit with delay %04d ends at %s", logtag, SS->tarpit_cval, ts ); fflush(logfp); } if (logfp != NULL) { fprintf(logfp, "%s%04d-\taborted (%ld bytes): %s\n", logtag, (int)(now-logtagepoch), tell, msg); fflush(logfp); } } int Z_write(SS, ptr, len) SmtpState * SS; const void *ptr; int len; { int i, rc = 0; char *buf = (char *)ptr; while (len > 0) { i = SS->sslwrspace - SS->sslwrin; /* space */ if (i == 0) { /* The buffer is full! Flush it */ typeflush(SS); if (gotalarm) break; i = SS->sslwrspace; } /* Copy only as much as can fit into current space */ if (i > len) i = len; memcpy(SS->sslwrbuf + SS->sslwrin, buf, i); SS->sslwrin += i; buf += i; len -= i; rc += i; } /* how much written out ? */ return rc; } void typeflush(SS) SmtpState *SS; { int len = SS->sslwrin - SS->sslwrout; int rc; time_t expiry_epoch; time(&expiry_epoch); expiry_epoch += SMTP_REPLY_ALARM_IVAL; while (len > 0) { #ifdef HAVE_OPENSSL if (SS->sslmode) rc = Z_SSL_flush(SS); else #endif /* - HAVE_OPENSSL */ rc = write(SS->outputfd, SS->sslwrbuf + SS->sslwrout, len); if (rc > 0) { len -= rc; SS->sslwrout += rc; continue; } if (rc < 0 && (errno == EAGAIN || errno == EINTR)) { /* Wait for write-space, or timeout! */ fd_set wrset; fd_set rdset; struct timeval tv; time_t now; int fd = SS->outputfd; _Z_FD_ZERO(rdset); _Z_FD_ZERO(wrset); time(&now); if (expiry_epoch <= now) tv.tv_sec = 1; else tv.tv_sec = expiry_epoch - now; tv.tv_usec = 0; if (rc == -1) _Z_FD_SET(fd, wrset); else _Z_FD_SET(SS->inputfd, rdset); /* SSL Want Read! */ if (SS->inputfd > fd) fd = SS->inputfd; rc = select(fd+1, &rdset, &wrset, NULL, &tv); if (rc == 0) { /* TIMEOUT! */ gotalarm = 1; SS->s_status = EOF; break; } /* rc < 0 --> redo.. */ /* rc > 0 --> have write-space! */ } else break; } /* ... while() */ /* Even with errors -- we have 'gotalarm' and s_status set so that the connection should abort on spot... */ SS->sslwrout = SS->sslwrin = 0; /* Buffer done */ } #ifndef HAVE_OPENSSL int Z_read(SS, ptr, len) SmtpState * SS; void *ptr; int len; { return read(SS->inputfd, ptr, len); } int Z_pending(SS) SmtpState * SS; { return 0; } #endif /* --HAVE_OPENSSL */ /* Support routine: Our own buffering for stdinput */ int s_feof(SS) SmtpState *SS; { return SS->s_status; } int s_getc(SS, timeout_is_fatal) SmtpState *SS; int timeout_is_fatal; { int rc = 0; if (SS->s_ungetcbuf >= 0) { rc = SS->s_ungetcbuf; SS->s_ungetcbuf = -1; return rc; } if (SS->s_status) return SS->s_status; if (SS->s_readout >= SS->s_bufread) { time_t expiry_epoch = time(NULL) + SS->read_alarm_ival; redo: /* We are about to read... */ if (rc < 0 && SS->inputfd >= 0) { fd_set rdset; fd_set wrset; struct timeval tv; time_t now; int fd = SS->inputfd; _Z_FD_ZERO(rdset); _Z_FD_ZERO(wrset); time(&now); if (expiry_epoch <= now) tv.tv_sec = 1; else tv.tv_sec = expiry_epoch - now; tv.tv_usec = 0; if (rc == -2) /* SSL Want Write ! */ _Z_FD_SET(SS->outputfd, wrset); else _Z_FD_SET(SS->inputfd, rdset); if (SS->outputfd > fd) fd = SS->outputfd; rc = select(fd+1, &rdset, &wrset, NULL, &tv); if (rc == 0) { /* TIMEOUT! */ if (timeout_is_fatal) { gotalarm = 1; SS->s_status = EOF; } return EOF; } /* rc < 0 ??? */ /* rc > 0 --> we have something to read! */ } rc = Z_read(SS, SS->s_buffer, sizeof(SS->s_buffer)); SS->s_readerrno = 0; if (rc < 0) { SS->s_readerrno = errno; if (errno == EINTR || errno == EAGAIN) { goto redo; } /* The read returned.. */ /* Other results are serious errors -- maybe */ SS->s_status = EOF; return EOF; } /* We did read successfully! */ if (rc == 0) { SS->s_status = EOF; return EOF; } SS->s_bufread = rc; SS->s_readout = 0; } /* if (rc) return EOF; XXX: Hmm.. what this was supposed to be ? */ return ((SS->s_buffer)[SS->s_readout++]) & 0xFF; } int s_hasinput(SS) SmtpState *SS; { int i = Z_pending(SS); if (i) return i; if (SS->s_readout >= SS->s_bufread) { /* So if it did dry up, try non-blocking read */ SS->s_readout = 0; SS->s_bufread = Z_read(SS, SS->s_buffer, sizeof(SS->s_buffer)); if (SS->s_bufread > 0) return SS->s_bufread; return 0; } return (SS->s_bufread - SS->s_readout); } void s_ungetc(SS, ch) SmtpState *SS; int ch; { SS->s_ungetcbuf = ch; } int s_gets(SS, buf, buflen, rcp, cop, cp) SmtpState *SS; char *buf, *cop, *cp; int buflen, *rcp; { int c, co = -1; int i = -1, rc = -1; if (!pipeliningok || !s_hasinput(SS)) typeflush(SS); /* Alarm processing on the SMTP protocol channel */ SS->read_alarm_ival = SMTP_COMMAND_ALARM_IVAL; /* Our own fgets() -- gets also NULs, flags illegals.. */ --buflen; while ((c = s_getc(SS, 1)) != EOF && i < buflen) { if (c == '\n') { buf[++i] = c; break; } else if (co == '\r' && rc < 0) rc = i; /* Spurious CR on the input.. */ if (c == '\0' && rc < 0) rc = i; if ((c & 0x80) != 0 && rc < 0) rc = i; if (c != '\r' && c != '\t' && (c < 32 || c == 127) && rc < 0) rc = i; buf[++i] = c; co = c; } buf[++i] = '\0'; if (c == EOF && i == 0) { /* XX: ??? Uh, Hung up on us ? */ if (SS->mfp != NULL) mail_abort(SS->mfp); SS->mfp = NULL; } /* Zap the ending newline */ if (c == '\n') buf[i-1] = '\0'; /* Zap the possible preceeding \r */ if (co == '\r') buf[i-2] = '\0'; *cop = co; *cp = c; *rcp = rc; if (i >= buflen && c != EOF && c != '\n') { /* Huh, oversized input line ?? */ while ((c = s_getc(SS, 1)) != EOF && c != '\n') ; /* Input eaten until a NEWLINE, or EOF occurred at the input. */ } return i; } void s_setup(SS, infd, outfd) SmtpState *SS; int infd; { SS->inputfd = infd; SS->outputfd = outfd; SS->s_status = 0; SS->s_bufread = -1; SS->s_ungetcbuf = -1; SS->s_readout = 0; /* Actually all modes use this write-out buffer */ SS->sslwrbuf = emalloc(8192); SS->sslwrspace = 8192; SS->sslwrin = SS->sslwrout = 0; fd_nonblockingmode(infd); fd_nonblockingmode(outfd); } /* The SMTP-server itself */ static void smtpserver(SS, insecure) SmtpState *SS; int insecure; { char *cp; time_t now; int rc; long tell; int policystatus; struct hostent *hostent; int localport; long maxsameip; #ifdef USE_TRANSLATION char lang[4]; lang[0] = '\0'; #endif SS->VerboseCommand = 0; SS->tarpit = tarpit_initial; SS->tarpit_cval = tarpit_cval; stashmyaddresses(NULL); pid = getpid(); if (!logfp) openlogfp(SS, insecure); runastrusteduser(); if (!SS->netconnected_flg) strict_protocol = 0; fd_nonblockingmode(SS->inputfd); /* redundant ? */ fd_nonblockingmode(SS->outputfd); /* redundant ? */ rc = sigsetjmp(jmpalarm,1); if (rc != 0) { /* Oooo... We are returning here via longjmp(), which means we just got a timeout (SIGALRM), which for us is instant death.. */ tell = 0; if (SS->mfp != NULL) { fseek(SS->mfp, 0, SEEK_END); tell = ftell(SS->mfp); } { char msgbuf[40]; sprintf(msgbuf,"SMTP protocol timed out (%d sec)", SS->read_alarm_ival); reporterr(SS, tell, msgbuf); } /* If there is something going on, kill the file.. */ if (SS->mfp != NULL) { if (STYLE(SS->cfinfo,'D')) { /* Says: DON'T DISCARD -- aka DEBUG ERRORS! */ mail_close_alternate(SS->mfp,"public",".SMTP-TIMEOUT"); } else mail_abort(SS->mfp); } SS->mfp = NULL; if (routerpid > 0) killr(SS, routerpid); if (contentpolicypid > 1) killcfilter(SS, contentpolicypid); exit(0); } report(SS, "(connected)"); now = time((time_t *) 0); cp = (char *) rfc822date(&now); if (*(cp + strlen(cp) - 1) == '\n') { *(cp + strlen(cp) - 1) = '\0'; } #if defined(AF_INET6) && defined(INET6) if (SS->localsock.v6.sin6_family == AF_INET6) { struct in6_addr *ip6 = &SS->localsock.v6.sin6_addr; localport = ntohs(SS->localsock.v6.sin6_port); /* If it is IPv4 mapped address to IPv6, then resolve the IPv4 address... */ if (memcmp((void *) ip6, zv4mapprefix, 12) == 0) hostent = gethostbyaddr(((char *) ip6) + 12, 4, AF_INET); else hostent = gethostbyaddr((char *) ip6, 16, AF_INET6); } else #endif { localport = ntohs(SS->localsock.v4.sin_port); hostent = gethostbyaddr((void *) &SS->localsock.v4.sin_addr, 4, AF_INET); } if (hostent) { strcpy(SS->myhostname, hostent->h_name); #ifdef USE_TRANSLATION strncpy(lang, hostent->h_name, 3); lang[3] = '\0'; X_settrrc = settrtab_byname(lang); } if (!(*lang) || (X_settrrc < 0)) { #if 0 /* The SS state isn't fully initialized now, can't use type() yet! Eugene Crosser Eh, really ? [mea] */ /* we don't know our codetable, hush client away */ type(SS, 451, NULL, "Server could not setup translation.", NULL); typeflush(SS); #endif sleep(2); exit(0); #endif /* USE_TRANSLATION */ } smtpauth_init(SS); if (localport != 25 && detect_incorrect_tls_use) { int c; int aval = SS->read_alarm_ival; SS->read_alarm_ival = 2; c = s_getc(SS, 0); SS->read_alarm_ival = aval; if (c >= 0) { s_ungetc(SS, c); #ifdef HAVE_OPENSSL /* THIS IS KLUDGE!!! Microsoft Outlook sExpress (not sure if it was that, and not one of those other Outlooks...) has a nasty misfunction of starting the TLS right away when the destination port at the server is not 25, and "USE TLS" flag is set... */ if (c == 0x80) { ssmtp_connected = 1; } #endif /* - HAVE_OPENSSL */ } } #ifdef HAVE_OPENSSL if (ssmtp_connected) { if (tls_start_servertls(SS)) { /* No dice... */ exit(2); } } #endif /* - HAVE_OPENSSL */ #ifdef HAVE_WHOSON_H policystatus = policyinit(&policydb, &SS->policystate, SS->whoson_result); #else policystatus = policyinit(&policydb, &SS->policystate, 0); #endif if (!SS->netconnected_flg) { policystatus = 0; /* For internal - non-net-connected - mode lack of PolicyDB is no problem at all.. */ SS->reject_net = 0; } else { if (debug) typeflush(SS); SS->policyresult = policytestaddr(policydb, &SS->policystate, POLICY_SOURCEADDR, (void *) &SS->raddr); SS->reject_net = (SS->policyresult < 0); maxsameip = policysameiplimit(policydb, &SS->policystate); if (maxsameip == 0 && SS->netconnected_flg) SS->reject_net = 1; /* count=0 equivalent to reject */ if (debug) typeflush(SS); if (SS->policyresult == 0) /* Alternates to this condition are: Always reject, or Always freeze.. */ SS->policyresult = policytest(policydb, &SS->policystate, POLICY_SOURCEDOMAIN, SS->rhostname,strlen(SS->rhostname), SS->authuser); } /* re-opening the log ?? */ zopenlog("smtpserver", LOG_PID, LOG_MAIL); #ifdef USE_TCPWRAPPER #ifdef HAVE_TCPD_H /* TCP-Wrapper code */ if (use_tcpwrapper && SS->netconnected_flg && wantconn(SS->inputfd, "smtp-receiver") == 0) { zsyslog((LOG_WARNING, "refusing connection from %s:%d/%s", SS->rhostname, SS->rport, SS->ident_username)); type(SS, 421, NULL, "%s ZMailer Server %s WILL NOT TALK TO YOU at %s", SS->myhostname, VersionNumb, cp); typeflush(SS); sleep(2); exit(0); } #endif #endif if (SS->reject_net) { char *msg = policymsg(policydb, &SS->policystate); smtp_tarpit(SS); if (msg != NULL) { type(SS, -550, NULL, "%s", msg); } else { type(SS, -550, NULL, "%s - You are on our reject-IP-address -list, GO AWAY!", SS->myhostname); } type(SS, -550, NULL, "If you feel we mistreat you, do contact us."); type(SS, 550, NULL, "Ask HELP for our contact information."); } else if ((maxsameip >= 0) && (SS->sameipcount > maxsameip)) { smtp_tarpit(SS); type(SS, -450, NULL, "%s - Too many simultaneous connections from this IP address (%li of %li)", SS->myhostname, SS->sameipcount, maxsameip); type(SS, 450, NULL, "Come again later"); MIBMtaEntry->ss.MaxSameIpSourceCloses ++; } else #ifdef USE_TRANSLATION if (hdr220lines[0] == NULL) { hdr220lines[0] = "%H ZMailer Server %V ESMTP%I (%X) ready at %T"; } type220headers(SS, ident_flag, X_settrrc ? "nulltrans" : lang, cp); #if 0 type(SS, 220, NULL, "%s ZMailer Server %s ESMTP%s (%s) ready at %s", SS->myhostname, VersionNumb, ident_flag ? "+IDENT" : "", X_settrrc ? "nulltrans" : lang, cp); #endif #else /* USE_TRANSLATION */ if (hdr220lines[0] == NULL) { hdr220lines[0] = "%H ZMailer Server %V ESMTP%I ready at %T"; } type220headers(SS, ident_flag, "", cp); #if 0 type(SS, 220, NULL, "%s ZMailer Server %s ESMTP%s ready at %s", SS->myhostname, VersionNumb, ident_flag ? "+IDENT" : "", cp); #endif #endif /* USE_TRANSLATION */ typeflush(SS); SS->state = Hello; if ((!insecure || (SS->ihostaddr[0] != '\0' && strcmp(SS->ihostaddr, "[127.0.0.1]") == 0)) && ((cfinfo = findcf("127.0.0.1")) == NULL || strcmp(cfinfo->flags, "-") == 0)) SS->state = MailOrHello; cfinfo = NULL; { char *s = policymsg(policydb, &SS->policystate); if (SS->policyresult != 0 || s != NULL) type(NULL,0,NULL,"-- policyresult=%d initial policy msg: %s", SS->policyresult, (s ? s : "")); if (logfp) fflush(logfp); } while (1) { char buf[SMTPLINESIZE]; /* limits size of SMTP commands... On the other hand, limit is asked to be only 1000 chars, not 8k.. */ char *eobuf; char c, co; int i; if (always_flush_replies) typeflush(SS); i = s_gets(SS, buf, sizeof(buf), &rc, &co, &c ); if (i <= 0) /* EOF ??? */ break; MIBMtaEntry->ss.IncomingCommands ++; time( & now ); if (s_hasinput(SS)) { if (logfp || logfp_to_syslog) type(NULL,0,NULL, "-- pipeline input exists %d bytes", s_hasinput(SS)); if (!SS->s_seen_pipeline) MIBMtaEntry->ss.IncomingClientPipelines ++; SS->s_seen_pipeline = 1; } eobuf = &buf[i-1]; /* Buf end ptr.. */ /* Chop the trailing spaces */ if (!strict_protocol) { while ((eobuf > buf) && (eobuf[-1] == ' ' || eobuf[-1] == '\t')) *--eobuf = '\0'; } else if (strict_protocol && eobuf > buf && (eobuf[-1] == ' ' || eobuf[-1] == '\t')) { /* XX: Warn about trailing whitespaces on inputs! ... except that this is likely *wrong* place, as there are many varying input syntaxes... */ } if (logfp_to_syslog) zsyslog((LOG_DEBUG, "%s%04d r %s", logtag, (int)(now-logtagepoch), buf)); if (logfp) { fprintf(logfp, "%s%04dr\t%s\n", logtag, (int)(now-logtagepoch), buf); fflush(logfp); } if (rc >= 0 && !strict_protocol) { if (CISTREQN(buf,"HELO",4) || CISTREQN(buf,"EHLO",4)) rc = -1; /* Sigh... Bloody windows users naming their machines with junky names, and M$ being its normal incompetent protocol cleaner... */ } if (rc >= 0) { rfc821_error_ptr = buf + rc; type821err(SS, 500, m552, buf, "Illegal input characters: %s", ((buf[rc] == '\0') ? "NUL on SMTP input" : ((buf[rc] & 0x80) ? "8-bit char on SMTP input" : "Control chars on SMTP input"))); typeflush(SS); MIBMtaEntry->ss.IncomingCommands_unknown ++; continue; } if (!strict_protocol && c != '\n' && i > 3) { /* Some bloody systems send: "QUIT\r", and then close the socket... */ if (CISTREQ(buf,"QUIT") == 0) { co = '\r'; c = '\n'; /* Be happy... */ } } if ((strict_protocol && (c != '\n' || co != '\r')) || (c != '\n')) { if (i < (sizeof(buf)-1)) type(SS, 500, m552, "Line not terminated with CRLF.."); else type(SS, 500, m552, "Line too long (%d chars)", i); MIBMtaEntry->ss.IncomingCommands_unknown ++; continue; } if (verbose && !daemon_flg) fprintf(stdout, "%s\n", buf); /* XX: trace.. */ report(SS, "%.100s", buf); for (cp = buf; (c = *cp) && isascii(c & 0xFF) && isalnum(c & 0xFF); ++cp) continue; if (cp > buf + 8) /* "DEBUG" is longest of them.. */ goto unknown_command; c = *cp; if (c != '\0') *cp = '\0'; for (SS->carp = &command_list[0]; SS->carp->verb != NULL; SS->carp += 1) { if (CISTREQ(SS->carp->verb, buf)) break; } *cp = c; if (SS->carp->verb == NULL) { unknown_command: MIBMtaEntry->ss.IncomingCommands_unknown ++; ++SS->unknown_cmd_count; if (SS->unknown_cmd_count >= unknown_cmd_limit) { type(SS, 550, m552, "One too many unknown command '%s'", buf); typeflush(SS); break; } smtp_tarpit(SS); type(SS, 550, m552, "Unknown command '%s'", buf); zsyslog((LOG_WARNING, "unknown SMTP command '%s' from %s/%d", buf, SS->rhostname, SS->rport)); typeflush(SS); continue; } /* RFC 2033 rules */ if (!lmtp_mode && SS->carp->cmd == HelloL) goto unknown_command; if (lmtp_mode && (SS->carp->cmd == Hello || SS->carp->cmd == Hello2)) goto unknown_command; if (SS->carp->cmd == DebugMode && ! debugcmdok) goto unknown_command; if (SS->carp->cmd == Expand && ! expncmdok) goto unknown_command; if (SS->carp->cmd == Verify && ! vrfycmdok) goto unknown_command; if (SS->carp->cmd == Verify2 && ! vrfycmdok) goto unknown_command; if (SS->carp->cmd == Hello2 && ! ehlo_ok) goto unknown_command; if (SS->carp->cmd == Turnme && ! etrn_ok) goto unknown_command; if (SS->carp->cmd == Auth && ! auth_ok) goto unknown_command; if (SS->carp->cmd == BData && ! chunkingok) goto unknown_command; /* Lack of configuration is problem only with network connections */ if (SS->netconnected_flg && !configuration_ok) { smtp_tarpit(SS); type(SS, -400, "4.7.0", "This SMTP server has not been configured!"); typeflush(SS); zsyslog((LOG_EMERG, "smtpserver configuration missing!")); sleep(20); continue; } if (policystatus != 0 && SS->carp->cmd != Quit && SS->carp->cmd != Help) { smtp_tarpit(SS); type(SS, -400, "4.7.0", "Policy database problem, code=%d", policystatus); type(SS, 400, "4.7.0", "With 'HELP' command you can get our contact information."); typeflush(SS); zsyslog((LOG_EMERG, "smtpserver policy database problem, code: %d", policystatus)); sleep(20); continue; } if (SS->reject_net && SS->carp->cmd != Quit && SS->carp->cmd != Help) { smtp_tarpit(SS); type(SS, -550, NULL, "You are on our reject-IP-address -list, GO AWAY!"); type(SS, -550, NULL, "If you feel we mistreat you, do contact us."); type(SS, 550, NULL, "With 'HELP' command you can get out contact information."); typeflush(SS); continue; } if ( msa_mode && SS->authuser == NULL && !(SS->policystate.always_accept || SS->policystate.full_trust) ) { switch (SS->carp->cmd) { #ifdef HAVE_OPENSSL case StartTLS: #endif /* - HAVE_OPENSSL */ case Auth: case Hello: case Hello2: case NoOp: case Reset: case Quit: break; default: type(SS, 530, m530, "Authentication required" ); typeflush(SS); continue; } } switch (SS->carp->cmd) { case Silent: /* We are SILENT - no response at all! */ break; case Null: type(SS, 550, m550, "panic!"); typeflush(SS); break; #ifdef HAVE_OPENSSL case StartTLS: #if 0 /* A debug thing.. */ always_flush_replies = 1; #endif smtp_starttls(SS, buf, cp); break; #endif /* - HAVE_OPENSSL */ case Hello: case Hello2: case HelloL: /* This code is LONG.. */ smtp_helo(SS, buf, cp); typeflush(SS); break; case Auth: smtp_auth(SS, buf, cp); typeflush(SS); break; case Mail: case Mail2: case Send: case Send2: case SendOrMail: case SendAndMail: /* This code is LONG.. */ MIBMtaEntry->ss.IncomingSMTP_MAIL += 1; if (smtp_mail(SS, buf, cp, insecure) != 0 || SS->mfp == NULL) MIBMtaEntry->ss.IncomingSMTP_MAIL_bad += 1; else MIBMtaEntry->ss.IncomingSMTP_MAIL_ok += 1; break; case Recipient: /* This code is LONG.. */ MIBMtaEntry->ss.IncomingSMTP_RCPT += 1; if (smtp_rcpt(SS, buf, cp) != 0 || SS->mfp == NULL) MIBMtaEntry->ss.IncomingSMTP_RCPT_bad += 1; else MIBMtaEntry->ss.IncomingSMTP_RCPT_ok += 1; break; case Data: if (smtp_data(SS, buf, cp) < 0) { #ifdef HAVE_OPENSSL Z_cleanup(SS); #endif /* - HAVE_OPENSSL */ return; } break; case BData: if (smtp_bdata(SS, buf, cp) < 0) { #ifdef HAVE_OPENSSL Z_cleanup(SS); #endif /* - HAVE_OPENSSL */ return; } break; case Reset: MIBMtaEntry->ss.IncomingSMTP_RSET ++; if (*cp != 0 && strict_protocol) { type(SS, 501, m554, "Extra junk after 'RSET' verb"); break; } if (SS->mfp != NULL) { clearerr(SS->mfp); mail_abort(SS->mfp); SS->mfp = NULL; } if (SS->state != Hello) SS->state = MailOrHello; type(SS, 250, m200, "Reset processed, now waiting for MAIL command"); SS->policyresult = 0; /* Clear this state too */ typeflush(SS); break; case Help: help(SS, cfinfo, cp); typeflush(SS); break; case Verify: case Verify2: smtp_verify(SS, buf, cp); typeflush(SS); break; case Expand: smtp_expand(SS, buf, cp); typeflush(SS); break; case Turnme: smtp_turnme(SS, SS->carp->verb, cp); typeflush(SS); break; case Turn: MIBMtaEntry->ss.IncomingSMTP_TURN ++; if (*cp != 0 && STYLE(SS->cfinfo,'R')) { type(SS, -502, m554, "Extra junk after 'TURN' verb"); } type(SS, 502, m551, (char *) NULL); typeflush(SS); break; case NoOp: MIBMtaEntry->ss.IncomingSMTP_NOOP ++; if (*cp != 0 && STYLE(SS->cfinfo,'R')) { type(SS, 501, m554, "Extra junk after 'NOOP' verb"); break; } type(SS, 250, m200, (char *) NULL); typeflush(SS); break; case Verbose: MIBMtaEntry->ss.IncomingSMTP_VERBOSE ++; type(SS, -250, m200, VerbID, Version); type(SS, -250, m200, Copyright); type(SS, 250, m200, Copyright2); typeflush(SS); SS->VerboseCommand = 1; break; case DebugMode: MIBMtaEntry->ss.IncomingSMTP_DEBUG ++; ++debug; debug_report(SS, SS->VerboseCommand, SS->rhostname, buf); typeflush(SS); break; case DebugIdent: setrfc1413ident(SS); type(SS, 200, "", "RFC1413 identuser='%s'", SS->ident_username); typeflush(SS); break; case Tick: MIBMtaEntry->ss.IncomingSMTP_TICK ++; type(SS, 250, m200, "%s", buf); typeflush(SS); SS->with_protocol = WITH_BSMTP; break; case Quit: MIBMtaEntry->ss.IncomingSMTP_QUIT ++; if (*cp != 0 && STYLE(SS->cfinfo,'R')) { type(SS, -221, m554, "Extra junk after 'QUIT' verb"); } if (SS->mfp != NULL) mail_abort(SS->mfp); SS->mfp = NULL; type(SS, 221, m200, NULL, "Out"); typeflush(SS); /* I want a log entry for when tarpit is complete - jmack Apr,2003 */ if (logfp != NULL && SS->tarpit > tarpit_initial ) { char *ts = rfc822date(&now); fprintf(logfp, "%s#\ttar_pit with delay %04d ends at %s", logtag, SS->tarpit_cval, ts ); fflush(logfp); } #ifdef HAVE_OPENSSL Z_cleanup(SS); #endif /* - HAVE_OPENSSL */ return; default: break; } } if (SS->mfp != NULL) { mail_abort(SS->mfp); SS->mfp = NULL; tell = lseek(0, 0, SEEK_CUR); reporterr(SS, tell, "session terminated"); } else if (logfp != NULL) { type(NULL,0,NULL,"Session closed w/o QUIT; read() errno=%d", SS->s_readerrno); fflush(logfp); } if (logfp != NULL && SS->tarpit > tarpit_initial ) { char *ts = rfc822date(&now); fprintf(logfp, "%s#\ttar_pit with delay %04d ends at %s", logtag, SS->tarpit_cval, ts ); fflush(logfp); } #ifdef HAVE_OPENSSL Z_cleanup(SS); #endif /* - HAVE_OPENSSL */ } #if 0 /* tmalloc() is in the library, isn't it ? */ univptr_t tmalloc(n) int n; { return emalloc((u_int) n); } #endif /* * In theory, this should modify the command that ps shows for this process. * This is known to not be portable, hopefully it will break badly on systems * where it doesn't work. */ void #ifdef HAVE_STDARG_H #ifdef __STDC__ report(SmtpState * SS, const char *cp,...) #else report(SS, cp) SmtpState *SS; const char *cp; #endif #else /* VARARGS */ report(va_alist) va_dcl #endif { va_list ap; char buf[8192], *s; int cmdlen; int bufspace; #ifdef HAVE_STDARG_H va_start(ap, cp); #else SmtpState *SS; const char *cp; va_start(ap); SS = va_arg(ap, SmtpState *); cp = va_arg(ap, const char *); #endif memset(buf, 0, sizeof(buf)); #ifdef HAVE_SNPRINTF snprintf(buf, sizeof(buf)-2, "<%s ", SS->rhostname); #else sprintf(buf, "<%s ", SS->rhostname); #endif s = buf + strlen(buf); bufspace = sizeof(buf) - (s - buf) - 2; #ifdef HAVE_VSPRINTF # ifdef HAVE_VSNPRINTF vsnprintf(s, bufspace, cp, ap); # else vsprintf(s, cp, ap); # endif #else /* !HAVE_VSPRINTF */ # ifdef HAVE_SNPRINTF sprintf(s, bufspace, cp, va_arg(ap, char *)); # else sprintf(s, cp, va_arg(ap, char *)); #endif #endif /* HAVE_VPRINTF */ #ifdef HAVE_SETPROCTITLE setproctitle("%s", buf); #else cmdlen = (eocmdline - cmdline); buf[sizeof(buf)-1] = '\0'; strncpy((char *) cmdline, buf, cmdlen+1); #endif /* HAVE_SETPROCTITLE */ va_end(ap); } #ifdef HAVE_VPRINTF #ifdef HAVE_STDARG_H void #ifdef __STDC__ type(SmtpState * SS, const int Code, const char *status, const char *fmt,...) #else /* Non ANSI-C */ type(SS, Code, status, fmt) SmtpState *SS; const int Code; const char *status, *fmt; #endif #else /* VARARGS2 */ void type(SS, Code, status, fmt, va_alist) SmtpState *SS; const int Code; const char *status, *fmt; va_dcl #endif #else /* No VPRINTF */ /* VARARGS2 */ void type(SS, Code, status, fmt, s1, s2, s3, s4, s5, s6) SmtpState *SS; const int Code; const char *status, *fmt, *s1, *s2, *s3, *s4, *s5, *s6; #endif { char format[256]; /* We limit the fill to 200+some */ const char *text = NULL; char c, *s; int code = Code, buflen; char buf[6000]; if (code <= 0) { code = -code; c = '-'; } else c = ' '; if (!SS) { sprintf(buf, "%03d%c", code, c); } else { sprintf(buf, "%03d%c", code, c); if (enhancedstatusok && status && status[0] != 0) sprintf(buf+4, "%s ", status); } s = strlen(buf)+buf; switch (code) { case 211: /* System status */ text = "%s"; break; case 214: /* Help message */ text = "%s"; break; case 220: /* Service ready */ case 221: /* Service closing transmission channel */ case 421: /* Service not available, closing transmission channel */ if (SS) sprintf(format, "%.200s %%s", SS->myhostname); else strcpy(format,"hostname-unavailable %s"); text = format; break; case 250: /* Requested mail action okay, completed */ text = "Ok"; break; case 251: /* User not local; will forward to */ text = "User not local; will forward to <%s>"; break; case 252: /* Cannot VRFY user, but will accept message and attempt delivery */ text = "Cannot VRFY user, but will accept message and attempt delivery"; break; case 354: /* Start mail input; end with . */ text = "Start mail input; end with ."; break; case 450: /* Requested mail action not taken: mailbox unavailable */ text = "Requested mail action not taken: mailbox unavailable"; break; case 451: /* Requested action aborted: local error in processing */ text = "Requested action aborted: local error in processing"; break; case 452: /* Requested action not taken: insufficient system storage */ text = "Requested action not taken: insufficient storage"; break; case 500: /* Syntax error, command unrecognized */ text = "Syntax error, command unrecognized"; break; case 501: /* Syntax error in parameters or arguments */ text = "Syntax error in parameters or arguments"; break; case 502: /* Command not implemented */ text = "Command not implemented"; break; case 503: /* Bad sequence of commands */ text = "Bad sequence of commands"; break; case 504: /* Command parameter not implemented */ text = "Command parameter not implemented"; break; case 550: /* Requested action not taken: mailbox unavailable */ text = "Requested action not taken: mailbox unavailable"; break; case 551: /* User not local; please try */ text = "User not local; please try <%s>"; break; case 552: /* Requested mail action aborted: exceeded storage allocation */ text = "Requested mail action aborted: exceeded storage allocation"; break; case 553: /* Requested action not taken: mailbox name not allowed */ text = "Requested action not taken: mailbox name not allowed"; break; case 554: /* Transaction failed */ text = "Transaction failed"; break; default: text = "code unknown, program bug!"; break; } #ifdef HAVE_VSNPRINTF { int bufspc = sizeof(buf) - (s - buf) - 8; va_list ap; #ifdef HAVE_STDARG_H va_start(ap, fmt); #else va_start(ap); #endif if (fmt != NULL) vsnprintf(s, bufspc, fmt, ap); else vsnprintf(s, bufspc, text, ap); va_end(ap); } #else #ifdef HAVE_VSRINTF { va_list ap; #ifdef HAVE_STDARG_H va_start(ap, fmt); #else va_start(ap); #endif if (fmt != NULL) vsprintf(s, fmt, ap); else vsprintf(s, text, ap); va_end(ap); } #else if (fmt != NULL) sprintf(s, fmt, s1, s2, s3, s4, s5, s6); else sprintf(s, text, s1, s2, s3, s4, s5, s6); #endif #endif s += strlen(s); buflen = s - buf; if (buflen+4 > sizeof(buf)) { /* XXX: Buffer overflow ??!! Signal about it, and crash! */ } if (logfp_to_syslog || logfp) time( & now ); if (logfp_to_syslog) zsyslog((LOG_DEBUG,"%s%04d %c %s", logtag, (int)(now - logtagepoch), (SS ? 'w' : '#'), buf)); if (logfp != NULL) { fprintf(logfp, "%s%04d%c\t%s\n", logtag, (int)(now - logtagepoch), (SS ? 'w' : '#'), buf); fflush(logfp); } if (debug && !SS) fprintf(stdout, "%s\n", buf); if (!SS) return; /* Only to local log.. */ memcpy(s, "\r\n", 2); Z_write(SS, buf, buflen+2); /* XX: check return value */ } /* * type220headers() outputs the initial greeting header(s), and * does it without need for SSL wrapping. */ void type220headers(SS, identflg, xlatelang, curtime) SmtpState *SS; const int identflg; const char *xlatelang; const char *curtime; { char *s, **hh = hdr220lines; char linebuf[8000]; char *l, *le; /* We collect the line into single buffer, then output it in one go with the code below. This to ensure that it will (very likely) be written out in single syscall -- some systems get mighty upset when they receive multiple TCP segments of the initial greeting :-/ */ for (; *hh ; ++hh) { char c = (hh[1] == NULL) ? ' ' : '-'; le = linebuf + sizeof(linebuf) -1; l = linebuf; /* The format meta-tags: * * %% -- '%' character * %H -- SS->myhostname * %I -- '+IDENT' if 'identflg' is set * %V -- VersionNumb * %T -- curtime string * %X -- xlatelang parameter */ s = *hh; while (*s && l < le) { if (*s == '%') { int freespc = le-l; int len; ++s; switch (*s) { case '%': *l++ = '%'; break; case 'H': len = strlen(SS->myhostname); memcpy(l, SS->myhostname, freespc < len ? freespc : len); l += len; break; case 'I': if (identflg) { len = 6; memcpy(l, "+IDENT", freespc < len ? freespc : len); l += len; } break; case 'V': len = strlen(VersionNumb); memcpy(l, VersionNumb, freespc < len ? freespc : len); l += len; break; case 'T': len = strlen(curtime); memcpy(l, curtime, freespc < len ? freespc : len); l += len; break; case 'X': if (!xlatelang) xlatelang = ""; len = strlen(xlatelang); memcpy(l, xlatelang, freespc < len ? freespc : len); l += len; break; default: /* Duh ?? */ break; } } else { *l++ = *s; } if (*s) ++s; } if (l < le) *l = 0; *le = 0; if (c == ' ') type(SS, 220, NULL, "%s", linebuf); else type(SS, -220, NULL, "%s", linebuf); } } void #ifdef HAVE_STDARG_H #ifdef __STDC__ type821err(SmtpState * SS, const int code, const char *status, const char *inbuf, const char *msg, ...) #else type821err(SS, code, status, inbuf, msg) SmtpState *SS; const int code; const char *status, *inbuf, *msg; #endif #else /* VARARGS */ type821err(va_alist) va_dcl #endif { va_list ap; int maxcnt = 200; int abscode, buflen; const char *a1, *a2, *a3, *a4; const char *s; char buf[2000], *bp; #ifdef HAVE_STDARG_H va_start(ap, msg); #else SmtpState *SS; const int code; const char *status, *inbuf, *msg; SS = va_arg(ap, SmtpState *); code = va_arg(ap, const int); status = va_arg(ap, const char *); inbuf = va_arg(ap, const char *); msg = va_arg(ap, const char *); #endif s = inbuf + 3 + 1; /* These are not always safe... but they should be ok if we are carrying (char*)s or (int)s.. */ a1 = va_arg(ap, const char *); a2 = va_arg(ap, const char *); a3 = va_arg(ap, const char *); a4 = va_arg(ap, const char *); abscode = (code < 0) ? -code : code; if (multilinereplies) { if (enhancedstatusok) { sprintf(buf, "%03d-%s ", abscode, status); s += strlen(status) +1; } else { /* No status codes */ sprintf(buf, "%03d- ", abscode); ++s; } bp = buf + strlen(buf); while (s < rfc821_error_ptr && --maxcnt >= 0) { ++s; *bp++ = ' '; } *bp++ = '^'; *bp = 0; buflen = bp - buf; if (logfp_to_syslog || logfp) time( & now ); if (logfp_to_syslog) zsyslog((LOG_DEBUG, "%s%04d w %s", logtag, (int)(now - logtagepoch), buf)); if (logfp) fprintf(logfp, "%s%04dw\t%s\n", logtag, (int)(now - logtagepoch), buf); memcpy(bp, "\r\n",2); Z_write(SS, buf, buflen+2); /* XX: check return value */ } type(SS, code, status, msg, a1, a2, a3, a4); va_end(ap); } static void setrfc1413ident(SS) SmtpState *SS; { volatile const char *cp; char identbuf[1024]; #if defined(AF_INET6) && defined(INET6) if (SS->raddr.v6.sin6_family == AF_INET6) { cp = ident_tcpuser9(AF_INET6, 16, &SS->localsock.v6.sin6_addr, &SS->raddr.v6.sin6_addr, ntohs(SS->localsock.v6.sin6_port), ntohs(SS->raddr.v6.sin6_port), IDENT_TIMEOUT, identbuf, sizeof(identbuf) - 1); } else #endif if (SS->raddr.v4.sin_family == AF_INET) cp = ident_tcpuser9(AF_INET, 4, &SS->localsock.v4.sin_addr, &SS->raddr.v4.sin_addr, ntohs(SS->localsock.v4.sin_port), ntohs(SS->raddr.v4.sin_port), IDENT_TIMEOUT, identbuf, sizeof(identbuf) - 1); else { cp = "Unknown_type_of_remote_system_address!"; } if (cp != NULL) strncpy(SS->ident_username, (char *) cp, MAXHOSTNAMELEN); else strncpy(SS->ident_username, "IDENT-CALL-FAULT", MAXHOSTNAMELEN); } void smtp_tarpit(SS) SmtpState *SS; { char *ts; if (SS->tarpit) { /* add this so we know when tarpit is active */ if (logfp != NULL && SS->tarpit != 0 ) { time(&now); ts = rfc822date(&now); fprintf(logfp, "%s#\ttarpit delay:%04d sec. at %s", logtag, SS->tarpit,ts ); fflush(logfp); } SS->tarpit_cval += SS->tarpit; sleep(SS->tarpit); /* adjust tarpit delay and limit here, "after!" the sleep */ SS->tarpit += (SS->tarpit * tarpit_exponent); /* was 250 - set up to a config param in smtpserver.conf - jmack apr 2003 */ if (SS->tarpit < 0 || SS->tarpit > tarpit_toplimit ) SS->tarpit = tarpit_toplimit; /* XX: Count each tarpit call, or just once per connection ? */ MIBMtaEntry->ss.IncomingSmtpTarpits ++; } } void header_to_mime(buf, lenptr, maxlen) char *buf; int *lenptr; int maxlen; { /* XXX: HEADERS -> MIME-2 */ } #ifdef USE_TRANSLATION void header_from_mime(buf, lenptr, maxlen) char *buf; int *lenptr; int maxlen; { /* XXX: HEADERS -> MIME-2 */ } #endif /* USE_TRANSLATION */