📄 smtp.c
字号:
/* ======================================================================== * Copyright 1988-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * * ======================================================================== *//* * Program: Simple Mail Transfer Protocol (SMTP) routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 27 July 1988 * Last Edited: 28 January 2008 * * This original version of this file is * Copyright 1988 Stanford University * and was developed in the Symbolic Systems Resources Group of the Knowledge * Systems Laboratory at Stanford University in 1987-88, and was funded by the * Biomedical Research Technology Program of the National Institutes of Health * under grant number RR-00785. */#include <ctype.h>#include <stdio.h>#include "c-client.h"/* Constants */#define SMTPSSLPORT (long) 465 /* former assigned SSL TCP contact port */#define SMTPGREET (long) 220 /* SMTP successful greeting */#define SMTPAUTHED (long) 235 /* SMTP successful authentication */#define SMTPOK (long) 250 /* SMTP OK code */#define SMTPAUTHREADY (long) 334/* SMTP ready for authentication */#define SMTPREADY (long) 354 /* SMTP ready for data */#define SMTPSOFTFATAL (long) 421/* SMTP soft fatal code */#define SMTPWANTAUTH (long) 505 /* SMTP authentication needed */#define SMTPWANTAUTH2 (long) 530/* SMTP authentication needed */#define SMTPUNAVAIL (long) 550 /* SMTP mailbox unavailable */#define SMTPHARDERROR (long) 554/* SMTP miscellaneous hard failure *//* Convenient access to protocol-specific data */#define ESMTP stream->protocol.esmtp/* Function prototypes */void *smtp_challenge (void *s,unsigned long *len);long smtp_response (void *s,char *response,unsigned long size);long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp);long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error);long smtp_send (SENDSTREAM *stream,char *command,char *args);long smtp_reply (SENDSTREAM *stream);long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb);long smtp_fake (SENDSTREAM *stream,char *text);static long smtp_seterror (SENDSTREAM *stream,long code,char *text);long smtp_soutr (void *stream,char *s);/* Mailer parameters */static unsigned long smtp_maxlogintrials = MAXLOGINTRIALS;static long smtp_port = 0; /* default port override */static long smtp_sslport = 0;#ifndef RFC2821#define RFC2821 /* RFC 2821 compliance */#endif/* SMTP limits, current as of RFC 2821 */#define SMTPMAXLOCALPART 64#define SMTPMAXDOMAIN 255#define SMTPMAXPATH 256/* I have seen local parts of more than 64 octets, in spite of the SMTP * limits. So, we'll have a more generous limit that's still guaranteed * not to pop the buffer, and let the server worry about it. As of this * writing, it comes out to 240. Anyone with a mailbox name larger than * that is in serious need of a life or at least a new ISP! 23 June 1998 */#define MAXLOCALPART ((MAILTMPLEN - (SMTPMAXDOMAIN + SMTPMAXPATH + 32)) / 2)/* Mail Transfer Protocol manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */void *smtp_parameters (long function,void *value){ switch ((int) function) { case SET_MAXLOGINTRIALS: smtp_maxlogintrials = (unsigned long) value; break; case GET_MAXLOGINTRIALS: value = (void *) smtp_maxlogintrials; break; case SET_SMTPPORT: smtp_port = (long) value; break; case GET_SMTPPORT: value = (void *) smtp_port; break; case SET_SSLSMTPPORT: smtp_sslport = (long) value; break; case GET_SSLSMTPPORT: value = (void *) smtp_sslport; break; default: value = NIL; /* error case */ break; } return value;}/* Mail Transfer Protocol open connection * Accepts: network driver * service host list * port number * service name * SMTP open options * Returns: SEND stream on success, NIL on failure */SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service, unsigned long port,long options){ SENDSTREAM *stream = NIL; long reply; char *s,tmp[MAILTMPLEN]; NETSTREAM *netstream; NETMBX mb; if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR); /* maximum domain name is 64 characters */ else do if (strlen (*hostlist) < SMTPMAXDOMAIN) { sprintf (tmp,"{%.1000s}",*hostlist); if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") || mb.anoflag || mb.readonlyflag) { sprintf (tmp,"Invalid host specifier: %.80s",*hostlist); mm_log (tmp,ERROR); } else { /* light tryssl flag if requested */ mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL; /* explicit port overrides all */ if (mb.port) port = mb.port; /* else /submit overrides port argument */ else if (!compare_cstring (mb.service,"submit")) { port = SUBMITTCPPORT; /* override port, use IANA name */ strcpy (mb.service,"submission"); } /* else port argument overrides SMTP port */ else if (!port) port = smtp_port ? smtp_port : SMTPTCPPORT; if (netstream = /* try to open ordinary connection */ net_open (&mb,dv,port, (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL), "*smtps",smtp_sslport ? smtp_sslport : SMTPSSLPORT)) { stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0, sizeof (SENDSTREAM)); stream->netstream = netstream; stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ? net_host (netstream) : mb.host); stream->debug = (mb.dbgflag || (options & OP_DEBUG)) ? T : NIL; if (options & SOP_SECURE) mb.secflag = T; /* get name of local host to use */ s = compare_cstring ("localhost",mb.host) ? net_localhost (netstream) : "localhost"; do reply = smtp_reply (stream); while ((reply < 100) || (stream->reply[3] == '-')); if (reply != SMTPGREET){/* get SMTP greeting */ sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } /* try EHLO first, then HELO */ else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) && ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) { sprintf (tmp,"SMTP hello failure: %.80s",stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } else { NETDRIVER *ssld =(NETDRIVER *)mail_parameters(NIL,GET_SSLDRIVER,NIL); sslstart_t stls = (sslstart_t) mail_parameters(NIL,GET_SSLSTART,NIL); ESMTP.ok = T; /* ESMTP server, start TLS if present */ if (!dv && stls && ESMTP.service.starttls && !mb.sslflag && !mb.notlsflag && (smtp_send (stream,"STARTTLS",NIL) == SMTPGREET)) { mb.tlsflag = T; /* TLS OK, get into TLS at this end */ stream->netstream->dtb = ssld; /* TLS started, negotiate it */ if (!(stream->netstream->stream = (*stls) (stream->netstream->stream,mb.host, (mb.tlssslv23 ? NIL : NET_TLSCLIENT) | (mb.novalidate ? NET_NOVALIDATECERT:NIL)))){ /* TLS negotiation failed after STARTTLS */ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s", mb.host); mm_log (tmp,ERROR); /* close without doing QUIT */ if (stream->netstream) net_close (stream->netstream); stream->netstream = NIL; stream = smtp_close (stream); } /* TLS OK, re-negotiate EHLO */ else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) { sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s", stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } else ESMTP.ok = T; /* TLS OK and EHLO successful */ } else if (mb.tlsflag) {/* user specified /tls but can't do it */ sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host); mm_log (tmp,ERROR); stream = smtp_close (stream); } /* remote name for authentication */ if (stream && ((mb.secflag || mb.user[0]))) { if (ESMTP.auth) { /* use authenticator? */ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) { /* remote name for authentication */ strncpy (mb.host, (long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ? net_remotehost (netstream) : net_host (netstream), NETMAXHOST-1); mb.host[NETMAXHOST-1] = '\0'; } if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream); } else { /* no available authenticators? */ sprintf (tmp,"%sSMTP authentication not available: %.80s", mb.secflag ? "Secure " : "",mb.host); mm_log (tmp,ERROR); stream = smtp_close (stream); } } } } } } while (!stream && *++hostlist); if (stream) { /* set stream options if have a stream */ if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY | SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) { ESMTP.dsn.want = T; if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T; if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T; if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T; if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T; } if (options & SOP_8BITMIME) ESMTP.eightbit.want = T; } return stream;}/* SMTP authenticate * Accepts: stream to login * parsed network mailbox structure * scratch buffer * place to return user name * Returns: T on success, NIL on failure */long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp){ unsigned long trial,auths; char *lsterr = NIL; char usr[MAILTMPLEN]; AUTHENTICATOR *at; long ret = NIL; for (auths = ESMTP.auth, stream->saslcancel = NIL; !ret && stream->netstream && auths && (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) { if (lsterr) { /* previous authenticator failed? */ sprintf (tmp,"Retrying using %s authentication after %.80s", at->name,lsterr); mm_log (tmp,NIL); fs_give ((void **) &lsterr); } trial = 0; /* initial trial count */ tmp[0] = '\0'; /* empty buffer */ if (stream->netstream) do { if (lsterr) { sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr); mm_log (tmp,WARN); fs_give ((void **) &lsterr); } stream->saslcancel = NIL; if (smtp_send (stream,"AUTH",at->name) == SMTPAUTHREADY) { /* hide client authentication responses */ if (!(at->flags & AU_SECURE)) stream->sensitive = T; if ((*at->client) (smtp_challenge,smtp_response,"smtp",mb,stream, &trial,usr)) { if (stream->replycode == SMTPAUTHED) { ESMTP.auth = NIL; /* disable authenticators */ ret = LONGT; } /* if main program requested cancellation */ else if (!trial) mm_log ("SMTP Authentication cancelled",ERROR); } stream->sensitive = NIL;/* unhide */ } /* remember response if error and no cancel */ if (!ret && trial) lsterr = cpystr (stream->reply); } while (!ret && stream->netstream && trial && (trial < smtp_maxlogintrials)); } if (lsterr) { /* previous authenticator failed? */ if (!stream->saslcancel) { /* don't do this if a cancel */ sprintf (tmp,"Can not authenticate to SMTP server: %.80s",lsterr); mm_log (tmp,ERROR); } fs_give ((void **) &lsterr); } return ret; /* authentication failed */}/* Get challenge to authenticator in binary * Accepts: stream * pointer to returned size * Returns: challenge or NIL if not challenge */void *smtp_challenge (void *s,unsigned long *len){ char tmp[MAILTMPLEN]; void *ret = NIL; SENDSTREAM *stream = (SENDSTREAM *) s; if ((stream->replycode == SMTPAUTHREADY) && !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4, strlen (stream->reply + 4),len))) { sprintf (tmp,"SMTP SERVER BUG (invalid challenge): %.80s",stream->reply+4); mm_log (tmp,ERROR); } return ret;}/* Send authenticator response in BASE64 * Accepts: MAIL stream * string to send * length of string * Returns: T, always */long smtp_response (void *s,char *response,unsigned long size){ SENDSTREAM *stream = (SENDSTREAM *) s; unsigned long i,j; char *t,*u; if (response) { /* make CRLFless BASE64 string */ if (size) { for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0; j < i; j++) if (t[j] > ' ') *u++ = t[j]; *u = '\0'; /* tie off string */ i = smtp_send (stream,t,NIL); fs_give ((void **) &t); } else i = smtp_send (stream,"",NIL); } else { /* abort requested */ i = smtp_send (stream,"*",NIL); stream->saslcancel = T; /* mark protocol-requested SASL cancel */ } return LONGT;}/* Mail Transfer Protocol close connection * Accepts: SEND stream * Returns: NIL always */SENDSTREAM *smtp_close (SENDSTREAM *stream){ if (stream) { /* send "QUIT" */ if (stream->netstream) { /* do close actions if have netstream */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -