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