⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smtp.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
#include "common.h"#include "smtp.h"#include <ctype.h>#include <mp.h>#include <libsec.h>#include <auth.h>static	char*	connect(char*);static	char*	dotls(char*);static	char*	doauth(char*);char*	hello(char*, int);char*	mailfrom(char*);char*	rcptto(char*);char*	data(String*, Biobuf*);void	quit(char*);int	getreply(void);void	addhostdom(String*, char*);String*	bangtoat(char*);String*	convertheader(String*);int	printheader(void);char*	domainify(char*, char*);void	putcrnl(char*, int);char*	getcrnl(String*);int	printdate(Node*);char	*rewritezone(char *);int	dBprint(char*, ...);int	dBputc(int);String*	fixrouteaddr(String*, Node*, Node*);int	ping;int	insecure;#define Retry	"Retry, Temporary Failure"#define Giveup	"Permanent Failure"int	debug;		/* true if we're debugging */String	*reply;		/* last reply */String	*toline;int	alarmscale;int	last = 'n';	/* last character sent by putcrnl() */int	filter;int	trysecure;	/* Try to use TLS if the other side supports it */int	tryauth;	/* Try to authenticate, if supported */int	quitting;	/* when error occurs in quit */char	*quitrv;	/* deferred return value when in quit */char	ddomain[1024];	/* domain name of destination machine */char	*gdomain;	/* domain name of gateway */char	*uneaten;	/* first character after rfc822 headers */char	*farend;	/* system we are trying to send to */char	*user;		/* user we are authenticating as, if authenticating */char	hostdomain[256];Biobuf	bin;Biobuf	bout;Biobuf	berr;Biobuf	bfile;voidusage(void){	fprint(2, "usage: smtp [-adips] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n");	exits(Giveup); }inttimeout(void *x, char *msg){	USED(x);	syslog(0, "smtp.fail", "interrupt: %s: %s", farend,  msg);	if(strstr(msg, "alarm")){		fprint(2, "smtp timeout: connection to %s timed out\n", farend);		if(quitting)			exits(quitrv);		exits(Retry);	}	if(strstr(msg, "closed pipe")){			/* call _exits() to prevent Bio from trying to flush closed pipe */		fprint(2, "smtp timeout: connection closed to %s\n", farend);		if(quitting){			syslog(0, "smtp.fail", "closed pipe to %s", farend);			_exits(quitrv);		}		_exits(Retry);	}	return 0;}voidremovenewline(char *p){	int n = strlen(p)-1;	if(n < 0)		return;	if(p[n] == '\n')		p[n] = 0;}voidmain(int argc, char **argv){	char hellodomain[256];	char *host, *domain;	String *from;	String *fromm;	String *sender;	char *addr;	char *rv, *trv;	int i, ok, rcvrs;	char **errs;	alarmscale = 60*1000;	/* minutes */	quotefmtinstall();	errs = malloc(argc*sizeof(char*));	reply = s_new();	host = 0;	ARGBEGIN{	case 'a':		tryauth = 1;		trysecure = 1;		break;	case 'f':		filter = 1;		break;	case 'd':		debug = 1;		break;	case 'g':		gdomain = ARGF();		break;	case 'h':		host = ARGF();		break;	case 'i':		insecure = 1;		break;	case 'p':		alarmscale = 10*1000;	/* tens of seconds */		ping = 1;		break;	case 's':		trysecure = 1;		break;	case 'u':		user = ARGF();		break;	default:		usage();		break;	}ARGEND;	Binit(&berr, 2, OWRITE);	Binit(&bfile, 0, OREAD);	/*	 *  get domain and add to host name	 */	if(*argv && **argv=='.') {		domain = *argv;		argv++; argc--;	} else		domain = domainname_read();	if(host == 0)		host = sysname_read();	strcpy(hostdomain, domainify(host, domain));	strcpy(hellodomain, domainify(sysname_read(), domain));	/*	 *  get destination address	 */	if(*argv == 0)		usage();	addr = *argv++; argc--;	farend = addr;	/*	 *  get sender's machine.	 *  get sender in internet style.  domainify if necessary.	 */	if(*argv == 0)		usage();	sender = unescapespecial(s_copy(*argv++));	argc--;	fromm = s_clone(sender);	rv = strrchr(s_to_c(fromm), '!');	if(rv)		*rv = 0;	else		*s_to_c(fromm) = 0;	from = bangtoat(s_to_c(sender));	/*	 *  send the mail	 */	if(filter){		Binit(&bout, 1, OWRITE);		rv = data(from, &bfile);		if(rv != 0)			goto error;		exits(0);	}	/* mxdial uses its own timeout handler */	if((rv = connect(addr)) != 0)		exits(rv);	/* 10 minutes to get through the initial handshake */	atnotify(timeout, 1);	alarm(10*alarmscale);	if((rv = hello(hellodomain, 0)) != 0)		goto error;	alarm(10*alarmscale);	if((rv = mailfrom(s_to_c(from))) != 0)		goto error;	ok = 0;	rcvrs = 0;	/* if any rcvrs are ok, we try to send the message */	for(i = 0; i < argc; i++){		if((trv = rcptto(argv[i])) != 0){			/* remember worst error */			if(rv != Giveup)				rv = trv;			errs[rcvrs] = strdup(s_to_c(reply));			removenewline(errs[rcvrs]);		} else {			ok++;			errs[rcvrs] = 0;		}		rcvrs++;	}	/* if no ok rcvrs or worst error is retry, give up */	if(ok == 0 || rv == Retry)		goto error;	if(ping){		quit(0);		exits(0);	}	rv = data(from, &bfile);	if(rv != 0)		goto error;	quit(0);	if(rcvrs == ok)		exits(0);	/*	 *  here when some but not all rcvrs failed	 */	fprint(2, "%s connect to %s:\n", thedate(), addr);	for(i = 0; i < rcvrs; i++){		if(errs[i]){			syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);			fprint(2, "  mail to %s failed: %s", argv[i], errs[i]);		}	}	exits(Giveup);	/*	 *  here when all rcvrs failed	 */error:	removenewline(s_to_c(reply));	syslog(0, "smtp.fail", "%s to %s failed: %s",		ping ? "ping" : "delivery",		addr, s_to_c(reply));	fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));	if(!filter)		quit(rv);	exits(rv);}/* *  connect to the remote host */static char *connect(char* net){	char buf[256];	int fd;	fd = mxdial(net, ddomain, gdomain);	if(fd < 0){		rerrstr(buf, sizeof(buf));		Bprint(&berr, "smtp: %s (%s)\n", buf, net);		syslog(0, "smtp.fail", "%s (%s)", buf, net);		if(strstr(buf, "illegal")		|| strstr(buf, "unknown")		|| strstr(buf, "can't translate"))			return Giveup;		else			return Retry;	}	Binit(&bin, fd, OREAD);	fd = dup(fd, -1);	Binit(&bout, fd, OWRITE);	return 0;}static char smtpthumbs[] =	"/sys/lib/tls/smtp";static char smtpexclthumbs[] =	"/sys/lib/tls/smtp.exclude";/* *  exchange names with remote host, attempt to *  enable encryption and optionally authenticate. *  not fatal if we can't. */static char *dotls(char *me){	TLSconn *c;	Thumbprint *goodcerts;	char *h;	int fd;	uchar hash[SHA1dlen];	c = mallocz(sizeof(*c), 1);	/* Note: not freed on success */	if (c == nil)		return Giveup;	dBprint("STARTTLS\r\n");	if (getreply() != 2)		return Giveup;	fd = tlsClient(Bfildes(&bout), c);	if (fd < 0) {		syslog(0, "smtp", "tlsClient to %q: %r", ddomain);		return Giveup;	}	goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);	if (goodcerts == nil) {		free(c);		close(fd);		syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);		return Giveup;		/* how to recover? TLS is started */	}	/* compute sha1 hash of remote's certificate, see if we know it */	sha1(c->cert, c->certlen, hash, nil);	if (!okThumbprint(hash, goodcerts)) {		/* TODO? if not excluded, add hash to thumb list */		free(c);		close(fd);		h = malloc(2*sizeof hash + 1);		if (h != nil) {			enc16(h, 2*sizeof hash + 1, hash, sizeof hash);			// print("x509 sha1=%s", h);			syslog(0, "smtp",		"remote cert. has bad thumbprint: x509 sha1=%s server=%q",				h, ddomain);			free(h);		}		return Giveup;		/* how to recover? TLS is started */	}	freeThumbprints(goodcerts);	Bterm(&bin);	Bterm(&bout);	/*	 * set up bin & bout to use the TLS fd, i/o upon which generates	 * i/o on the original, underlying fd.	 */	Binit(&bin, fd, OREAD);	fd = dup(fd, -1);	Binit(&bout, fd, OWRITE);	syslog(0, "smtp", "started TLS to %q", ddomain);	return(hello(me, 1));}static char *doauth(char *methods){	char *buf, *base64;	int n;	DS ds;	UserPasswd *p;	dial_string_parse(ddomain, &ds);	if(user != nil)		p = auth_getuserpasswd(nil,	  	  "proto=pass service=smtp server=%q user=%q", ds.host, user);	else		p = auth_getuserpasswd(nil,	  	  "proto=pass service=smtp server=%q", ds.host);	if (p == nil)		return Giveup;	if (strstr(methods, "LOGIN")){		dBprint("AUTH LOGIN\r\n");		if (getreply() != 3)			return Retry;		n = strlen(p->user);		base64 = malloc(2*n);		if (base64 == nil)			return Retry;	/* Out of memory */		enc64(base64, 2*n, (uchar *)p->user, n);		dBprint("%s\r\n", base64);		if (getreply() != 3)			return Retry;		n = strlen(p->passwd);		base64 = malloc(2*n);		if (base64 == nil)			return Retry;	/* Out of memory */		enc64(base64, 2*n, (uchar *)p->passwd, n);		dBprint("%s\r\n", base64);		if (getreply() != 2)			return Retry;		free(base64);	}	else	if (strstr(methods, "PLAIN")){		n = strlen(p->user) + strlen(p->passwd) + 3;		buf = malloc(n);		base64 = malloc(2 * n);		if (buf == nil || base64 == nil) {			free(buf);			return Retry;	/* Out of memory */		}		snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);		enc64(base64, 2 * n, (uchar *)buf, n - 1);		free(buf);		dBprint("AUTH PLAIN %s\r\n", base64);		free(base64);		if (getreply() != 2)			return Retry;	}	else		return "No supported AUTH method";	return(0);}char *hello(char *me, int encrypted){	int ehlo;	String *r;	char *ret, *s, *t;	if (!encrypted)		switch(getreply()){		case 2:			break;		case 5:			return Giveup;		default:			return Retry;		}	ehlo = 1;  Again:	if(ehlo)		dBprint("EHLO %s\r\n", me);	else		dBprint("HELO %s\r\n", me);	switch (getreply()) {	case 2:		break;	case 5:		if(ehlo){			ehlo = 0;			goto Again;		}		return Giveup;	default:		return Retry;	}	r = s_clone(reply);	if(r == nil)		return Retry;	/* Out of memory or couldn't get string */	/* Invariant: every line has a newline, a result of getcrlf() */	for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){		*t = '\0';		for (t = s; *t != '\0'; t++)			*t = toupper(*t);		if(!encrypted && trysecure &&		    (strcmp(s, "250-STARTTLS") == 0 ||		     strcmp(s, "250 STARTTLS") == 0)){			s_free(r);			return(dotls(me));		}		if(tryauth && (encrypted || insecure) &&		    (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||		     strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){			ret = doauth(s + strlen("250 AUTH "));			s_free(r);			return ret;		}	}	s_free(r);	return 0;}/* *  report sender to remote */char *mailfrom(char *from){	if(!returnable(from))		dBprint("MAIL FROM:<>\r\n");	else	if(strchr(from, '@'))		dBprint("MAIL FROM:<%s>\r\n", from);	else		dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);	switch(getreply()){	case 2:		break;	case 5:		return Giveup;	default:		return Retry;	}	return 0;}/* *  report a recipient to remote */char *rcptto(char *to){	String *s;	s = unescapespecial(bangtoat(to));	if(toline == 0)		toline = s_new();	else		s_append(toline, ", ");	s_append(toline, s_to_c(s));	if(strchr(s_to_c(s), '@'))		dBprint("RCPT TO:<%s>\r\n", s_to_c(s));	else {		s_append(toline, "@");		s_append(toline, ddomain);		dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);	}	alarm(10*alarmscale);	switch(getreply()){	case 2:		break;	case 5:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -