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