1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 * RFC1870 SMTP Service Extension for Message Size
24 * RFC2195 CRAM-MD5 authentication
25 * RFC2831 DIGEST-MD5 authentication
26 * RFC3207 SMTP over TLS
27 * RFC4422 Simple Authentication and Security Layer (SASL)
28 * RFC4616 PLAIN authentication
29 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
30 * RFC4954 SMTP Authentication
31 * RFC5321 SMTP protocol
32 * RFC5890 Internationalized Domain Names for Applications (IDNA)
33 * RFC6531 SMTP Extension for Internationalized Email
34 * RFC6532 Internationalized Email Headers
35 * RFC6749 OAuth 2.0 Authorization Framework
36 * RFC8314 Use of TLS for Email Submission and Access
37 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
38 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
39 *
40 ***************************************************************************/
41
42 #include "curl_setup.h"
43
44 #ifndef CURL_DISABLE_SMTP
45
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
48 #endif
49 #ifdef HAVE_ARPA_INET_H
50 #include <arpa/inet.h>
51 #endif
52 #ifdef HAVE_NETDB_H
53 #include <netdb.h>
54 #endif
55 #ifdef __VMS
56 #include <in.h>
57 #include <inet.h>
58 #endif
59
60 #include <curl/curl.h>
61 #include "urldata.h"
62 #include "sendf.h"
63 #include "hostip.h"
64 #include "progress.h"
65 #include "transfer.h"
66 #include "escape.h"
67 #include "http.h" /* for HTTP proxy tunnel stuff */
68 #include "mime.h"
69 #include "socks.h"
70 #include "smtp.h"
71 #include "strtoofft.h"
72 #include "strcase.h"
73 #include "vtls/vtls.h"
74 #include "cfilters.h"
75 #include "connect.h"
76 #include "select.h"
77 #include "multiif.h"
78 #include "url.h"
79 #include "curl_gethostname.h"
80 #include "bufref.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83 #include "idn.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
87 #include "memdebug.h"
88
89 /* Local API functions */
90 static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
91 static CURLcode smtp_do(struct Curl_easy *data, bool *done);
92 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
93 bool premature);
94 static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
95 static CURLcode smtp_disconnect(struct Curl_easy *data,
96 struct connectdata *conn, bool dead);
97 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
98 static int smtp_getsock(struct Curl_easy *data,
99 struct connectdata *conn, curl_socket_t *socks);
100 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
101 static CURLcode smtp_setup_connection(struct Curl_easy *data,
102 struct connectdata *conn);
103 static CURLcode smtp_parse_url_options(struct connectdata *conn);
104 static CURLcode smtp_parse_url_path(struct Curl_easy *data);
105 static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
106 static CURLcode smtp_parse_address(const char *fqma,
107 char **address, struct hostname *host);
108 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
109 const struct bufref *initresp);
110 static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
111 const struct bufref *resp);
112 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
113 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
114 static CURLcode cr_eob_add(struct Curl_easy *data);
115
116 /*
117 * SMTP protocol handler.
118 */
119
120 const struct Curl_handler Curl_handler_smtp = {
121 "smtp", /* scheme */
122 smtp_setup_connection, /* setup_connection */
123 smtp_do, /* do_it */
124 smtp_done, /* done */
125 ZERO_NULL, /* do_more */
126 smtp_connect, /* connect_it */
127 smtp_multi_statemach, /* connecting */
128 smtp_doing, /* doing */
129 smtp_getsock, /* proto_getsock */
130 smtp_getsock, /* doing_getsock */
131 ZERO_NULL, /* domore_getsock */
132 ZERO_NULL, /* perform_getsock */
133 smtp_disconnect, /* disconnect */
134 ZERO_NULL, /* write_resp */
135 ZERO_NULL, /* write_resp_hd */
136 ZERO_NULL, /* connection_check */
137 ZERO_NULL, /* attach connection */
138 ZERO_NULL, /* follow */
139 PORT_SMTP, /* defport */
140 CURLPROTO_SMTP, /* protocol */
141 CURLPROTO_SMTP, /* family */
142 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
143 PROTOPT_URLOPTIONS
144 };
145
146 #ifdef USE_SSL
147 /*
148 * SMTPS protocol handler.
149 */
150
151 const struct Curl_handler Curl_handler_smtps = {
152 "smtps", /* scheme */
153 smtp_setup_connection, /* setup_connection */
154 smtp_do, /* do_it */
155 smtp_done, /* done */
156 ZERO_NULL, /* do_more */
157 smtp_connect, /* connect_it */
158 smtp_multi_statemach, /* connecting */
159 smtp_doing, /* doing */
160 smtp_getsock, /* proto_getsock */
161 smtp_getsock, /* doing_getsock */
162 ZERO_NULL, /* domore_getsock */
163 ZERO_NULL, /* perform_getsock */
164 smtp_disconnect, /* disconnect */
165 ZERO_NULL, /* write_resp */
166 ZERO_NULL, /* write_resp_hd */
167 ZERO_NULL, /* connection_check */
168 ZERO_NULL, /* attach connection */
169 ZERO_NULL, /* follow */
170 PORT_SMTPS, /* defport */
171 CURLPROTO_SMTPS, /* protocol */
172 CURLPROTO_SMTP, /* family */
173 PROTOPT_CLOSEACTION | PROTOPT_SSL
174 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
175 };
176 #endif
177
178 /* SASL parameters for the smtp protocol */
179 static const struct SASLproto saslsmtp = {
180 "smtp", /* The service name */
181 smtp_perform_auth, /* Send authentication command */
182 smtp_continue_auth, /* Send authentication continuation */
183 smtp_cancel_auth, /* Cancel authentication */
184 smtp_get_message, /* Get SASL response message */
185 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
186 334, /* Code received when continuation is expected */
187 235, /* Code to receive upon authentication success */
188 SASL_AUTH_DEFAULT, /* Default mechanisms */
189 SASL_FLAG_BASE64 /* Configuration flags */
190 };
191
192 #ifdef USE_SSL
smtp_to_smtps(struct connectdata * conn)193 static void smtp_to_smtps(struct connectdata *conn)
194 {
195 /* Change the connection handler */
196 conn->handler = &Curl_handler_smtps;
197
198 /* Set the connection's upgraded to TLS flag */
199 conn->bits.tls_upgraded = TRUE;
200 }
201 #else
202 #define smtp_to_smtps(x) Curl_nop_stmt
203 #endif
204
205 /***********************************************************************
206 *
207 * smtp_endofresp()
208 *
209 * Checks for an ending SMTP status code at the start of the given string, but
210 * also detects various capabilities from the EHLO response including the
211 * supported authentication mechanisms.
212 */
smtp_endofresp(struct Curl_easy * data,struct connectdata * conn,char * line,size_t len,int * resp)213 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
214 char *line, size_t len, int *resp)
215 {
216 struct smtp_conn *smtpc = &conn->proto.smtpc;
217 bool result = FALSE;
218 (void)data;
219
220 /* Nothing for us */
221 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
222 return FALSE;
223
224 /* Do we have a command response? This should be the response code followed
225 by a space and optionally some text as per RFC-5321 and as outlined in
226 Section 4. Examples of RFC-4954 but some email servers ignore this and
227 only send the response code instead as per Section 4.2. */
228 if(line[3] == ' ' || len == 5) {
229 char tmpline[6];
230
231 result = TRUE;
232 memset(tmpline, '\0', sizeof(tmpline));
233 memcpy(tmpline, line, (len == 5 ? 5 : 3));
234 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
235
236 /* Make sure real server never sends internal value */
237 if(*resp == 1)
238 *resp = 0;
239 }
240 /* Do we have a multiline (continuation) response? */
241 else if(line[3] == '-' &&
242 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
243 result = TRUE;
244 *resp = 1; /* Internal response code */
245 }
246
247 return result;
248 }
249
250 /***********************************************************************
251 *
252 * smtp_get_message()
253 *
254 * Gets the authentication message from the response buffer.
255 */
smtp_get_message(struct Curl_easy * data,struct bufref * out)256 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
257 {
258 char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
259 size_t len = data->conn->proto.smtpc.pp.nfinal;
260
261 if(len > 4) {
262 /* Find the start of the message */
263 len -= 4;
264 for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
265 ;
266
267 /* Find the end of the message */
268 while(len--)
269 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
270 message[len] != '\t')
271 break;
272
273 /* Terminate the message */
274 message[++len] = '\0';
275 Curl_bufref_set(out, message, len, NULL);
276 }
277 else
278 /* junk input => zero length output */
279 Curl_bufref_set(out, "", 0, NULL);
280
281 return CURLE_OK;
282 }
283
284 /***********************************************************************
285 *
286 * smtp_state()
287 *
288 * This is the ONLY way to change SMTP state!
289 */
smtp_state(struct Curl_easy * data,smtpstate newstate)290 static void smtp_state(struct Curl_easy *data, smtpstate newstate)
291 {
292 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
293 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
294 /* for debug purposes */
295 static const char * const names[] = {
296 "STOP",
297 "SERVERGREET",
298 "EHLO",
299 "HELO",
300 "STARTTLS",
301 "UPGRADETLS",
302 "AUTH",
303 "COMMAND",
304 "MAIL",
305 "RCPT",
306 "DATA",
307 "POSTDATA",
308 "QUIT",
309 /* LAST */
310 };
311
312 if(smtpc->state != newstate)
313 CURL_TRC_SMTP(data, "state change from %s to %s",
314 names[smtpc->state], names[newstate]);
315 #endif
316
317 smtpc->state = newstate;
318 }
319
320 /***********************************************************************
321 *
322 * smtp_perform_ehlo()
323 *
324 * Sends the EHLO command to not only initialise communication with the ESMTP
325 * server but to also obtain a list of server side supported capabilities.
326 */
smtp_perform_ehlo(struct Curl_easy * data)327 static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
328 {
329 CURLcode result = CURLE_OK;
330 struct connectdata *conn = data->conn;
331 struct smtp_conn *smtpc = &conn->proto.smtpc;
332
333 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
334 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
335 used for esmtp connections */
336 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
337 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
338
339 /* Send the EHLO command */
340 result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
341
342 if(!result)
343 smtp_state(data, SMTP_EHLO);
344
345 return result;
346 }
347
348 /***********************************************************************
349 *
350 * smtp_perform_helo()
351 *
352 * Sends the HELO command to initialise communication with the SMTP server.
353 */
smtp_perform_helo(struct Curl_easy * data,struct connectdata * conn)354 static CURLcode smtp_perform_helo(struct Curl_easy *data,
355 struct connectdata *conn)
356 {
357 CURLcode result = CURLE_OK;
358 struct smtp_conn *smtpc = &conn->proto.smtpc;
359
360 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
361 in smtp connections */
362
363 /* Send the HELO command */
364 result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
365
366 if(!result)
367 smtp_state(data, SMTP_HELO);
368
369 return result;
370 }
371
372 /***********************************************************************
373 *
374 * smtp_perform_starttls()
375 *
376 * Sends the STLS command to start the upgrade to TLS.
377 */
smtp_perform_starttls(struct Curl_easy * data,struct connectdata * conn)378 static CURLcode smtp_perform_starttls(struct Curl_easy *data,
379 struct connectdata *conn)
380 {
381 /* Send the STARTTLS command */
382 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
383 "%s", "STARTTLS");
384
385 if(!result)
386 smtp_state(data, SMTP_STARTTLS);
387
388 return result;
389 }
390
391 /***********************************************************************
392 *
393 * smtp_perform_upgrade_tls()
394 *
395 * Performs the upgrade to TLS.
396 */
smtp_perform_upgrade_tls(struct Curl_easy * data)397 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
398 {
399 /* Start the SSL connection */
400 struct connectdata *conn = data->conn;
401 struct smtp_conn *smtpc = &conn->proto.smtpc;
402 CURLcode result;
403 bool ssldone = FALSE;
404
405 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
406 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
407 if(result)
408 goto out;
409 }
410
411 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
412 if(!result) {
413 smtpc->ssldone = ssldone;
414 if(smtpc->state != SMTP_UPGRADETLS)
415 smtp_state(data, SMTP_UPGRADETLS);
416
417 if(smtpc->ssldone) {
418 smtp_to_smtps(conn);
419 result = smtp_perform_ehlo(data);
420 }
421 }
422 out:
423 return result;
424 }
425
426 /***********************************************************************
427 *
428 * smtp_perform_auth()
429 *
430 * Sends an AUTH command allowing the client to login with the given SASL
431 * authentication mechanism.
432 */
smtp_perform_auth(struct Curl_easy * data,const char * mech,const struct bufref * initresp)433 static CURLcode smtp_perform_auth(struct Curl_easy *data,
434 const char *mech,
435 const struct bufref *initresp)
436 {
437 CURLcode result = CURLE_OK;
438 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
439 const char *ir = (const char *) Curl_bufref_ptr(initresp);
440
441 if(ir) { /* AUTH <mech> ...<crlf> */
442 /* Send the AUTH command with the initial response */
443 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
444 }
445 else {
446 /* Send the AUTH command */
447 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
448 }
449
450 return result;
451 }
452
453 /***********************************************************************
454 *
455 * smtp_continue_auth()
456 *
457 * Sends SASL continuation data.
458 */
smtp_continue_auth(struct Curl_easy * data,const char * mech,const struct bufref * resp)459 static CURLcode smtp_continue_auth(struct Curl_easy *data,
460 const char *mech,
461 const struct bufref *resp)
462 {
463 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
464
465 (void)mech;
466
467 return Curl_pp_sendf(data, &smtpc->pp,
468 "%s", (const char *) Curl_bufref_ptr(resp));
469 }
470
471 /***********************************************************************
472 *
473 * smtp_cancel_auth()
474 *
475 * Sends SASL cancellation.
476 */
smtp_cancel_auth(struct Curl_easy * data,const char * mech)477 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
478 {
479 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
480
481 (void)mech;
482
483 return Curl_pp_sendf(data, &smtpc->pp, "*");
484 }
485
486 /***********************************************************************
487 *
488 * smtp_perform_authentication()
489 *
490 * Initiates the authentication sequence, with the appropriate SASL
491 * authentication mechanism.
492 */
smtp_perform_authentication(struct Curl_easy * data)493 static CURLcode smtp_perform_authentication(struct Curl_easy *data)
494 {
495 CURLcode result = CURLE_OK;
496 struct connectdata *conn = data->conn;
497 struct smtp_conn *smtpc = &conn->proto.smtpc;
498 saslprogress progress;
499
500 /* Check we have enough data to authenticate with, and the
501 server supports authentication, and end the connect phase if not */
502 if(!smtpc->auth_supported ||
503 !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
504 smtp_state(data, SMTP_STOP);
505 return result;
506 }
507
508 /* Calculate the SASL login details */
509 result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
510
511 if(!result) {
512 if(progress == SASL_INPROGRESS)
513 smtp_state(data, SMTP_AUTH);
514 else {
515 /* Other mechanisms not supported */
516 infof(data, "No known authentication mechanisms supported");
517 result = CURLE_LOGIN_DENIED;
518 }
519 }
520
521 return result;
522 }
523
524 /***********************************************************************
525 *
526 * smtp_perform_command()
527 *
528 * Sends a SMTP based command.
529 */
smtp_perform_command(struct Curl_easy * data)530 static CURLcode smtp_perform_command(struct Curl_easy *data)
531 {
532 CURLcode result = CURLE_OK;
533 struct connectdata *conn = data->conn;
534 struct SMTP *smtp = data->req.p.smtp;
535
536 if(smtp->rcpt) {
537 /* We notify the server we are sending UTF-8 data if a) it supports the
538 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
539 either the local address or hostname parts. This is regardless of
540 whether the hostname is encoded using IDN ACE */
541 bool utf8 = FALSE;
542
543 if((!smtp->custom) || (!smtp->custom[0])) {
544 char *address = NULL;
545 struct hostname host = { NULL, NULL, NULL, NULL };
546
547 /* Parse the mailbox to verify into the local address and hostname
548 parts, converting the hostname to an IDN A-label if necessary */
549 result = smtp_parse_address(smtp->rcpt->data,
550 &address, &host);
551 if(result)
552 return result;
553
554 /* Establish whether we should report SMTPUTF8 to the server for this
555 mailbox as per RFC-6531 sect. 3.1 point 6 */
556 utf8 = (conn->proto.smtpc.utf8_supported) &&
557 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
558 (!Curl_is_ASCII_name(host.name)));
559
560 /* Send the VRFY command (Note: The hostname part may be absent when the
561 host is a local system) */
562 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
563 address,
564 host.name ? "@" : "",
565 host.name ? host.name : "",
566 utf8 ? " SMTPUTF8" : "");
567
568 Curl_free_idnconverted_hostname(&host);
569 free(address);
570 }
571 else {
572 /* Establish whether we should report that we support SMTPUTF8 for EXPN
573 commands to the server as per RFC-6531 sect. 3.1 point 6 */
574 utf8 = (conn->proto.smtpc.utf8_supported) &&
575 (!strcmp(smtp->custom, "EXPN"));
576
577 /* Send the custom recipient based command such as the EXPN command */
578 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
579 "%s %s%s", smtp->custom,
580 smtp->rcpt->data,
581 utf8 ? " SMTPUTF8" : "");
582 }
583 }
584 else
585 /* Send the non-recipient based command such as HELP */
586 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
587 smtp->custom && smtp->custom[0] != '\0' ?
588 smtp->custom : "HELP");
589
590 if(!result)
591 smtp_state(data, SMTP_COMMAND);
592
593 return result;
594 }
595
596 /***********************************************************************
597 *
598 * smtp_perform_mail()
599 *
600 * Sends an MAIL command to initiate the upload of a message.
601 */
smtp_perform_mail(struct Curl_easy * data)602 static CURLcode smtp_perform_mail(struct Curl_easy *data)
603 {
604 char *from = NULL;
605 char *auth = NULL;
606 char *size = NULL;
607 CURLcode result = CURLE_OK;
608 struct connectdata *conn = data->conn;
609
610 /* We notify the server we are sending UTF-8 data if a) it supports the
611 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
612 either the local address or hostname parts. This is regardless of
613 whether the hostname is encoded using IDN ACE */
614 bool utf8 = FALSE;
615
616 /* Calculate the FROM parameter */
617 if(data->set.str[STRING_MAIL_FROM]) {
618 char *address = NULL;
619 struct hostname host = { NULL, NULL, NULL, NULL };
620
621 /* Parse the FROM mailbox into the local address and hostname parts,
622 converting the hostname to an IDN A-label if necessary */
623 result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
624 &address, &host);
625 if(result)
626 goto out;
627
628 /* Establish whether we should report SMTPUTF8 to the server for this
629 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
630 utf8 = (conn->proto.smtpc.utf8_supported) &&
631 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
632 (!Curl_is_ASCII_name(host.name)));
633
634 if(host.name) {
635 from = aprintf("<%s@%s>", address, host.name);
636
637 Curl_free_idnconverted_hostname(&host);
638 }
639 else
640 /* An invalid mailbox was provided but we will simply let the server
641 worry about that and reply with a 501 error */
642 from = aprintf("<%s>", address);
643
644 free(address);
645 }
646 else
647 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
648 from = strdup("<>");
649
650 if(!from) {
651 result = CURLE_OUT_OF_MEMORY;
652 goto out;
653 }
654
655 /* Calculate the optional AUTH parameter */
656 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
657 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
658 char *address = NULL;
659 struct hostname host = { NULL, NULL, NULL, NULL };
660
661 /* Parse the AUTH mailbox into the local address and hostname parts,
662 converting the hostname to an IDN A-label if necessary */
663 result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
664 &address, &host);
665 if(result)
666 goto out;
667
668 /* Establish whether we should report SMTPUTF8 to the server for this
669 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
670 if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
671 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
672 (!Curl_is_ASCII_name(host.name))))
673 utf8 = TRUE;
674
675 if(host.name) {
676 auth = aprintf("<%s@%s>", address, host.name);
677
678 Curl_free_idnconverted_hostname(&host);
679 }
680 else
681 /* An invalid mailbox was provided but we will simply let the server
682 worry about it */
683 auth = aprintf("<%s>", address);
684 free(address);
685 }
686 else
687 /* Empty AUTH, RFC-2554, sect. 5 */
688 auth = strdup("<>");
689
690 if(!auth) {
691 result = CURLE_OUT_OF_MEMORY;
692 goto out;
693 }
694 }
695
696 #ifndef CURL_DISABLE_MIME
697 /* Prepare the mime data if some. */
698 if(data->set.mimepost.kind != MIMEKIND_NONE) {
699 /* Use the whole structure as data. */
700 data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY;
701
702 /* Add external headers and mime version. */
703 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
704 result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
705 NULL, MIMESTRATEGY_MAIL);
706
707 if(!result)
708 if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
709 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
710 "Mime-Version: 1.0");
711
712 if(!result)
713 result = Curl_creader_set_mime(data, &data->set.mimepost);
714 if(result)
715 goto out;
716 data->state.infilesize = Curl_creader_total_length(data);
717 }
718 else
719 #endif
720 {
721 result = Curl_creader_set_fread(data, data->state.infilesize);
722 if(result)
723 goto out;
724 }
725
726 /* Calculate the optional SIZE parameter */
727 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
728 size = aprintf("%" FMT_OFF_T, data->state.infilesize);
729
730 if(!size) {
731 result = CURLE_OUT_OF_MEMORY;
732 goto out;
733 }
734 }
735
736 /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8
737 based address then quickly scan through the recipient list and check if
738 any there do, as we need to correctly identify our support for SMTPUTF8
739 in the envelope, as per RFC-6531 sect. 3.4 */
740 if(conn->proto.smtpc.utf8_supported && !utf8) {
741 struct SMTP *smtp = data->req.p.smtp;
742 struct curl_slist *rcpt = smtp->rcpt;
743
744 while(rcpt && !utf8) {
745 /* Does the hostname contain non-ASCII characters? */
746 if(!Curl_is_ASCII_name(rcpt->data))
747 utf8 = TRUE;
748
749 rcpt = rcpt->next;
750 }
751 }
752
753 /* Add the client reader doing STMP EOB escaping */
754 result = cr_eob_add(data);
755 if(result)
756 goto out;
757
758 /* Send the MAIL command */
759 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
760 "MAIL FROM:%s%s%s%s%s%s",
761 from, /* Mandatory */
762 auth ? " AUTH=" : "", /* Optional on AUTH support */
763 auth ? auth : "", /* */
764 size ? " SIZE=" : "", /* Optional on SIZE support */
765 size ? size : "", /* */
766 utf8 ? " SMTPUTF8" /* Internationalised mailbox */
767 : ""); /* included in our envelope */
768
769 out:
770 free(from);
771 free(auth);
772 free(size);
773
774 if(!result)
775 smtp_state(data, SMTP_MAIL);
776
777 return result;
778 }
779
780 /***********************************************************************
781 *
782 * smtp_perform_rcpt_to()
783 *
784 * Sends a RCPT TO command for a given recipient as part of the message upload
785 * process.
786 */
smtp_perform_rcpt_to(struct Curl_easy * data)787 static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
788 {
789 CURLcode result = CURLE_OK;
790 struct connectdata *conn = data->conn;
791 struct SMTP *smtp = data->req.p.smtp;
792 char *address = NULL;
793 struct hostname host = { NULL, NULL, NULL, NULL };
794
795 /* Parse the recipient mailbox into the local address and hostname parts,
796 converting the hostname to an IDN A-label if necessary */
797 result = smtp_parse_address(smtp->rcpt->data,
798 &address, &host);
799 if(result)
800 return result;
801
802 /* Send the RCPT TO command */
803 if(host.name)
804 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
805 address, host.name);
806 else
807 /* An invalid mailbox was provided but we will simply let the server worry
808 about that and reply with a 501 error */
809 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
810 address);
811
812 Curl_free_idnconverted_hostname(&host);
813 free(address);
814
815 if(!result)
816 smtp_state(data, SMTP_RCPT);
817
818 return result;
819 }
820
821 /***********************************************************************
822 *
823 * smtp_perform_quit()
824 *
825 * Performs the quit action prior to sclose() being called.
826 */
smtp_perform_quit(struct Curl_easy * data,struct connectdata * conn)827 static CURLcode smtp_perform_quit(struct Curl_easy *data,
828 struct connectdata *conn)
829 {
830 /* Send the QUIT command */
831 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
832
833 if(!result)
834 smtp_state(data, SMTP_QUIT);
835
836 return result;
837 }
838
839 /* For the initial server greeting */
smtp_state_servergreet_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)840 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
841 int smtpcode,
842 smtpstate instate)
843 {
844 CURLcode result = CURLE_OK;
845 (void)instate; /* no use for this yet */
846
847 if(smtpcode/100 != 2) {
848 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
849 result = CURLE_WEIRD_SERVER_REPLY;
850 }
851 else
852 result = smtp_perform_ehlo(data);
853
854 return result;
855 }
856
857 /* For STARTTLS responses */
smtp_state_starttls_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)858 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
859 int smtpcode,
860 smtpstate instate)
861 {
862 CURLcode result = CURLE_OK;
863 (void)instate; /* no use for this yet */
864
865 /* Pipelining in response is forbidden. */
866 if(data->conn->proto.smtpc.pp.overflow)
867 return CURLE_WEIRD_SERVER_REPLY;
868
869 if(smtpcode != 220) {
870 if(data->set.use_ssl != CURLUSESSL_TRY) {
871 failf(data, "STARTTLS denied, code %d", smtpcode);
872 result = CURLE_USE_SSL_FAILED;
873 }
874 else
875 result = smtp_perform_authentication(data);
876 }
877 else
878 result = smtp_perform_upgrade_tls(data);
879
880 return result;
881 }
882
883 /* For EHLO responses */
smtp_state_ehlo_resp(struct Curl_easy * data,struct connectdata * conn,int smtpcode,smtpstate instate)884 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
885 struct connectdata *conn, int smtpcode,
886 smtpstate instate)
887 {
888 CURLcode result = CURLE_OK;
889 struct smtp_conn *smtpc = &conn->proto.smtpc;
890 const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
891 size_t len = smtpc->pp.nfinal;
892
893 (void)instate; /* no use for this yet */
894
895 if(smtpcode/100 != 2 && smtpcode != 1) {
896 if(data->set.use_ssl <= CURLUSESSL_TRY
897 || Curl_conn_is_ssl(conn, FIRSTSOCKET))
898 result = smtp_perform_helo(data, conn);
899 else {
900 failf(data, "Remote access denied: %d", smtpcode);
901 result = CURLE_REMOTE_ACCESS_DENIED;
902 }
903 }
904 else if(len >= 4) {
905 line += 4;
906 len -= 4;
907
908 /* Does the server support the STARTTLS capability? */
909 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
910 smtpc->tls_supported = TRUE;
911
912 /* Does the server support the SIZE capability? */
913 else if(len >= 4 && !memcmp(line, "SIZE", 4))
914 smtpc->size_supported = TRUE;
915
916 /* Does the server support the UTF-8 capability? */
917 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
918 smtpc->utf8_supported = TRUE;
919
920 /* Does the server support authentication? */
921 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
922 smtpc->auth_supported = TRUE;
923
924 /* Advance past the AUTH keyword */
925 line += 5;
926 len -= 5;
927
928 /* Loop through the data line */
929 for(;;) {
930 size_t llen;
931 size_t wordlen;
932 unsigned short mechbit;
933
934 while(len &&
935 (*line == ' ' || *line == '\t' ||
936 *line == '\r' || *line == '\n')) {
937
938 line++;
939 len--;
940 }
941
942 if(!len)
943 break;
944
945 /* Extract the word */
946 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
947 line[wordlen] != '\t' && line[wordlen] != '\r' &&
948 line[wordlen] != '\n';)
949 wordlen++;
950
951 /* Test the word for a matching authentication mechanism */
952 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
953 if(mechbit && llen == wordlen)
954 smtpc->sasl.authmechs |= mechbit;
955
956 line += wordlen;
957 len -= wordlen;
958 }
959 }
960
961 if(smtpcode != 1) {
962 if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
963 /* We do not have a SSL/TLS connection yet, but SSL is requested */
964 if(smtpc->tls_supported)
965 /* Switch to TLS connection now */
966 result = smtp_perform_starttls(data, conn);
967 else if(data->set.use_ssl == CURLUSESSL_TRY)
968 /* Fallback and carry on with authentication */
969 result = smtp_perform_authentication(data);
970 else {
971 failf(data, "STARTTLS not supported.");
972 result = CURLE_USE_SSL_FAILED;
973 }
974 }
975 else
976 result = smtp_perform_authentication(data);
977 }
978 }
979 else {
980 failf(data, "Unexpectedly short EHLO response");
981 result = CURLE_WEIRD_SERVER_REPLY;
982 }
983
984 return result;
985 }
986
987 /* For HELO responses */
smtp_state_helo_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)988 static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
989 smtpstate instate)
990 {
991 CURLcode result = CURLE_OK;
992 (void)instate; /* no use for this yet */
993
994 if(smtpcode/100 != 2) {
995 failf(data, "Remote access denied: %d", smtpcode);
996 result = CURLE_REMOTE_ACCESS_DENIED;
997 }
998 else
999 /* End of connect phase */
1000 smtp_state(data, SMTP_STOP);
1001
1002 return result;
1003 }
1004
1005 /* For SASL authentication responses */
smtp_state_auth_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1006 static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
1007 int smtpcode,
1008 smtpstate instate)
1009 {
1010 CURLcode result = CURLE_OK;
1011 struct connectdata *conn = data->conn;
1012 struct smtp_conn *smtpc = &conn->proto.smtpc;
1013 saslprogress progress;
1014
1015 (void)instate; /* no use for this yet */
1016
1017 result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1018 if(!result)
1019 switch(progress) {
1020 case SASL_DONE:
1021 smtp_state(data, SMTP_STOP); /* Authenticated */
1022 break;
1023 case SASL_IDLE: /* No mechanism left after cancellation */
1024 failf(data, "Authentication cancelled");
1025 result = CURLE_LOGIN_DENIED;
1026 break;
1027 default:
1028 break;
1029 }
1030
1031 return result;
1032 }
1033
1034 /* For command responses */
smtp_state_command_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1035 static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
1036 smtpstate instate)
1037 {
1038 CURLcode result = CURLE_OK;
1039 struct SMTP *smtp = data->req.p.smtp;
1040 char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
1041 size_t len = data->conn->proto.smtpc.pp.nfinal;
1042
1043 (void)instate; /* no use for this yet */
1044
1045 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1046 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1047 failf(data, "Command failed: %d", smtpcode);
1048 result = CURLE_WEIRD_SERVER_REPLY;
1049 }
1050 else {
1051 if(!data->req.no_body)
1052 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
1053
1054 if(smtpcode != 1) {
1055 if(smtp->rcpt) {
1056 smtp->rcpt = smtp->rcpt->next;
1057
1058 if(smtp->rcpt) {
1059 /* Send the next command */
1060 result = smtp_perform_command(data);
1061 }
1062 else
1063 /* End of DO phase */
1064 smtp_state(data, SMTP_STOP);
1065 }
1066 else
1067 /* End of DO phase */
1068 smtp_state(data, SMTP_STOP);
1069 }
1070 }
1071
1072 return result;
1073 }
1074
1075 /* For MAIL responses */
smtp_state_mail_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1076 static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
1077 smtpstate instate)
1078 {
1079 CURLcode result = CURLE_OK;
1080 (void)instate; /* no use for this yet */
1081
1082 if(smtpcode/100 != 2) {
1083 failf(data, "MAIL failed: %d", smtpcode);
1084 result = CURLE_SEND_ERROR;
1085 }
1086 else
1087 /* Start the RCPT TO command */
1088 result = smtp_perform_rcpt_to(data);
1089
1090 return result;
1091 }
1092
1093 /* For RCPT responses */
smtp_state_rcpt_resp(struct Curl_easy * data,struct connectdata * conn,int smtpcode,smtpstate instate)1094 static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
1095 struct connectdata *conn, int smtpcode,
1096 smtpstate instate)
1097 {
1098 CURLcode result = CURLE_OK;
1099 struct SMTP *smtp = data->req.p.smtp;
1100 bool is_smtp_err = FALSE;
1101 bool is_smtp_blocking_err = FALSE;
1102
1103 (void)instate; /* no use for this yet */
1104
1105 is_smtp_err = (smtpcode/100 != 2);
1106
1107 /* If there is multiple RCPT TO to be issued, it is possible to ignore errors
1108 and proceed with only the valid addresses. */
1109 is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails);
1110
1111 if(is_smtp_err) {
1112 /* Remembering the last failure which we can report if all "RCPT TO" have
1113 failed and we cannot proceed. */
1114 smtp->rcpt_last_error = smtpcode;
1115
1116 if(is_smtp_blocking_err) {
1117 failf(data, "RCPT failed: %d", smtpcode);
1118 result = CURLE_SEND_ERROR;
1119 }
1120 }
1121 else {
1122 /* Some RCPT TO commands have succeeded. */
1123 smtp->rcpt_had_ok = TRUE;
1124 }
1125
1126 if(!is_smtp_blocking_err) {
1127 smtp->rcpt = smtp->rcpt->next;
1128
1129 if(smtp->rcpt)
1130 /* Send the next RCPT TO command */
1131 result = smtp_perform_rcpt_to(data);
1132 else {
1133 /* We were not able to issue a successful RCPT TO command while going
1134 over recipients (potentially multiple). Sending back last error. */
1135 if(!smtp->rcpt_had_ok) {
1136 failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1137 result = CURLE_SEND_ERROR;
1138 }
1139 else {
1140 /* Send the DATA command */
1141 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
1142
1143 if(!result)
1144 smtp_state(data, SMTP_DATA);
1145 }
1146 }
1147 }
1148
1149 return result;
1150 }
1151
1152 /* For DATA response */
smtp_state_data_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1153 static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
1154 smtpstate instate)
1155 {
1156 CURLcode result = CURLE_OK;
1157 (void)instate; /* no use for this yet */
1158
1159 if(smtpcode != 354) {
1160 failf(data, "DATA failed: %d", smtpcode);
1161 result = CURLE_SEND_ERROR;
1162 }
1163 else {
1164 /* Set the progress upload size */
1165 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1166
1167 /* SMTP upload */
1168 Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
1169
1170 /* End of DO phase */
1171 smtp_state(data, SMTP_STOP);
1172 }
1173
1174 return result;
1175 }
1176
1177 /* For POSTDATA responses, which are received after the entire DATA
1178 part has been sent to the server */
smtp_state_postdata_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1179 static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
1180 int smtpcode,
1181 smtpstate instate)
1182 {
1183 CURLcode result = CURLE_OK;
1184
1185 (void)instate; /* no use for this yet */
1186
1187 if(smtpcode != 250)
1188 result = CURLE_WEIRD_SERVER_REPLY;
1189
1190 /* End of DONE phase */
1191 smtp_state(data, SMTP_STOP);
1192
1193 return result;
1194 }
1195
smtp_statemachine(struct Curl_easy * data,struct connectdata * conn)1196 static CURLcode smtp_statemachine(struct Curl_easy *data,
1197 struct connectdata *conn)
1198 {
1199 CURLcode result = CURLE_OK;
1200 int smtpcode;
1201 struct smtp_conn *smtpc = &conn->proto.smtpc;
1202 struct pingpong *pp = &smtpc->pp;
1203 size_t nread = 0;
1204
1205 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1206 upgrade_tls:
1207 if(smtpc->state == SMTP_UPGRADETLS)
1208 return smtp_perform_upgrade_tls(data);
1209
1210 /* Flush any data that needs to be sent */
1211 if(pp->sendleft)
1212 return Curl_pp_flushsend(data, pp);
1213
1214 do {
1215 /* Read the response from the server */
1216 result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread);
1217 if(result)
1218 return result;
1219
1220 /* Store the latest response for later retrieval if necessary */
1221 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1222 data->info.httpcode = smtpcode;
1223
1224 if(!smtpcode)
1225 break;
1226
1227 /* We have now received a full SMTP server response */
1228 switch(smtpc->state) {
1229 case SMTP_SERVERGREET:
1230 result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
1231 break;
1232
1233 case SMTP_EHLO:
1234 result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
1235 break;
1236
1237 case SMTP_HELO:
1238 result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
1239 break;
1240
1241 case SMTP_STARTTLS:
1242 result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
1243 /* During UPGRADETLS, leave the read loop as we need to connect
1244 * (e.g. TLS handshake) before we continue sending/receiving. */
1245 if(!result && (smtpc->state == SMTP_UPGRADETLS))
1246 goto upgrade_tls;
1247 break;
1248
1249 case SMTP_AUTH:
1250 result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
1251 break;
1252
1253 case SMTP_COMMAND:
1254 result = smtp_state_command_resp(data, smtpcode, smtpc->state);
1255 break;
1256
1257 case SMTP_MAIL:
1258 result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
1259 break;
1260
1261 case SMTP_RCPT:
1262 result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
1263 break;
1264
1265 case SMTP_DATA:
1266 result = smtp_state_data_resp(data, smtpcode, smtpc->state);
1267 break;
1268
1269 case SMTP_POSTDATA:
1270 result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
1271 break;
1272
1273 case SMTP_QUIT:
1274 default:
1275 /* internal error */
1276 smtp_state(data, SMTP_STOP);
1277 break;
1278 }
1279 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1280
1281 return result;
1282 }
1283
1284 /* Called repeatedly until done from multi.c */
smtp_multi_statemach(struct Curl_easy * data,bool * done)1285 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1286 {
1287 CURLcode result = CURLE_OK;
1288 struct connectdata *conn = data->conn;
1289 struct smtp_conn *smtpc = &conn->proto.smtpc;
1290
1291 if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !smtpc->ssldone) {
1292 bool ssldone = FALSE;
1293 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
1294 smtpc->ssldone = ssldone;
1295 if(result || !smtpc->ssldone)
1296 return result;
1297 }
1298
1299 result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1300 *done = (smtpc->state == SMTP_STOP);
1301
1302 return result;
1303 }
1304
smtp_block_statemach(struct Curl_easy * data,struct connectdata * conn,bool disconnecting)1305 static CURLcode smtp_block_statemach(struct Curl_easy *data,
1306 struct connectdata *conn,
1307 bool disconnecting)
1308 {
1309 CURLcode result = CURLE_OK;
1310 struct smtp_conn *smtpc = &conn->proto.smtpc;
1311
1312 while(smtpc->state != SMTP_STOP && !result)
1313 result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1314
1315 return result;
1316 }
1317
1318 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1319 required */
smtp_init(struct Curl_easy * data)1320 static CURLcode smtp_init(struct Curl_easy *data)
1321 {
1322 CURLcode result = CURLE_OK;
1323 struct SMTP *smtp;
1324
1325 smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
1326 if(!smtp)
1327 result = CURLE_OUT_OF_MEMORY;
1328
1329 return result;
1330 }
1331
1332 /* For the SMTP "protocol connect" and "doing" phases only */
smtp_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)1333 static int smtp_getsock(struct Curl_easy *data,
1334 struct connectdata *conn, curl_socket_t *socks)
1335 {
1336 return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
1337 }
1338
1339 /***********************************************************************
1340 *
1341 * smtp_connect()
1342 *
1343 * This function should do everything that is to be considered a part of
1344 * the connection phase.
1345 *
1346 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1347 * connect phase is done when this function returns, or FALSE if not.
1348 */
smtp_connect(struct Curl_easy * data,bool * done)1349 static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
1350 {
1351 CURLcode result = CURLE_OK;
1352 struct connectdata *conn = data->conn;
1353 struct smtp_conn *smtpc = &conn->proto.smtpc;
1354 struct pingpong *pp = &smtpc->pp;
1355
1356 *done = FALSE; /* default to not done yet */
1357
1358 /* We always support persistent connections in SMTP */
1359 connkeep(conn, "SMTP default");
1360
1361 PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
1362
1363 /* Initialize the SASL storage */
1364 Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1365
1366 /* Initialise the pingpong layer */
1367 Curl_pp_init(pp);
1368
1369 /* Parse the URL options */
1370 result = smtp_parse_url_options(conn);
1371 if(result)
1372 return result;
1373
1374 /* Parse the URL path */
1375 result = smtp_parse_url_path(data);
1376 if(result)
1377 return result;
1378
1379 /* Start off waiting for the server greeting response */
1380 smtp_state(data, SMTP_SERVERGREET);
1381
1382 result = smtp_multi_statemach(data, done);
1383
1384 return result;
1385 }
1386
1387 /***********************************************************************
1388 *
1389 * smtp_done()
1390 *
1391 * The DONE function. This does what needs to be done after a single DO has
1392 * performed.
1393 *
1394 * Input argument is already checked for validity.
1395 */
smtp_done(struct Curl_easy * data,CURLcode status,bool premature)1396 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
1397 bool premature)
1398 {
1399 CURLcode result = CURLE_OK;
1400 struct connectdata *conn = data->conn;
1401 struct SMTP *smtp = data->req.p.smtp;
1402
1403 (void)premature;
1404
1405 if(!smtp)
1406 return CURLE_OK;
1407
1408 /* Cleanup our per-request based variables */
1409 Curl_safefree(smtp->custom);
1410
1411 if(status) {
1412 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1413 result = status; /* use the already set error code */
1414 }
1415 else if(!data->set.connect_only && data->set.mail_rcpt &&
1416 (data->state.upload || IS_MIME_POST(data))) {
1417
1418 smtp_state(data, SMTP_POSTDATA);
1419
1420 /* Run the state-machine */
1421 result = smtp_block_statemach(data, conn, FALSE);
1422 }
1423
1424 /* Clear the transfer mode for the next request */
1425 smtp->transfer = PPTRANSFER_BODY;
1426 CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
1427 status, premature, result);
1428 return result;
1429 }
1430
1431 /***********************************************************************
1432 *
1433 * smtp_perform()
1434 *
1435 * This is the actual DO function for SMTP. Transfer a mail, send a command
1436 * or get some data according to the options previously setup.
1437 */
smtp_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)1438 static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
1439 bool *dophase_done)
1440 {
1441 /* This is SMTP and no proxy */
1442 CURLcode result = CURLE_OK;
1443 struct SMTP *smtp = data->req.p.smtp;
1444
1445 CURL_TRC_SMTP(data, "smtp_perform(), start");
1446
1447 if(data->req.no_body) {
1448 /* Requested no body means no transfer */
1449 smtp->transfer = PPTRANSFER_INFO;
1450 }
1451
1452 *dophase_done = FALSE; /* not done yet */
1453
1454 /* Store the first recipient (or NULL if not specified) */
1455 smtp->rcpt = data->set.mail_rcpt;
1456
1457 /* Track of whether we have successfully sent at least one RCPT TO command */
1458 smtp->rcpt_had_ok = FALSE;
1459
1460 /* Track of the last error we have received by sending RCPT TO command */
1461 smtp->rcpt_last_error = 0;
1462
1463 /* Initial data character is the first character in line: it is implicitly
1464 preceded by a virtual CRLF. */
1465 smtp->trailing_crlf = TRUE;
1466 smtp->eob = 2;
1467
1468 /* Start the first command in the DO phase */
1469 if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
1470 /* MAIL transfer */
1471 result = smtp_perform_mail(data);
1472 else
1473 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1474 result = smtp_perform_command(data);
1475
1476 if(result)
1477 goto out;
1478
1479 /* Run the state-machine */
1480 result = smtp_multi_statemach(data, dophase_done);
1481
1482 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1483
1484 out:
1485 CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
1486 result, *connected, *dophase_done);
1487 return result;
1488 }
1489
1490 /***********************************************************************
1491 *
1492 * smtp_do()
1493 *
1494 * This function is registered as 'curl_do' function. It decodes the path
1495 * parts etc as a wrapper to the actual DO function (smtp_perform).
1496 *
1497 * The input argument is already checked for validity.
1498 */
smtp_do(struct Curl_easy * data,bool * done)1499 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1500 {
1501 CURLcode result = CURLE_OK;
1502 DEBUGASSERT(data);
1503 DEBUGASSERT(data->conn);
1504 *done = FALSE; /* default to false */
1505
1506 /* Parse the custom request */
1507 result = smtp_parse_custom_request(data);
1508 if(result)
1509 return result;
1510
1511 result = smtp_regular_transfer(data, done);
1512 CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
1513 return result;
1514 }
1515
1516 /***********************************************************************
1517 *
1518 * smtp_disconnect()
1519 *
1520 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1521 * resources. BLOCKING.
1522 */
smtp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)1523 static CURLcode smtp_disconnect(struct Curl_easy *data,
1524 struct connectdata *conn,
1525 bool dead_connection)
1526 {
1527 struct smtp_conn *smtpc = &conn->proto.smtpc;
1528 (void)data;
1529
1530 /* We cannot send quit unconditionally. If this connection is stale or
1531 bad in any way, sending quit and waiting around here will make the
1532 disconnect wait in vain and cause more problems than we need to. */
1533
1534 if(!dead_connection && conn->bits.protoconnstart) {
1535 if(!smtp_perform_quit(data, conn))
1536 (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1537 }
1538
1539 /* Disconnect from the server */
1540 Curl_pp_disconnect(&smtpc->pp);
1541
1542 /* Cleanup the SASL module */
1543 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1544
1545 /* Cleanup our connection based variables */
1546 Curl_safefree(smtpc->domain);
1547 CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
1548
1549 return CURLE_OK;
1550 }
1551
1552 /* Call this when the DO phase has completed */
smtp_dophase_done(struct Curl_easy * data,bool connected)1553 static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
1554 {
1555 struct SMTP *smtp = data->req.p.smtp;
1556
1557 (void)connected;
1558
1559 if(smtp->transfer != PPTRANSFER_BODY)
1560 /* no data to transfer */
1561 Curl_xfer_setup_nop(data);
1562
1563 return CURLE_OK;
1564 }
1565
1566 /* Called from multi.c while DOing */
smtp_doing(struct Curl_easy * data,bool * dophase_done)1567 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1568 {
1569 CURLcode result = smtp_multi_statemach(data, dophase_done);
1570
1571 if(result)
1572 DEBUGF(infof(data, "DO phase failed"));
1573 else if(*dophase_done) {
1574 result = smtp_dophase_done(data, FALSE /* not connected */);
1575
1576 DEBUGF(infof(data, "DO phase is complete"));
1577 }
1578
1579 CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done);
1580 return result;
1581 }
1582
1583 /***********************************************************************
1584 *
1585 * smtp_regular_transfer()
1586 *
1587 * The input argument is already checked for validity.
1588 *
1589 * Performs all commands done before a regular transfer between a local and a
1590 * remote host.
1591 */
smtp_regular_transfer(struct Curl_easy * data,bool * dophase_done)1592 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1593 bool *dophase_done)
1594 {
1595 CURLcode result = CURLE_OK;
1596 bool connected = FALSE;
1597
1598 /* Make sure size is unknown at this point */
1599 data->req.size = -1;
1600
1601 /* Set the progress data */
1602 Curl_pgrsSetUploadCounter(data, 0);
1603 Curl_pgrsSetDownloadCounter(data, 0);
1604 Curl_pgrsSetUploadSize(data, -1);
1605 Curl_pgrsSetDownloadSize(data, -1);
1606
1607 /* Carry out the perform */
1608 result = smtp_perform(data, &connected, dophase_done);
1609
1610 /* Perform post DO phase operations if necessary */
1611 if(!result && *dophase_done)
1612 result = smtp_dophase_done(data, connected);
1613
1614 CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
1615 result, *dophase_done);
1616 return result;
1617 }
1618
smtp_setup_connection(struct Curl_easy * data,struct connectdata * conn)1619 static CURLcode smtp_setup_connection(struct Curl_easy *data,
1620 struct connectdata *conn)
1621 {
1622 CURLcode result;
1623
1624 /* Clear the TLS upgraded flag */
1625 conn->bits.tls_upgraded = FALSE;
1626
1627 /* Initialise the SMTP layer */
1628 result = smtp_init(data);
1629 CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
1630 return result;
1631 }
1632
1633 /***********************************************************************
1634 *
1635 * smtp_parse_url_options()
1636 *
1637 * Parse the URL login options.
1638 */
smtp_parse_url_options(struct connectdata * conn)1639 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1640 {
1641 CURLcode result = CURLE_OK;
1642 struct smtp_conn *smtpc = &conn->proto.smtpc;
1643 const char *ptr = conn->options;
1644
1645 while(!result && ptr && *ptr) {
1646 const char *key = ptr;
1647 const char *value;
1648
1649 while(*ptr && *ptr != '=')
1650 ptr++;
1651
1652 value = ptr + 1;
1653
1654 while(*ptr && *ptr != ';')
1655 ptr++;
1656
1657 if(strncasecompare(key, "AUTH=", 5))
1658 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1659 value, ptr - value);
1660 else
1661 result = CURLE_URL_MALFORMAT;
1662
1663 if(*ptr == ';')
1664 ptr++;
1665 }
1666
1667 return result;
1668 }
1669
1670 /***********************************************************************
1671 *
1672 * smtp_parse_url_path()
1673 *
1674 * Parse the URL path into separate path components.
1675 */
smtp_parse_url_path(struct Curl_easy * data)1676 static CURLcode smtp_parse_url_path(struct Curl_easy *data)
1677 {
1678 /* The SMTP struct is already initialised in smtp_connect() */
1679 struct connectdata *conn = data->conn;
1680 struct smtp_conn *smtpc = &conn->proto.smtpc;
1681 const char *path = &data->state.up.path[1]; /* skip leading path */
1682 char localhost[HOSTNAME_MAX + 1];
1683
1684 /* Calculate the path if necessary */
1685 if(!*path) {
1686 if(!Curl_gethostname(localhost, sizeof(localhost)))
1687 path = localhost;
1688 else
1689 path = "localhost";
1690 }
1691
1692 /* URL decode the path and use it as the domain in our EHLO */
1693 return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
1694 }
1695
1696 /***********************************************************************
1697 *
1698 * smtp_parse_custom_request()
1699 *
1700 * Parse the custom request.
1701 */
smtp_parse_custom_request(struct Curl_easy * data)1702 static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
1703 {
1704 CURLcode result = CURLE_OK;
1705 struct SMTP *smtp = data->req.p.smtp;
1706 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1707
1708 /* URL decode the custom request */
1709 if(custom)
1710 result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
1711
1712 return result;
1713 }
1714
1715 /***********************************************************************
1716 *
1717 * smtp_parse_address()
1718 *
1719 * Parse the fully qualified mailbox address into a local address part and the
1720 * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
1721 * necessary.
1722 *
1723 * Parameters:
1724 *
1725 * conn [in] - The connection handle.
1726 * fqma [in] - The fully qualified mailbox address (which may or
1727 * may not contain UTF-8 characters).
1728 * address [in/out] - A new allocated buffer which holds the local
1729 * address part of the mailbox. This buffer must be
1730 * free'ed by the caller.
1731 * host [in/out] - The hostname structure that holds the original,
1732 * and optionally encoded, hostname.
1733 * Curl_free_idnconverted_hostname() must be called
1734 * once the caller has finished with the structure.
1735 *
1736 * Returns CURLE_OK on success.
1737 *
1738 * Notes:
1739 *
1740 * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
1741 * that conversion then we shall return success. This allow the caller to send
1742 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1743 *
1744 * If an mailbox '@' separator cannot be located then the mailbox is considered
1745 * to be either a local mailbox or an invalid mailbox (depending on what the
1746 * calling function deems it to be) then the input will simply be returned in
1747 * the address part with the hostname being NULL.
1748 */
smtp_parse_address(const char * fqma,char ** address,struct hostname * host)1749 static CURLcode smtp_parse_address(const char *fqma, char **address,
1750 struct hostname *host)
1751 {
1752 CURLcode result = CURLE_OK;
1753 size_t length;
1754
1755 /* Duplicate the fully qualified email address so we can manipulate it,
1756 ensuring it does not contain the delimiters if specified */
1757 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1758 if(!dup)
1759 return CURLE_OUT_OF_MEMORY;
1760
1761 length = strlen(dup);
1762 if(length) {
1763 if(dup[length - 1] == '>')
1764 dup[length - 1] = '\0';
1765 }
1766
1767 /* Extract the hostname from the address (if we can) */
1768 host->name = strpbrk(dup, "@");
1769 if(host->name) {
1770 *host->name = '\0';
1771 host->name = host->name + 1;
1772
1773 /* Attempt to convert the hostname to IDN ACE */
1774 (void) Curl_idnconvert_hostname(host);
1775
1776 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1777 and send the hostname using UTF-8 rather than as 7-bit ACE (which is
1778 our preference) */
1779 }
1780
1781 /* Extract the local address from the mailbox */
1782 *address = dup;
1783
1784 return result;
1785 }
1786
1787 struct cr_eob_ctx {
1788 struct Curl_creader super;
1789 struct bufq buf;
1790 size_t n_eob; /* how many EOB bytes we matched so far */
1791 size_t eob; /* Number of bytes of the EOB (End Of Body) that
1792 have been received so far */
1793 BIT(read_eos); /* we read an EOS from the next reader */
1794 BIT(eos); /* we have returned an EOS */
1795 };
1796
cr_eob_init(struct Curl_easy * data,struct Curl_creader * reader)1797 static CURLcode cr_eob_init(struct Curl_easy *data,
1798 struct Curl_creader *reader)
1799 {
1800 struct cr_eob_ctx *ctx = reader->ctx;
1801 (void)data;
1802 /* The first char we read is the first on a line, as if we had
1803 * read CRLF just before */
1804 ctx->n_eob = 2;
1805 Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
1806 return CURLE_OK;
1807 }
1808
cr_eob_close(struct Curl_easy * data,struct Curl_creader * reader)1809 static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
1810 {
1811 struct cr_eob_ctx *ctx = reader->ctx;
1812 (void)data;
1813 Curl_bufq_free(&ctx->buf);
1814 }
1815
1816 /* this is the 5-bytes End-Of-Body marker for SMTP */
1817 #define SMTP_EOB "\r\n.\r\n"
1818 #define SMTP_EOB_FIND_LEN 3
1819
1820 /* client reader doing SMTP End-Of-Body escaping. */
cr_eob_read(struct Curl_easy * data,struct Curl_creader * reader,char * buf,size_t blen,size_t * pnread,bool * peos)1821 static CURLcode cr_eob_read(struct Curl_easy *data,
1822 struct Curl_creader *reader,
1823 char *buf, size_t blen,
1824 size_t *pnread, bool *peos)
1825 {
1826 struct cr_eob_ctx *ctx = reader->ctx;
1827 CURLcode result = CURLE_OK;
1828 size_t nread, i, start, n;
1829 bool eos;
1830
1831 if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
1832 /* Get more and convert it when needed */
1833 result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
1834 if(result)
1835 return result;
1836
1837 ctx->read_eos = eos;
1838 if(nread) {
1839 if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
1840 /* not in the middle of a match, no EOB start found, just pass */
1841 *pnread = nread;
1842 *peos = FALSE;
1843 return CURLE_OK;
1844 }
1845 /* scan for EOB (continuation) and convert */
1846 for(i = start = 0; i < nread; ++i) {
1847 if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
1848 /* matched the EOB prefix and seeing additional char, add '.' */
1849 result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
1850 if(result)
1851 return result;
1852 result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
1853 if(result)
1854 return result;
1855 ctx->n_eob = 0;
1856 start = i;
1857 if(data->state.infilesize > 0)
1858 data->state.infilesize++;
1859 }
1860
1861 if(buf[i] != SMTP_EOB[ctx->n_eob])
1862 ctx->n_eob = 0;
1863
1864 if(buf[i] == SMTP_EOB[ctx->n_eob]) {
1865 /* matching another char of the EOB */
1866 ++ctx->n_eob;
1867 }
1868 }
1869
1870 /* add any remainder to buf */
1871 if(start < nread) {
1872 result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
1873 if(result)
1874 return result;
1875 }
1876 }
1877
1878 if(ctx->read_eos) {
1879 /* if we last matched a CRLF or if the data was empty, add ".\r\n"
1880 * to end the body. If we sent something and it did not end with "\r\n",
1881 * add "\r\n.\r\n" to end the body */
1882 const char *eob = SMTP_EOB;
1883 switch(ctx->n_eob) {
1884 case 2:
1885 /* seen a CRLF at the end, just add the remainder */
1886 eob = &SMTP_EOB[2];
1887 break;
1888 case 3:
1889 /* ended with '\r\n.', we should escpe the last '.' */
1890 eob = "." SMTP_EOB;
1891 break;
1892 default:
1893 break;
1894 }
1895 result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
1896 if(result)
1897 return result;
1898 }
1899 }
1900
1901 *peos = FALSE;
1902 if(!Curl_bufq_is_empty(&ctx->buf)) {
1903 result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
1904 }
1905 else
1906 *pnread = 0;
1907
1908 if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
1909 /* no more data, read all, done. */
1910 ctx->eos = TRUE;
1911 }
1912 *peos = ctx->eos;
1913 DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
1914 blen, result, *pnread, *peos));
1915 return result;
1916 }
1917
cr_eob_total_length(struct Curl_easy * data,struct Curl_creader * reader)1918 static curl_off_t cr_eob_total_length(struct Curl_easy *data,
1919 struct Curl_creader *reader)
1920 {
1921 /* this reader changes length depending on input */
1922 (void)data;
1923 (void)reader;
1924 return -1;
1925 }
1926
1927 static const struct Curl_crtype cr_eob = {
1928 "cr-smtp-eob",
1929 cr_eob_init,
1930 cr_eob_read,
1931 cr_eob_close,
1932 Curl_creader_def_needs_rewind,
1933 cr_eob_total_length,
1934 Curl_creader_def_resume_from,
1935 Curl_creader_def_rewind,
1936 Curl_creader_def_unpause,
1937 Curl_creader_def_is_paused,
1938 Curl_creader_def_done,
1939 sizeof(struct cr_eob_ctx)
1940 };
1941
cr_eob_add(struct Curl_easy * data)1942 static CURLcode cr_eob_add(struct Curl_easy *data)
1943 {
1944 struct Curl_creader *reader = NULL;
1945 CURLcode result;
1946
1947 result = Curl_creader_create(&reader, data, &cr_eob,
1948 CURL_CR_CONTENT_ENCODE);
1949 if(!result)
1950 result = Curl_creader_add(data, reader);
1951
1952 if(result && reader)
1953 Curl_creader_free(data, reader);
1954 return result;
1955 }
1956
1957 #endif /* CURL_DISABLE_SMTP */
1958