1 /*
2  *  SSL client for SMTP servers
3  *
4  *  Copyright The Mbed TLS Contributors
5  *  SPDX-License-Identifier: Apache-2.0
6  *
7  *  Licensed under the Apache License, Version 2.0 (the "License"); you may
8  *  not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *  http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  */
19 
20 /* Enable definition of gethostname() even when compiling with -std=c99. Must
21  * be set before mbedtls_config.h, which pulls in glibc's features.h indirectly.
22  * Harmless on other platforms. */
23 
24 #define _POSIX_C_SOURCE 200112L
25 #define _XOPEN_SOURCE 600
26 
27 #include "mbedtls/build_info.h"
28 
29 #include "mbedtls/platform.h"
30 
31 #if !defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_ENTROPY_C) ||  \
32     !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_CLI_C) || \
33     !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_RSA_C) ||         \
34     !defined(MBEDTLS_CTR_DRBG_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \
35     !defined(MBEDTLS_FS_IO)
main(void)36 int main(void)
37 {
38     mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_ENTROPY_C and/or "
39                    "MBEDTLS_SSL_TLS_C and/or MBEDTLS_SSL_CLI_C and/or "
40                    "MBEDTLS_NET_C and/or MBEDTLS_RSA_C and/or "
41                    "MBEDTLS_CTR_DRBG_C and/or MBEDTLS_X509_CRT_PARSE_C "
42                    "not defined.\n");
43     mbedtls_exit(0);
44 }
45 #else
46 
47 #include "mbedtls/base64.h"
48 #include "mbedtls/error.h"
49 #include "mbedtls/net_sockets.h"
50 #include "mbedtls/ssl.h"
51 #include "mbedtls/entropy.h"
52 #include "mbedtls/ctr_drbg.h"
53 #include "test/certs.h"
54 #include "mbedtls/x509.h"
55 
56 #include <stdlib.h>
57 #include <string.h>
58 
59 #if !defined(_MSC_VER) || defined(EFIX64) || defined(EFI32)
60 #include <unistd.h>
61 #else
62 #include <io.h>
63 #endif
64 
65 #if defined(_WIN32) || defined(_WIN32_WCE)
66 #include <winsock2.h>
67 #include <windows.h>
68 
69 #if defined(_MSC_VER)
70 #if defined(_WIN32_WCE)
71 #pragma comment( lib, "ws2.lib" )
72 #else
73 #pragma comment( lib, "ws2_32.lib" )
74 #endif
75 #endif /* _MSC_VER */
76 #endif
77 
78 #define DFL_SERVER_NAME         "localhost"
79 #define DFL_SERVER_PORT         "465"
80 #define DFL_USER_NAME           "user"
81 #define DFL_USER_PWD            "password"
82 #define DFL_MAIL_FROM           ""
83 #define DFL_MAIL_TO             ""
84 #define DFL_DEBUG_LEVEL         0
85 #define DFL_CA_FILE             ""
86 #define DFL_CRT_FILE            ""
87 #define DFL_KEY_FILE            ""
88 #define DFL_FORCE_CIPHER        0
89 #define DFL_MODE                0
90 #define DFL_AUTHENTICATION      0
91 
92 #define MODE_SSL_TLS            0
93 #define MODE_STARTTLS           0
94 
95 #if defined(MBEDTLS_BASE64_C)
96 #define USAGE_AUTH \
97     "    authentication=%%d   default: 0 (disabled)\n"          \
98     "    user_name=%%s        default: \"" DFL_USER_NAME "\"\n" \
99                                                          "    user_pwd=%%s         default: \"" \
100     DFL_USER_PWD "\"\n"
101 #else
102 #define USAGE_AUTH \
103     "    authentication options disabled. (Require MBEDTLS_BASE64_C)\n"
104 #endif /* MBEDTLS_BASE64_C */
105 
106 #if defined(MBEDTLS_FS_IO)
107 #define USAGE_IO \
108     "    ca_file=%%s          default: \"\" (pre-loaded)\n" \
109     "    crt_file=%%s         default: \"\" (pre-loaded)\n" \
110     "    key_file=%%s         default: \"\" (pre-loaded)\n"
111 #else
112 #define USAGE_IO \
113     "    No file operations available (MBEDTLS_FS_IO not defined)\n"
114 #endif /* MBEDTLS_FS_IO */
115 
116 #define USAGE \
117     "\n usage: ssl_mail_client param=<>...\n"                 \
118     "\n acceptable parameters:\n"                             \
119     "    server_name=%%s      default: " DFL_SERVER_NAME "\n" \
120                                                          "    server_port=%%d      default: " \
121     DFL_SERVER_PORT "\n" \
122                     "    debug_level=%%d      default: 0 (disabled)\n"        \
123                     "    mode=%%d             default: 0 (SSL/TLS) (1 for STARTTLS)\n"  \
124     USAGE_AUTH                                                \
125     "    mail_from=%%s        default: \"\"\n"                \
126     "    mail_to=%%s          default: \"\"\n"                \
127     USAGE_IO                                                  \
128     "    force_ciphersuite=<name>    default: all enabled\n"  \
129     " acceptable ciphersuite names:\n"
130 
131 
132 /*
133  * global options
134  */
135 struct options {
136     const char *server_name;    /* hostname of the server (client only)     */
137     const char *server_port;    /* port on which the ssl service runs       */
138     int debug_level;            /* level of debugging                       */
139     int authentication;         /* if authentication is required            */
140     int mode;                   /* SSL/TLS (0) or STARTTLS (1)              */
141     const char *user_name;      /* username to use for authentication       */
142     const char *user_pwd;       /* password to use for authentication       */
143     const char *mail_from;      /* E-Mail address to use as sender          */
144     const char *mail_to;        /* E-Mail address to use as recipient       */
145     const char *ca_file;        /* the file with the CA certificate(s)      */
146     const char *crt_file;       /* the file with the client certificate     */
147     const char *key_file;       /* the file with the client key             */
148     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
149 } opt;
150 
my_debug(void * ctx,int level,const char * file,int line,const char * str)151 static void my_debug(void *ctx, int level,
152                      const char *file, int line,
153                      const char *str)
154 {
155     ((void) level);
156 
157     mbedtls_fprintf((FILE *) ctx, "%s:%04d: %s", file, line, str);
158     fflush((FILE *) ctx);
159 }
160 
do_handshake(mbedtls_ssl_context * ssl)161 static int do_handshake(mbedtls_ssl_context *ssl)
162 {
163     int ret;
164     uint32_t flags;
165     unsigned char buf[1024];
166     memset(buf, 0, 1024);
167 
168     /*
169      * 4. Handshake
170      */
171     mbedtls_printf("  . Performing the SSL/TLS handshake...");
172     fflush(stdout);
173 
174     while ((ret = mbedtls_ssl_handshake(ssl)) != 0) {
175         if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
176 #if defined(MBEDTLS_ERROR_C)
177             mbedtls_strerror(ret, (char *) buf, 1024);
178 #endif
179             mbedtls_printf(" failed\n  ! mbedtls_ssl_handshake returned %d: %s\n\n", ret, buf);
180             return -1;
181         }
182     }
183 
184     mbedtls_printf(" ok\n    [ Ciphersuite is %s ]\n",
185                    mbedtls_ssl_get_ciphersuite(ssl));
186 
187     /*
188      * 5. Verify the server certificate
189      */
190     mbedtls_printf("  . Verifying peer X.509 certificate...");
191 
192     /* In real life, we probably want to bail out when ret != 0 */
193     if ((flags = mbedtls_ssl_get_verify_result(ssl)) != 0) {
194 #if !defined(MBEDTLS_X509_REMOVE_INFO)
195         char vrfy_buf[512];
196 #endif
197 
198         mbedtls_printf(" failed\n");
199 
200 #if !defined(MBEDTLS_X509_REMOVE_INFO)
201         mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "  ! ", flags);
202 
203         mbedtls_printf("%s\n", vrfy_buf);
204 #endif
205     } else {
206         mbedtls_printf(" ok\n");
207     }
208 
209 #if !defined(MBEDTLS_X509_REMOVE_INFO)
210     mbedtls_printf("  . Peer certificate information    ...\n");
211     mbedtls_x509_crt_info((char *) buf, sizeof(buf) - 1, "      ",
212                           mbedtls_ssl_get_peer_cert(ssl));
213     mbedtls_printf("%s\n", buf);
214 #endif
215 
216     return 0;
217 }
218 
write_ssl_data(mbedtls_ssl_context * ssl,unsigned char * buf,size_t len)219 static int write_ssl_data(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len)
220 {
221     int ret;
222 
223     mbedtls_printf("\n%s", buf);
224     while (len && (ret = mbedtls_ssl_write(ssl, buf, len)) <= 0) {
225         if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
226             mbedtls_printf(" failed\n  ! mbedtls_ssl_write returned %d\n\n", ret);
227             return -1;
228         }
229     }
230 
231     return 0;
232 }
233 
write_ssl_and_get_response(mbedtls_ssl_context * ssl,unsigned char * buf,size_t len)234 static int write_ssl_and_get_response(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len)
235 {
236     int ret;
237     unsigned char data[128];
238     char code[4];
239     size_t i, idx = 0;
240 
241     mbedtls_printf("\n%s", buf);
242     while (len && (ret = mbedtls_ssl_write(ssl, buf, len)) <= 0) {
243         if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
244             mbedtls_printf(" failed\n  ! mbedtls_ssl_write returned %d\n\n", ret);
245             return -1;
246         }
247     }
248 
249     do {
250         len = sizeof(data) - 1;
251         memset(data, 0, sizeof(data));
252         ret = mbedtls_ssl_read(ssl, data, len);
253 
254         if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
255             continue;
256         }
257 
258         if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
259             return -1;
260         }
261 
262         if (ret <= 0) {
263             mbedtls_printf("failed\n  ! mbedtls_ssl_read returned %d\n\n", ret);
264             return -1;
265         }
266 
267         mbedtls_printf("\n%s", data);
268         len = ret;
269         for (i = 0; i < len; i++) {
270             if (data[i] != '\n') {
271                 if (idx < 4) {
272                     code[idx++] = data[i];
273                 }
274                 continue;
275             }
276 
277             if (idx == 4 && code[0] >= '0' && code[0] <= '9' && code[3] == ' ') {
278                 code[3] = '\0';
279                 return atoi(code);
280             }
281 
282             idx = 0;
283         }
284     } while (1);
285 }
286 
write_and_get_response(mbedtls_net_context * sock_fd,unsigned char * buf,size_t len)287 static int write_and_get_response(mbedtls_net_context *sock_fd, unsigned char *buf, size_t len)
288 {
289     int ret;
290     unsigned char data[128];
291     char code[4];
292     size_t i, idx = 0;
293 
294     mbedtls_printf("\n%s", buf);
295     if (len && (ret = mbedtls_net_send(sock_fd, buf, len)) <= 0) {
296         mbedtls_printf(" failed\n  ! mbedtls_net_send returned %d\n\n", ret);
297         return -1;
298     }
299 
300     do {
301         len = sizeof(data) - 1;
302         memset(data, 0, sizeof(data));
303         ret = mbedtls_net_recv(sock_fd, data, len);
304 
305         if (ret <= 0) {
306             mbedtls_printf("failed\n  ! mbedtls_net_recv returned %d\n\n", ret);
307             return -1;
308         }
309 
310         data[len] = '\0';
311         mbedtls_printf("\n%s", data);
312         len = ret;
313         for (i = 0; i < len; i++) {
314             if (data[i] != '\n') {
315                 if (idx < 4) {
316                     code[idx++] = data[i];
317                 }
318                 continue;
319             }
320 
321             if (idx == 4 && code[0] >= '0' && code[0] <= '9' && code[3] == ' ') {
322                 code[3] = '\0';
323                 return atoi(code);
324             }
325 
326             idx = 0;
327         }
328     } while (1);
329 }
330 
main(int argc,char * argv[])331 int main(int argc, char *argv[])
332 {
333     int ret = 1, len;
334     int exit_code = MBEDTLS_EXIT_FAILURE;
335     mbedtls_net_context server_fd;
336 #if defined(MBEDTLS_BASE64_C)
337     unsigned char base[1024];
338     /* buf is used as the destination buffer for printing base with the format:
339      * "%s\r\n". Hence, the size of buf should be at least the size of base
340      * plus 2 bytes for the \r and \n characters.
341      */
342     unsigned char buf[sizeof(base) + 2];
343 #else
344     unsigned char buf[1024];
345 #endif
346     char hostname[32];
347     const char *pers = "ssl_mail_client";
348 
349     mbedtls_entropy_context entropy;
350     mbedtls_ctr_drbg_context ctr_drbg;
351     mbedtls_ssl_context ssl;
352     mbedtls_ssl_config conf;
353     mbedtls_x509_crt cacert;
354     mbedtls_x509_crt clicert;
355     mbedtls_pk_context pkey;
356     int i;
357     size_t n;
358     char *p, *q;
359     const int *list;
360 
361     /*
362      * Make sure memory references are valid in case we exit early.
363      */
364     mbedtls_net_init(&server_fd);
365     mbedtls_ssl_init(&ssl);
366     mbedtls_ssl_config_init(&conf);
367     memset(&buf, 0, sizeof(buf));
368     mbedtls_x509_crt_init(&cacert);
369     mbedtls_x509_crt_init(&clicert);
370     mbedtls_pk_init(&pkey);
371     mbedtls_ctr_drbg_init(&ctr_drbg);
372 
373     if (argc < 2) {
374 usage:
375         mbedtls_printf(USAGE);
376 
377         list = mbedtls_ssl_list_ciphersuites();
378         while (*list) {
379             mbedtls_printf("    %s\n", mbedtls_ssl_get_ciphersuite_name(*list));
380             list++;
381         }
382         mbedtls_printf("\n");
383         goto exit;
384     }
385 
386     opt.server_name         = DFL_SERVER_NAME;
387     opt.server_port         = DFL_SERVER_PORT;
388     opt.debug_level         = DFL_DEBUG_LEVEL;
389     opt.authentication      = DFL_AUTHENTICATION;
390     opt.mode                = DFL_MODE;
391     opt.user_name           = DFL_USER_NAME;
392     opt.user_pwd            = DFL_USER_PWD;
393     opt.mail_from           = DFL_MAIL_FROM;
394     opt.mail_to             = DFL_MAIL_TO;
395     opt.ca_file             = DFL_CA_FILE;
396     opt.crt_file            = DFL_CRT_FILE;
397     opt.key_file            = DFL_KEY_FILE;
398     opt.force_ciphersuite[0] = DFL_FORCE_CIPHER;
399 
400     for (i = 1; i < argc; i++) {
401         p = argv[i];
402         if ((q = strchr(p, '=')) == NULL) {
403             goto usage;
404         }
405         *q++ = '\0';
406 
407         if (strcmp(p, "server_name") == 0) {
408             opt.server_name = q;
409         } else if (strcmp(p, "server_port") == 0) {
410             opt.server_port = q;
411         } else if (strcmp(p, "debug_level") == 0) {
412             opt.debug_level = atoi(q);
413             if (opt.debug_level < 0 || opt.debug_level > 65535) {
414                 goto usage;
415             }
416         } else if (strcmp(p, "authentication") == 0) {
417             opt.authentication = atoi(q);
418             if (opt.authentication < 0 || opt.authentication > 1) {
419                 goto usage;
420             }
421         } else if (strcmp(p, "mode") == 0) {
422             opt.mode = atoi(q);
423             if (opt.mode < 0 || opt.mode > 1) {
424                 goto usage;
425             }
426         } else if (strcmp(p, "user_name") == 0) {
427             opt.user_name = q;
428         } else if (strcmp(p, "user_pwd") == 0) {
429             opt.user_pwd = q;
430         } else if (strcmp(p, "mail_from") == 0) {
431             opt.mail_from = q;
432         } else if (strcmp(p, "mail_to") == 0) {
433             opt.mail_to = q;
434         } else if (strcmp(p, "ca_file") == 0) {
435             opt.ca_file = q;
436         } else if (strcmp(p, "crt_file") == 0) {
437             opt.crt_file = q;
438         } else if (strcmp(p, "key_file") == 0) {
439             opt.key_file = q;
440         } else if (strcmp(p, "force_ciphersuite") == 0) {
441             opt.force_ciphersuite[0] = -1;
442 
443             opt.force_ciphersuite[0] = mbedtls_ssl_get_ciphersuite_id(q);
444 
445             if (opt.force_ciphersuite[0] <= 0) {
446                 goto usage;
447             }
448 
449             opt.force_ciphersuite[1] = 0;
450         } else {
451             goto usage;
452         }
453     }
454 
455     /*
456      * 0. Initialize the RNG and the session data
457      */
458     mbedtls_printf("\n  . Seeding the random number generator...");
459     fflush(stdout);
460 
461     mbedtls_entropy_init(&entropy);
462     if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
463                                      (const unsigned char *) pers,
464                                      strlen(pers))) != 0) {
465         mbedtls_printf(" failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret);
466         goto exit;
467     }
468 
469     mbedtls_printf(" ok\n");
470 
471     /*
472      * 1.1. Load the trusted CA
473      */
474     mbedtls_printf("  . Loading the CA root certificate ...");
475     fflush(stdout);
476 
477 #if defined(MBEDTLS_FS_IO)
478     if (strlen(opt.ca_file)) {
479         ret = mbedtls_x509_crt_parse_file(&cacert, opt.ca_file);
480     } else
481 #endif
482 #if defined(MBEDTLS_PEM_PARSE_C)
483     ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) mbedtls_test_cas_pem,
484                                  mbedtls_test_cas_pem_len);
485 #else
486     {
487         mbedtls_printf("MBEDTLS_PEM_PARSE_C not defined.");
488         goto exit;
489     }
490 #endif
491     if (ret < 0) {
492         mbedtls_printf(" failed\n  !  mbedtls_x509_crt_parse returned %d\n\n", ret);
493         goto exit;
494     }
495 
496     mbedtls_printf(" ok (%d skipped)\n", ret);
497 
498     /*
499      * 1.2. Load own certificate and private key
500      *
501      * (can be skipped if client authentication is not required)
502      */
503     mbedtls_printf("  . Loading the client cert. and key...");
504     fflush(stdout);
505 
506 #if defined(MBEDTLS_FS_IO)
507     if (strlen(opt.crt_file)) {
508         ret = mbedtls_x509_crt_parse_file(&clicert, opt.crt_file);
509     } else
510 #endif
511     ret = mbedtls_x509_crt_parse(&clicert, (const unsigned char *) mbedtls_test_cli_crt,
512                                  mbedtls_test_cli_crt_len);
513     if (ret != 0) {
514         mbedtls_printf(" failed\n  !  mbedtls_x509_crt_parse returned %d\n\n", ret);
515         goto exit;
516     }
517 
518 #if defined(MBEDTLS_FS_IO)
519     if (strlen(opt.key_file)) {
520         ret = mbedtls_pk_parse_keyfile(&pkey, opt.key_file, "",
521                                        mbedtls_ctr_drbg_random, &ctr_drbg);
522     } else
523 #endif
524 #if defined(MBEDTLS_PEM_PARSE_C)
525     {
526         ret = mbedtls_pk_parse_key(&pkey,
527                                    (const unsigned char *) mbedtls_test_cli_key,
528                                    mbedtls_test_cli_key_len,
529                                    NULL,
530                                    0,
531                                    mbedtls_ctr_drbg_random,
532                                    &ctr_drbg);
533     }
534 #else
535     {
536         mbedtls_printf("MBEDTLS_PEM_PARSE_C not defined.");
537         goto exit;
538     }
539 #endif
540     if (ret != 0) {
541         mbedtls_printf(" failed\n  !  mbedtls_pk_parse_key returned %d\n\n", ret);
542         goto exit;
543     }
544 
545     mbedtls_printf(" ok\n");
546 
547     /*
548      * 2. Start the connection
549      */
550     mbedtls_printf("  . Connecting to tcp/%s/%s...", opt.server_name,
551                    opt.server_port);
552     fflush(stdout);
553 
554     if ((ret = mbedtls_net_connect(&server_fd, opt.server_name,
555                                    opt.server_port, MBEDTLS_NET_PROTO_TCP)) != 0) {
556         mbedtls_printf(" failed\n  ! mbedtls_net_connect returned %d\n\n", ret);
557         goto exit;
558     }
559 
560     mbedtls_printf(" ok\n");
561 
562     /*
563      * 3. Setup stuff
564      */
565     mbedtls_printf("  . Setting up the SSL/TLS structure...");
566     fflush(stdout);
567 
568     if ((ret = mbedtls_ssl_config_defaults(&conf,
569                                            MBEDTLS_SSL_IS_CLIENT,
570                                            MBEDTLS_SSL_TRANSPORT_STREAM,
571                                            MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
572         mbedtls_printf(" failed\n  ! mbedtls_ssl_config_defaults returned %d\n\n", ret);
573         goto exit;
574     }
575 
576     /* OPTIONAL is not optimal for security,
577      * but makes interop easier in this simplified example */
578     mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
579 
580     mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
581     mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
582 
583     if (opt.force_ciphersuite[0] != DFL_FORCE_CIPHER) {
584         mbedtls_ssl_conf_ciphersuites(&conf, opt.force_ciphersuite);
585     }
586 
587     mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
588     if ((ret = mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey)) != 0) {
589         mbedtls_printf(" failed\n  ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret);
590         goto exit;
591     }
592 
593     if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
594         mbedtls_printf(" failed\n  ! mbedtls_ssl_setup returned %d\n\n", ret);
595         goto exit;
596     }
597 
598     if ((ret = mbedtls_ssl_set_hostname(&ssl, opt.server_name)) != 0) {
599         mbedtls_printf(" failed\n  ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
600         goto exit;
601     }
602 
603     mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
604 
605     mbedtls_printf(" ok\n");
606 
607     if (opt.mode == MODE_SSL_TLS) {
608         if (do_handshake(&ssl) != 0) {
609             goto exit;
610         }
611 
612         mbedtls_printf("  > Get header from server:");
613         fflush(stdout);
614 
615         ret = write_ssl_and_get_response(&ssl, buf, 0);
616         if (ret < 200 || ret > 299) {
617             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
618             goto exit;
619         }
620 
621         mbedtls_printf(" ok\n");
622 
623         mbedtls_printf("  > Write EHLO to server:");
624         fflush(stdout);
625 
626         gethostname(hostname, 32);
627         len = sprintf((char *) buf, "EHLO %s\r\n", hostname);
628         ret = write_ssl_and_get_response(&ssl, buf, len);
629         if (ret < 200 || ret > 299) {
630             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
631             goto exit;
632         }
633     } else {
634         mbedtls_printf("  > Get header from server:");
635         fflush(stdout);
636 
637         ret = write_and_get_response(&server_fd, buf, 0);
638         if (ret < 200 || ret > 299) {
639             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
640             goto exit;
641         }
642 
643         mbedtls_printf(" ok\n");
644 
645         mbedtls_printf("  > Write EHLO to server:");
646         fflush(stdout);
647 
648         gethostname(hostname, 32);
649         len = sprintf((char *) buf, "EHLO %s\r\n", hostname);
650         ret = write_and_get_response(&server_fd, buf, len);
651         if (ret < 200 || ret > 299) {
652             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
653             goto exit;
654         }
655 
656         mbedtls_printf(" ok\n");
657 
658         mbedtls_printf("  > Write STARTTLS to server:");
659         fflush(stdout);
660 
661         gethostname(hostname, 32);
662         len = sprintf((char *) buf, "STARTTLS\r\n");
663         ret = write_and_get_response(&server_fd, buf, len);
664         if (ret < 200 || ret > 299) {
665             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
666             goto exit;
667         }
668 
669         mbedtls_printf(" ok\n");
670 
671         if (do_handshake(&ssl) != 0) {
672             goto exit;
673         }
674     }
675 
676 #if defined(MBEDTLS_BASE64_C)
677     if (opt.authentication) {
678         mbedtls_printf("  > Write AUTH LOGIN to server:");
679         fflush(stdout);
680 
681         len = sprintf((char *) buf, "AUTH LOGIN\r\n");
682         ret = write_ssl_and_get_response(&ssl, buf, len);
683         if (ret < 200 || ret > 399) {
684             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
685             goto exit;
686         }
687 
688         mbedtls_printf(" ok\n");
689 
690         mbedtls_printf("  > Write username to server: %s", opt.user_name);
691         fflush(stdout);
692 
693         ret = mbedtls_base64_encode(base, sizeof(base), &n, (const unsigned char *) opt.user_name,
694                                     strlen(opt.user_name));
695 
696         if (ret != 0) {
697             mbedtls_printf(" failed\n  ! mbedtls_base64_encode returned %d\n\n", ret);
698             goto exit;
699         }
700         len = sprintf((char *) buf, "%s\r\n", base);
701         ret = write_ssl_and_get_response(&ssl, buf, len);
702         if (ret < 300 || ret > 399) {
703             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
704             goto exit;
705         }
706 
707         mbedtls_printf(" ok\n");
708 
709         mbedtls_printf("  > Write password to server: %s", opt.user_pwd);
710         fflush(stdout);
711 
712         ret = mbedtls_base64_encode(base, sizeof(base), &n, (const unsigned char *) opt.user_pwd,
713                                     strlen(opt.user_pwd));
714 
715         if (ret != 0) {
716             mbedtls_printf(" failed\n  ! mbedtls_base64_encode returned %d\n\n", ret);
717             goto exit;
718         }
719         len = sprintf((char *) buf, "%s\r\n", base);
720         ret = write_ssl_and_get_response(&ssl, buf, len);
721         if (ret < 200 || ret > 399) {
722             mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
723             goto exit;
724         }
725 
726         mbedtls_printf(" ok\n");
727     }
728 #endif
729 
730     mbedtls_printf("  > Write MAIL FROM to server:");
731     fflush(stdout);
732 
733     len = sprintf((char *) buf, "MAIL FROM:<%s>\r\n", opt.mail_from);
734     ret = write_ssl_and_get_response(&ssl, buf, len);
735     if (ret < 200 || ret > 299) {
736         mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
737         goto exit;
738     }
739 
740     mbedtls_printf(" ok\n");
741 
742     mbedtls_printf("  > Write RCPT TO to server:");
743     fflush(stdout);
744 
745     len = sprintf((char *) buf, "RCPT TO:<%s>\r\n", opt.mail_to);
746     ret = write_ssl_and_get_response(&ssl, buf, len);
747     if (ret < 200 || ret > 299) {
748         mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
749         goto exit;
750     }
751 
752     mbedtls_printf(" ok\n");
753 
754     mbedtls_printf("  > Write DATA to server:");
755     fflush(stdout);
756 
757     len = sprintf((char *) buf, "DATA\r\n");
758     ret = write_ssl_and_get_response(&ssl, buf, len);
759     if (ret < 300 || ret > 399) {
760         mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
761         goto exit;
762     }
763 
764     mbedtls_printf(" ok\n");
765 
766     mbedtls_printf("  > Write content to server:");
767     fflush(stdout);
768 
769     len = sprintf((char *) buf, "From: %s\r\nSubject: mbed TLS Test mail\r\n\r\n"
770                                 "This is a simple test mail from the "
771                                 "mbed TLS mail client example.\r\n"
772                                 "\r\n"
773                                 "Enjoy!", opt.mail_from);
774     ret = write_ssl_data(&ssl, buf, len);
775 
776     len = sprintf((char *) buf, "\r\n.\r\n");
777     ret = write_ssl_and_get_response(&ssl, buf, len);
778     if (ret < 200 || ret > 299) {
779         mbedtls_printf(" failed\n  ! server responded with %d\n\n", ret);
780         goto exit;
781     }
782 
783     mbedtls_printf(" ok\n");
784 
785     mbedtls_ssl_close_notify(&ssl);
786 
787     exit_code = MBEDTLS_EXIT_SUCCESS;
788 
789 exit:
790 
791     mbedtls_net_free(&server_fd);
792     mbedtls_x509_crt_free(&clicert);
793     mbedtls_x509_crt_free(&cacert);
794     mbedtls_pk_free(&pkey);
795     mbedtls_ssl_free(&ssl);
796     mbedtls_ssl_config_free(&conf);
797     mbedtls_ctr_drbg_free(&ctr_drbg);
798     mbedtls_entropy_free(&entropy);
799 
800     mbedtls_exit(exit_code);
801 }
802 #endif /* MBEDTLS_BIGNUM_C && MBEDTLS_ENTROPY_C && MBEDTLS_SSL_TLS_C &&
803           MBEDTLS_SSL_CLI_C && MBEDTLS_NET_C && MBEDTLS_RSA_C **
804           MBEDTLS_CTR_DRBG_C */
805