?? smtpserv.c
字號:
/* SMTP Server state machine - see RFC 821
*/
#include <stdio.h>
#include <time.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#if defined(__STDC__) || defined(__TURBOC__)
#include <stdarg.h>
#endif
#include <ctype.h>
#include <setjmp.h>
#include "global.h"
#include "mbuf.h"
#include "cmdparse.h"
#include "socket.h"
#include "iface.h"
#include "proc.h"
#include "smtp.h"
#include "commands.h"
#include "dirutil.h"
#include "mailbox.h"
#include "bm.h"
#include "domain.h"
char *Days[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec" };
static struct list *expandalias(struct list **head,char *user);
static int getmsgtxt(struct smtpsv *mp);
static struct smtpsv *mail_create(void);
static void mail_clean(struct smtpsv *mp);
static int mailit(FILE *data,char *from,struct list *tolist);
static int router_queue(FILE *data,char *from,struct list *to);
static void smtplog(char *fmt,...);
static void smtpserv(int s,void *unused,void *p);
static int mailuser(FILE *data,char *from,char *to);
/* Command table */
static char *commands[] = {
"helo",
"noop",
"mail from:",
"quit",
"rcpt to:",
"help",
"data",
"rset",
"expn",
NULL
};
enum smtp_cmd {
HELO_CMD,
NOOP_CMD,
MAIL_CMD,
QUIT_CMD,
RCPT_CMD,
HELP_CMD,
DATA_CMD,
RSET_CMD,
EXPN_CMD
};
/* Reply messages */
static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
static char Banner[] = "220 %s SMTP ready\n";
static char Closing[] = "221 Closing\n";
static char Ok[] = "250 Ok\n";
static char Reset[] = "250 Reset state\n";
static char Sent[] = "250 Sent\n";
static char Ourname[] = "250 %s, Share and Enjoy!\n";
static char Enter[] = "354 Enter mail, end with .\n";
static char Ioerr[] = "452 Temp file write error\n";
static char Badcmd[] = "500 Command unrecognized\n";
static char Lowmem[] = "421 System overloaded, try again later\n";
static char Syntax[] = "501 Syntax error\n";
static char Needrcpt[] = "503 Need RCPT (recipient)\n";
static char Unknown[] = "550 <%s> address unknown\n";
static char Noalias[] = "550 No alias for <%s>\n";
static int Ssmtp = -1; /* prototype socket for service */
/* Start up SMTP receiver service */
int
smtp1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct sockaddr_in lsocket;
int s;
FILE *network;
if(Ssmtp != -1){
return 0;
}
ksignal(Curproc,0); /* Don't keep the parser waiting */
chname(Curproc,"SMTP listener");
lsocket.sin_family = AF_INET;
lsocket.sin_addr.s_addr = INADDR_ANY;
if(argc < 2)
lsocket.sin_port = IPPORT_SMTP;
else
lsocket.sin_port = atoi(argv[1]);
Ssmtp = socket(AF_INET,SOCK_STREAM,0);
bind(Ssmtp,(struct sockaddr *)&lsocket,sizeof(lsocket));
listen(Ssmtp,1);
for(;;){
if((s = accept(Ssmtp,NULL,(int *)NULL)) == -1)
break; /* Service is shutting down */
network = fdopen(s,"r+t");
if(availmem() != 0){
fprintf(network,Lowmem);
fclose(network);
} else {
/* Spawn a server */
newproc("SMTP server",2048,smtpserv,s,(void *)network,NULL,0);
}
}
return 0;
}
/* Shutdown SMTP service (existing connections are allowed to finish) */
int
smtp0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
close_s(Ssmtp);
Ssmtp = -1;
return 0;
}
static void
smtpserv(s,n,p)
int s;
void *n;
void *p;
{
struct smtpsv *mp;
char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
struct list *ap,*list;
int cnt;
char address_type;
FILE *network;
network = (FILE *)n;
sockowner(fileno(network),Curproc); /* We own it now */
logmsg(fileno(network),"open SMTP");
if((mp = mail_create()) == NULL){
printf(Nospace);
logmsg(fileno(network),"close SMTP - no space");
fclose(network);
return;
}
mp->network = network;
(void) fprintf(network,Banner,Hostname);
loop: if (fgets(buf,sizeof(buf),network) == NULL) {
/* He closed on us */
goto quit;
}
cnt = strlen(buf);
if(cnt < 4){
/* Can't be a legal command */
fprintf(network,Badcmd);
goto loop;
}
rip(buf);
cmd = buf;
/* Translate entire buffer to lower case */
for(cp = cmd;*cp != '\0';cp++)
*cp = tolower(*cp);
/* Find command in table; if not present, return syntax error */
for(cmdp = commands;*cmdp != NULL;cmdp++)
if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
break;
if(*cmdp == NULL){
(void) fprintf(network,Badcmd);
goto loop;
}
arg = &cmd[strlen(*cmdp)];
/* Skip spaces after command */
while(*arg == ' ')
arg++;
/* Execute specific command */
switch(cmdp-commands) {
case HELO_CMD:
free(mp->system);
mp->system = strdup(arg);
(void) fprintf(network,Ourname,Hostname);
break;
case NOOP_CMD:
(void) fprintf(network,Ok);
break;
case MAIL_CMD:
if((cp = getname(arg)) == NULL){
(void) fprintf(network,Syntax);
break;
}
free(mp->from);
mp->from = strdup(cp);
(void) fprintf(network,Ok);
break;
case QUIT_CMD:
(void) fprintf(network,Closing);
goto quit;
case RCPT_CMD: /* Specify recipient */
if((cp = getname(arg)) == NULL){
(void) fprintf(network,Syntax);
break;
}
/* rewrite address if possible */
if((newaddr = rewrite_address(cp)) != NULL) {
strcpy(buf,newaddr);
cp = buf;
free(newaddr);
}
/* check if address is ok */
if ((address_type = validate_address(cp)) == BADADDR) {
(void) fprintf(network,Unknown,cp);
break;
}
/* if a local address check for an alias */
if (address_type == LOCAL)
expandalias(&mp->to, cp);
else
/* a remote address is added to the list */
addlist(&mp->to, cp, address_type);
(void) fprintf(network,Ok);
break;
case HELP_CMD:
(void) fprintf(network,Help);
break;
case DATA_CMD:
if(mp->to == NULL)
(void) fprintf(network,Needrcpt);
else if ((mp->data = tmpfile()) == NULL)
(void) fprintf(network,Ioerr);
else
getmsgtxt(mp);
break;
case RSET_CMD:
del_list(mp->to);
mp->to = NULL;
(void) fprintf(network,Reset);
break;
case EXPN_CMD:
if (*arg == '\0') {
(void) fprintf(network,Syntax);
break;
}
list = NULL;
/* rewrite address if possible */
if((newaddr = rewrite_address(arg)) != NULL)
if(strcmp(newaddr,arg) == 0) {
free(newaddr);
newaddr = NULL;
}
else {
strcpy(buf,newaddr);
arg = buf;
}
list = NULL;
expandalias(&list,arg);
if (strcmp(list->val,arg) == 0 && list->next == NULL)
if(newaddr == NULL) {
(void) fprintf(network,Noalias,arg);
del_list(list);
break;
}
ap = list;
while (ap->next != NULL) {
(void) fprintf(network,"250-%s\n",ap->val);
ap = ap->next;
}
fprintf(network,"250 %s\n",ap->val);
del_list(list);
free(newaddr);
break;
}
goto loop;
quit:
logmsg(fileno(network),"close SMTP");
fclose(network);
mail_clean(mp);
smtptick(0L); /* start SMTP daemon immediately */
}
/* read the message text */
static int
getmsgtxt(mp)
struct smtpsv *mp;
{
char buf[LINELEN];
register char *p = buf;
long t;
FILE *network;
FILE *data;
char *cp;
network = mp->network;
data = mp->data;
/* Add timestamp; ptime adds newline */
time(&t);
fprintf(data,"Received: ");
if(mp->system != NULL)
fprintf(data,"from %s ",mp->system);
fprintf(data,"by %s with SMTP\n\tid AA%ld ; %s",
Hostname, get_msgid(), ptime(&t));
if(ferror(data)){
(void) fprintf(network,Ioerr);
return 1;
} else {
(void) fprintf(network,Enter);
}
while(1) {
if(fgets(p,sizeof(buf),network) == NULL){
return 1;
}
rip(p);
/* check for end of message ie a . or escaped .. */
if (*p == '.') {
if (*++p == '\0') {
/* Also sends appropriate response */
if (mailit(data,mp->from,mp->to) != 0)
(void) fprintf(network,Ioerr);
else
(void) fprintf(network,Sent);
fclose(data);
data = NULL;
del_list(mp->to);
mp->to = NULL;
return 0;
} else if (!(*p == '.' && *(p+1) == '\0'))
p--;
}
#ifdef MSDOS
while((cp = strchr(p,CTLZ)) != NULL)
*cp = '\n';
#endif
/* for UNIX mail compatiblity */
if (strncmp(p,"From ",5) == 0)
(void) putc('>',data);
/* Append to data file */
if(fprintf(data,"%s\n",p) < 0) {
(void) fprintf(network,Ioerr);
return 1;
}
}
return 0;
}
/* Create control block, initialize */
static struct smtpsv *
mail_create()
{
register struct smtpsv *mp;
mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
mp->from = strdup(""); /* Default to null From address */
return mp;
}
/* Free resources, delete control block */
static void
mail_clean(mp)
register struct smtpsv *mp;
{
if (mp == NULL)
return;
free(mp->system);
free(mp->from);
if(mp->data != NULL)
fclose(mp->data);
del_list(mp->to);
free(mp);
}
/* Given a string of the form <user@host>, extract the part inside the
* brackets and return a pointer to it.
*/
char *
getname(cp)
register char *cp;
{
register char *cp1;
if ((cp = strchr(cp,'<')) == NULL)
return NULL;
cp++; /* cp -> first char of name */
if ((cp1 = strchr(cp,'>')) == NULL)
return NULL;
*cp1 = '\0';
return cp;
}
/* General mailit function. It takes a list of addresses which have already
** been verified and expanded for aliases. Base on the current mode the message
** is place in an mbox, the outbound smtp queue or the rqueue interface
*/
static int
mailit(data,from,tolist)
FILE *data;
char *from;
struct list *tolist;
{
struct list *ap, *dlist = NULL;
register FILE *fp;
char mailbox[50], *cp, *host, *qhost;
int c, fail = 0;
time_t t;
if ((Smtpmode & QUEUE) != 0)
return(router_queue(data,from,tolist));
do {
qhost = NULL;
for(ap = tolist;ap != NULL;ap = ap->next)
if (ap->type == DOMAIN) {
if ((host = strrchr(ap->val,'@')) != NULL)
host++;
else
host = Hostname;
if(qhost == NULL)
qhost = host;
if(stricmp(qhost,host) == 0) {
ap->type = BADADDR;
addlist(&dlist,ap->val,0);
}
}
if(qhost != NULL) {
rewind(data);
queuejob(data,qhost,dlist,from);
del_list(dlist);
dlist = NULL;
}
} while(qhost != NULL);
for(ap = tolist;ap != NULL;ap = ap->next) {
if(ap->type != LOCAL) {
ap->type = DOMAIN;
continue;
}
rewind(data);
/* strip off host name of LOCAL addresses */
if ((cp = strchr(ap->val,'@')) != NULL)
*cp = '\0';
/* truncate long user names */
if (strlen(ap->val) > MBOXLEN)
ap->val[MBOXLEN] = '\0';
/* if mail file is busy save it in our smtp queue
* and let the smtp daemon try later.
*/
if (mlock(Mailspool,ap->val)) {
addlist(&dlist,ap->val,0);
fail = queuejob(data,Hostname,dlist,from);
del_list(dlist);
dlist = NULL;
}
else {
char buf[LINELEN];
int tocnt = 0;
sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
#ifndef AMIGA
if((fp = fopen(mailbox,APPEND_TEXT)) != NULL) {
#else
if((fp = fopen(mailbox,"r+")) != NULL) {
(void) fseek(fp, 0L, 2);
#endif
time(&t);
fprintf(fp,"From %s %s",from,ctime(&t));
host = NULL;
while(fgets(buf,sizeof(buf),data) != NULL){
if(buf[0] == '\n'){
if(tocnt == 0)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -