aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Monnerat <Patrick.Monnerat@datasphere.ch>2010-02-22 12:41:02 +0000
committerPatrick Monnerat <Patrick.Monnerat@datasphere.ch>2010-02-22 12:41:02 +0000
commit338553eda38a7ed163f935e28461eeda6dde294f (patch)
tree62e46e9a2580449f181c6933fa580d37443c4098
parent2abcd132f832cbd1f3a476e655f47b6a02862db3 (diff)
- Proper handling of STARTTLS on SMTP, taking CURLUSESSL_TRY into account.
- SMTP falls back to RFC821 HELO when EHLO fails (and SSL is not required). - Use of true local host name (i.e.: via gethostname()) when available, as default argument to SMTP HELO/EHLO. - Test case 804 for HELO fallback.
-rw-r--r--CHANGES7
-rw-r--r--docs/RESOURCES3
-rw-r--r--lib/smtp.c91
-rw-r--r--lib/smtp.h1
-rw-r--r--tests/data/DISABLED1
-rw-r--r--tests/data/Makefile.am2
-rw-r--r--tests/data/test80459
7 files changed, 147 insertions, 17 deletions
diff --git a/CHANGES b/CHANGES
index 99c93dc9e..79f0c6ed4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,13 @@
Changelog
+Patrick Monnerat (22 Feb 2010)
+- Proper handling of STARTTLS on SMTP, taking CURLUSESSL_TRY into account.
+- SMTP falls back to RFC821 HELO when EHLO fails (and SSL is not required).
+- Use of true local host name (i.e.: via gethostname()) when available, as
+ default argument to SMTP HELO/EHLO.
+- Test case 804 for HELO fallback.
+
Daniel Stenberg (20 Feb 2010)
- Fixed the SMTP compliance by making sure RCPT TO addresses are specified
properly in angle brackets. Recipients provided with CURLOPT_MAIL_RCPT now
diff --git a/docs/RESOURCES b/docs/RESOURCES
index b8bfc2fbd..4d6465d6e 100644
--- a/docs/RESOURCES
+++ b/docs/RESOURCES
@@ -66,7 +66,10 @@ This document lists documents and standards used by curl.
RFC 2818 - HTTP Over TLS (TLS is the successor to SSL)
+ RFC 2821 - SMTP protocol
+
RFC 2964 - Use of HTTP State Management
RFC 2965 - HTTP State Management Mechanism. Cookies. Obsoletes RFC2109
+ RFC 3207 - SMTP over TLS
diff --git a/lib/smtp.c b/lib/smtp.c
index 238fc2dbb..45df3042e 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -19,6 +19,7 @@
* KIND, either express or implied.
*
* RFC2821 SMTP protocol
+ * RFC3207 SMTP over TLS
*
* $Id$
***************************************************************************/
@@ -228,6 +229,7 @@ static void state(struct connectdata *conn,
"STOP",
"SERVERGREET",
"EHLO",
+ "HELO",
"STARTTLS",
"MAIL",
"RCPT",
@@ -253,11 +255,26 @@ static CURLcode smtp_state_ehlo(struct connectdata *conn)
/* send EHLO */
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "EHLO %s", smtpc->domain);
+
if(result)
return result;
state(conn, SMTP_EHLO);
+ return CURLE_OK;
+}
+
+static CURLcode smtp_state_helo(struct connectdata *conn)
+{
+ CURLcode result;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ /* send HELO */
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "HELO %s", smtpc->domain);
+
+ if(result)
+ return result;
+ state(conn, SMTP_HELO);
return CURLE_OK;
}
@@ -278,9 +295,13 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
struct SessionHandle *data = conn->data;
(void)instate; /* no use for this yet */
- if(smtpcode != 'O') {
- failf(data, "STARTTLS denied. %c", smtpcode);
- result = CURLE_LOGIN_DENIED;
+ if(smtpcode != 220) {
+ if(data->set.ftp_ssl == CURLUSESSL_TRY)
+ state(conn, SMTP_STOP);
+ else {
+ failf(data, "STARTTLS denied. %c", smtpcode);
+ result = CURLE_LOGIN_DENIED;
+ }
}
else {
/* Curl_ssl_connect is BLOCKING */
@@ -290,7 +311,6 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
result = smtp_state_ehlo(conn);
}
}
- state(conn, SMTP_STOP);
return result;
}
@@ -305,12 +325,44 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
(void)instate; /* no use for this yet */
if(smtpcode/100 != 2) {
+ if(data->set.ftp_ssl > CURLUSESSL_TRY && !conn->ssl[FIRSTSOCKET].use)
+ result = smtp_state_helo(conn);
+ else {
+ failf(data, "Access denied: %d", smtpcode);
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+ else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
+ /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
+ to TLS connection now */
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS", NULL);
+ state(conn, SMTP_STARTTLS);
+ }
+ else {
+ /* end the connect phase */
+ state(conn, SMTP_STOP);
+ }
+ return result;
+}
+
+/* for HELO responses */
+static CURLcode smtp_state_helo_resp(struct connectdata *conn,
+ int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode/100 != 2) {
failf(data, "Access denied: %d", smtpcode);
result = CURLE_LOGIN_DENIED;
+ }
+ else {
+ /* end the connect phase */
+ state(conn, SMTP_STOP);
}
-
- /* end the connect phase */
- state(conn, SMTP_STOP);
return result;
}
@@ -478,14 +530,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
- if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
- /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
- to TLS connection now */
- result = Curl_pp_sendf(&smtpc->pp, "STARTTLS", NULL);
- state(conn, SMTP_STARTTLS);
- }
- else
- result = smtp_state_ehlo(conn);
+ result = smtp_state_ehlo(conn);
if(result)
return result;
break;
@@ -494,6 +539,10 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
break;
+ case SMTP_HELO:
+ result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
+ break;
+
case SMTP_MAIL:
result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
break;
@@ -597,6 +646,10 @@ static CURLcode smtp_connect(struct connectdata *conn,
const char *path = conn->data->state.path;
int len;
+#ifdef HAVE_GETHOSTNAME
+ char localhost[1024 + 1];
+#endif
+
*done = FALSE; /* default to not done yet */
/* If there already is a protocol-specific struct allocated for this
@@ -660,8 +713,14 @@ static CURLcode smtp_connect(struct connectdata *conn,
pp->endofresp = smtp_endofresp;
pp->conn = conn;
- if(!*path)
+ if(!*path) {
+#ifdef HAVE_GETHOSTNAME
+ if(!gethostname(localhost, sizeof localhost))
+ path = localhost;
+ else
+#endif
path = "localhost";
+ }
/* url decode the path and use it as domain with EHLO */
smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
diff --git a/lib/smtp.h b/lib/smtp.h
index 02cd467f5..41efabba4 100644
--- a/lib/smtp.h
+++ b/lib/smtp.h
@@ -33,6 +33,7 @@ typedef enum {
SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
a connect */
SMTP_EHLO,
+ SMTP_HELO,
SMTP_STARTTLS,
SMTP_MAIL, /* MAIL FROM */
SMTP_RCPT, /* RCPT TO */
diff --git a/tests/data/DISABLED b/tests/data/DISABLED
index c65cdd90b..24bc532c2 100644
--- a/tests/data/DISABLED
+++ b/tests/data/DISABLED
@@ -7,3 +7,4 @@
564
802
803
+804
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 0a9ac547c..1878e013c 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -65,7 +65,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
test312 test1105 test565 test800 test1106 test801 test566 test802 test803 \
test1107 test1108 test1109 test1110 test1111 test1112 test129 test567 \
- test568 test569 test570 test571
+ test568 test569 test570 test571 test804
filecheck:
@mkdir test-place; \
diff --git a/tests/data/test804 b/tests/data/test804
new file mode 100644
index 000000000..e1fc419e9
--- /dev/null
+++ b/tests/data/test804
@@ -0,0 +1,59 @@
+<testcase>
+<info>
+<keywords>
+SMTP
+SMTP HELO
+RFC821
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+REPLY EHLO 500 Command unrecognized
+REPLY HELO 250 Already old but still servicing...
+</servercmd>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+smtp
+</server>
+ <name>
+RFC821-only SMTP server (EHLO not supported)
+ </name>
+<stdin>
+From: different
+To: another
+
+body
+</stdin>
+ <command>
+smtp://%HOSTIP:%SMTPPORT/user --mail-rcpt 804@foo --mail-from 804@from -T -
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+EHLO user
+HELO user
+MAIL FROM:804@from
+RCPT TO:<804@foo>
+DATA
+QUIT
+</protocol>
+<upload>
+From: different
+To: another
+
+body
+
+.
+</upload>
+</verify>
+</testcase>