1 /**
2  * @file
3  * SMTP client module
4  *
5  * Author: Simon Goldschmidt
6  *
7  * @defgroup smtp SMTP client
8  * @ingroup apps
9  *
10  * This is simple SMTP client for raw API.
11  * It is a minimal implementation of SMTP as specified in RFC 5321.
12  *
13  * Example usage:
14 @code{.c}
15  void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
16  {
17    printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
18           smtp_result, srv_err, err);
19  }
20  static void my_smtp_test(void)
21  {
22    smtp_set_server_addr("mymailserver.org");
23    -> set both username and password as NULL if no auth needed
24    smtp_set_auth("username", "password");
25    smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
26                   some_argument);
27  }
28 @endcode
29 
30  * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
31  * smtp_send_mail_int()!
32  *
33  * SMTP_BODYDH usage:
34 @code{.c}
35  int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
36  {
37     if(bdh->state >= 10) {
38        return BDH_DONE;
39     }
40     sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
41     bdh->length = strlen(bdh->buffer);
42     ++bdh->state;
43     return BDH_WORKING;
44  }
45 
46  smtp_send_mail_bodycback("sender", "recipient", "subject",
47                 my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
48 @endcode
49  *
50  * @todo:
51  * - attachments (the main difficulty here is streaming base64-encoding to
52  *   prevent having to allocate a buffer for the whole encoded file at once)
53  * - test with more mail servers...
54  *
55  */
56 
57 #include "lwip/apps/smtp.h"
58 
59 #if LWIP_TCP && LWIP_CALLBACK_API
60 #include "lwip/sys.h"
61 #include "lwip/sockets.h"
62 #include "lwip/altcp.h"
63 #include "lwip/dns.h"
64 #include "lwip/mem.h"
65 #include "lwip/altcp_tcp.h"
66 #include "lwip/altcp_tls.h"
67 
68 #include <string.h> /* strlen, memcpy */
69 #include <stdlib.h>
70 
71 /** TCP poll interval. Unit is 0.5 sec. */
72 #define SMTP_POLL_INTERVAL      4
73 /** TCP poll timeout while sending message body, reset after every
74  * successful write. 3 minutes */
75 #define SMTP_TIMEOUT_DATABLOCK  ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
76 /** TCP poll timeout while waiting for confirmation after sending the body.
77  * 10 minutes */
78 #define SMTP_TIMEOUT_DATATERM   (10 * 60 * SMTP_POLL_INTERVAL / 2)
79 /** TCP poll timeout while not sending the body.
80  * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
81  * and RCPT) but still OK for us here.
82  * 2 minutes */
83 #define SMTP_TIMEOUT            ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
84 
85 /* the various debug levels for this file */
86 #define SMTP_DEBUG_TRACE        (SMTP_DEBUG | LWIP_DBG_TRACE)
87 #define SMTP_DEBUG_STATE        (SMTP_DEBUG | LWIP_DBG_STATE)
88 #define SMTP_DEBUG_WARN         (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
89 #define SMTP_DEBUG_WARN_STATE   (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
90 #define SMTP_DEBUG_SERIOUS      (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
91 
92 
93 #define SMTP_RX_BUF_LEN         255
94 #define SMTP_TX_BUF_LEN         255
95 #define SMTP_CRLF               "\r\n"
96 #define SMTP_CRLF_LEN           2
97 
98 #define SMTP_RESP_220           "220"
99 #define SMTP_RESP_235           "235"
100 #define SMTP_RESP_250           "250"
101 #define SMTP_RESP_334           "334"
102 #define SMTP_RESP_354           "354"
103 #define SMTP_RESP_LOGIN_UNAME   "VXNlcm5hbWU6"
104 #define SMTP_RESP_LOGIN_PASS    "UGFzc3dvcmQ6"
105 
106 #define SMTP_KEYWORD_AUTH_SP    "AUTH "
107 #define SMTP_KEYWORD_AUTH_EQ    "AUTH="
108 #define SMTP_KEYWORD_AUTH_LEN   5
109 #define SMTP_AUTH_PARAM_PLAIN   "PLAIN"
110 #define SMTP_AUTH_PARAM_LOGIN   "LOGIN"
111 
112 #define SMTP_CMD_EHLO_1           "EHLO ["
113 #define SMTP_CMD_EHLO_1_LEN       6
114 #define SMTP_CMD_EHLO_2           "]\r\n"
115 #define SMTP_CMD_EHLO_2_LEN       3
116 #define SMTP_CMD_AUTHPLAIN_1      "AUTH PLAIN "
117 #define SMTP_CMD_AUTHPLAIN_1_LEN  11
118 #define SMTP_CMD_AUTHPLAIN_2      "\r\n"
119 #define SMTP_CMD_AUTHPLAIN_2_LEN  2
120 #define SMTP_CMD_AUTHLOGIN        "AUTH LOGIN\r\n"
121 #define SMTP_CMD_AUTHLOGIN_LEN    12
122 #define SMTP_CMD_MAIL_1           "MAIL FROM: <"
123 #define SMTP_CMD_MAIL_1_LEN       12
124 #define SMTP_CMD_MAIL_2           ">\r\n"
125 #define SMTP_CMD_MAIL_2_LEN       3
126 #define SMTP_CMD_RCPT_1           "RCPT TO: <"
127 #define SMTP_CMD_RCPT_1_LEN       10
128 #define SMTP_CMD_RCPT_2           ">\r\n"
129 #define SMTP_CMD_RCPT_2_LEN       3
130 #define SMTP_CMD_DATA             "DATA\r\n"
131 #define SMTP_CMD_DATA_LEN         6
132 #define SMTP_CMD_HEADER_1         "From: <"
133 #define SMTP_CMD_HEADER_1_LEN     7
134 #define SMTP_CMD_HEADER_2         ">\r\nTo: <"
135 #define SMTP_CMD_HEADER_2_LEN     8
136 #define SMTP_CMD_HEADER_3         ">\r\nSubject: "
137 #define SMTP_CMD_HEADER_3_LEN     12
138 #define SMTP_CMD_HEADER_4         "\r\n\r\n"
139 #define SMTP_CMD_HEADER_4_LEN     4
140 #define SMTP_CMD_BODY_FINISHED    "\r\n.\r\n"
141 #define SMTP_CMD_BODY_FINISHED_LEN 5
142 #define SMTP_CMD_QUIT             "QUIT\r\n"
143 #define SMTP_CMD_QUIT_LEN         6
144 
145 #if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
146 #define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
147 #else /* SMTP_STAT_TX_BUF_MAX */
148 #define SMTP_TX_BUF_MAX(len)
149 #endif /* SMTP_STAT_TX_BUF_MAX */
150 
151 #if SMTP_COPY_AUTHDATA
152 #define SMTP_USERNAME(session)        (session)->username
153 #define SMTP_PASS(session)            (session)->pass
154 #define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
155 #define SMTP_AUTH_PLAIN_LEN(session)  (session)->auth_plain_len
156 #else /* SMTP_COPY_AUTHDATA */
157 #define SMTP_USERNAME(session)        smtp_username
158 #define SMTP_PASS(session)            smtp_pass
159 #define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
160 #define SMTP_AUTH_PLAIN_LEN(session)  smtp_auth_plain_len
161 #endif /* SMTP_COPY_AUTHDATA */
162 
163 #if SMTP_BODYDH
164 #ifndef SMTP_BODYDH_MALLOC
165 #define SMTP_BODYDH_MALLOC(size)      mem_malloc(size)
166 #define SMTP_BODYDH_FREE(ptr)         mem_free(ptr)
167 #endif
168 
169 /* Some internal state return values */
170 #define BDHALLDATASENT                2
171 #define BDHSOMEDATASENT               1
172 
173 enum bdh_handler_state {
174   BDH_SENDING,         /* Serving the user function generating body content */
175   BDH_STOP             /* User function stopped, closing */
176 };
177 #endif
178 
179 /** State for SMTP client state machine */
180 enum smtp_session_state {
181   SMTP_NULL,
182   SMTP_HELO,
183   SMTP_AUTH_PLAIN,
184   SMTP_AUTH_LOGIN_UNAME,
185   SMTP_AUTH_LOGIN_PASS,
186   SMTP_AUTH_LOGIN,
187   SMTP_MAIL,
188   SMTP_RCPT,
189   SMTP_DATA,
190   SMTP_BODY,
191   SMTP_QUIT,
192   SMTP_CLOSED
193 };
194 
195 #ifdef LWIP_DEBUG
196 /** State-to-string table for debugging */
197 static const char *smtp_state_str[] = {
198   "SMTP_NULL",
199   "SMTP_HELO",
200   "SMTP_AUTH_PLAIN",
201   "SMTP_AUTH_LOGIN_UNAME",
202   "SMTP_AUTH_LOGIN_PASS",
203   "SMTP_AUTH_LOGIN",
204   "SMTP_MAIL",
205   "SMTP_RCPT",
206   "SMTP_DATA",
207   "SMTP_BODY",
208   "SMTP_QUIT",
209   "SMTP_CLOSED",
210 };
211 
212 static const char *smtp_result_strs[] = {
213   "SMTP_RESULT_OK",
214   "SMTP_RESULT_ERR_UNKNOWN",
215   "SMTP_RESULT_ERR_CONNECT",
216   "SMTP_RESULT_ERR_HOSTNAME",
217   "SMTP_RESULT_ERR_CLOSED",
218   "SMTP_RESULT_ERR_TIMEOUT",
219   "SMTP_RESULT_ERR_SVR_RESP",
220   "SMTP_RESULT_ERR_MEM"
221 };
222 #endif /* LWIP_DEBUG */
223 
224 #if SMTP_BODYDH
225 struct smtp_bodydh_state {
226   smtp_bodycback_fn callback_fn;  /* The function to call (again) */
227   u16_t state;
228   struct smtp_bodydh exposed;     /* the user function structure */
229 };
230 #endif /* SMTP_BODYDH */
231 
232 /** struct keeping the body and state of an smtp session */
233 struct smtp_session {
234   /** keeping the state of the smtp session */
235   enum smtp_session_state state;
236   /** timeout handling, if this reaches 0, the connection is closed */
237   u16_t timer;
238   /** helper buffer for transmit, not used for sending body */
239   char tx_buf[SMTP_TX_BUF_LEN + 1];
240   struct pbuf* p;
241   /** source email address */
242   const char* from;
243   /** size of the sourceemail address */
244   u16_t from_len;
245   /** target email address */
246   const char* to;
247   /** size of the target email address */
248   u16_t to_len;
249   /** subject of the email */
250   const char *subject;
251   /** length of the subject string */
252   u16_t subject_len;
253   /** this is the body of the mail to be sent */
254   const char* body;
255   /** this is the length of the body to be sent */
256   u16_t body_len;
257   /** amount of data from body already sent */
258   u16_t body_sent;
259   /** callback function to call when closed */
260   smtp_result_fn callback_fn;
261   /** argument for callback function */
262   void *callback_arg;
263 #if SMTP_COPY_AUTHDATA
264   /** Username to use for this request */
265   char *username;
266   /** Password to use for this request */
267   char *pass;
268   /** Username and password combined as necessary for PLAIN authentication */
269   char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
270   /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
271   size_t auth_plain_len;
272 #endif /* SMTP_COPY_AUTHDATA */
273 #if SMTP_BODYDH
274   struct smtp_bodydh_state *bodydh;
275 #endif /* SMTP_BODYDH */
276 };
277 
278 /** IP address or DNS name of the server to use for next SMTP request */
279 static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
280 /** TCP port of the server to use for next SMTP request */
281 static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
282 #if LWIP_ALTCP && LWIP_ALTCP_TLS
283 /** If this is set, mail is sent using SMTPS */
284 static struct altcp_tls_config *smtp_server_tls_config;
285 #endif
286 /** Username to use for the next SMTP request */
287 static char *smtp_username;
288 /** Password to use for the next SMTP request */
289 static char *smtp_pass;
290 /** Username and password combined as necessary for PLAIN authentication */
291 static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
292 /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
293 static size_t smtp_auth_plain_len;
294 
295 #if SMTP_CHECK_DATA
296 static err_t  smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
297 #endif /* SMTP_CHECK_DATA */
298 static err_t  smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
299 static void   smtp_tcp_err(void *arg, err_t err);
300 static err_t  smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
301 static err_t  smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
302 static err_t  smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
303 #if LWIP_DNS
304 static void   smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
305 #endif /* LWIP_DNS */
306 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
307 static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
308 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
309 static enum   smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
310 static void   smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
311 static void   smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
312 #if SMTP_BODYDH
313 static void   smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
314 #endif /* SMTP_BODYDH */
315 
316 
317 #ifdef LWIP_DEBUG
318 /** Convert an smtp result to a string */
319 const char*
smtp_result_str(u8_t smtp_result)320 smtp_result_str(u8_t smtp_result)
321 {
322   if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
323     return "UNKNOWN";
324   }
325   return smtp_result_strs[smtp_result];
326 }
327 
328 /** Null-terminates the payload of p for printing out messages.
329  * WARNING: use this only if p is not needed any more as the last byte of
330  *          payload is deleted!
331  */
332 static const char*
smtp_pbuf_str(struct pbuf * p)333 smtp_pbuf_str(struct pbuf* p)
334 {
335   if ((p == NULL) || (p->len == 0)) {
336     return "";
337   }
338   ((char*)p->payload)[p->len] = 0;
339   return (const char*)p->payload;
340 }
341 #endif /* LWIP_DEBUG */
342 
343 /** @ingroup smtp
344  * Set IP address or DNS name for next SMTP connection
345  *
346  * @param server IP address (in ASCII representation) or DNS name of the server
347  */
348 err_t
smtp_set_server_addr(const char * server)349 smtp_set_server_addr(const char* server)
350 {
351   size_t len = 0;
352 
353   LWIP_ASSERT_CORE_LOCKED();
354 
355   if (server != NULL) {
356     /* strlen: returns length WITHOUT terminating 0 byte */
357     len = strlen(server);
358   }
359   if (len > SMTP_MAX_SERVERNAME_LEN) {
360     return ERR_MEM;
361   }
362   if (len != 0) {
363     MEMCPY(smtp_server, server, len);
364   }
365   smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
366   return ERR_OK;
367 }
368 
369 /** @ingroup smtp
370  * Set TCP port for next SMTP connection
371  *
372  * @param port TCP port
373  */
374 void
smtp_set_server_port(u16_t port)375 smtp_set_server_port(u16_t port)
376 {
377   LWIP_ASSERT_CORE_LOCKED();
378   smtp_server_port = port;
379 }
380 
381 #if LWIP_ALTCP && LWIP_ALTCP_TLS
382 /** @ingroup smtp
383  * Set TLS configuration for next SMTP connection
384  *
385  * @param tls_config TLS configuration
386  */
387 void
smtp_set_tls_config(struct altcp_tls_config * tls_config)388 smtp_set_tls_config(struct altcp_tls_config *tls_config)
389 {
390   LWIP_ASSERT_CORE_LOCKED();
391   smtp_server_tls_config = tls_config;
392 }
393 #endif
394 
395 /** @ingroup smtp
396  * Set authentication parameters for next SMTP connection
397  *
398  * @param username login name as passed to the server
399  * @param pass password passed to the server together with username
400  */
401 err_t
smtp_set_auth(const char * username,const char * pass)402 smtp_set_auth(const char* username, const char* pass)
403 {
404   size_t uname_len = 0;
405   size_t pass_len = 0;
406 
407   LWIP_ASSERT_CORE_LOCKED();
408 
409   memset(smtp_auth_plain, 0xfa, 64);
410   if (username != NULL) {
411     uname_len = strlen(username);
412     if (uname_len > SMTP_MAX_USERNAME_LEN) {
413       LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
414         (int)uname_len, SMTP_MAX_USERNAME_LEN));
415       return ERR_ARG;
416     }
417   }
418   if (pass != NULL) {
419 #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
420     pass_len = strlen(pass);
421     if (pass_len > SMTP_MAX_PASS_LEN) {
422       LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
423         (int)uname_len, SMTP_MAX_USERNAME_LEN));
424       return ERR_ARG;
425     }
426 #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
427     LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
428 #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
429   }
430   *smtp_auth_plain = 0;
431   if (username != NULL) {
432     smtp_username = smtp_auth_plain + 1;
433     strcpy(smtp_username, username);
434   }
435   if (pass != NULL) {
436     smtp_pass = smtp_auth_plain + uname_len + 2;
437     strcpy(smtp_pass, pass);
438   }
439   smtp_auth_plain_len = uname_len + pass_len + 2;
440 
441   return ERR_OK;
442 }
443 
444 #if SMTP_BODYDH
smtp_free_struct(struct smtp_session * s)445 static void smtp_free_struct(struct smtp_session *s)
446 {
447   if (s->bodydh != NULL) {
448     SMTP_BODYDH_FREE(s->bodydh);
449   }
450   SMTP_STATE_FREE(s);
451 }
452 #else /* SMTP_BODYDH */
453 #define smtp_free_struct(x) SMTP_STATE_FREE(x)
454 #endif /* SMTP_BODYDH */
455 
456 static struct altcp_pcb*
smtp_setup_pcb(struct smtp_session * s,const ip_addr_t * remote_ip)457 smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
458 {
459   struct altcp_pcb* pcb;
460   LWIP_UNUSED_ARG(remote_ip);
461 
462 #if LWIP_ALTCP && LWIP_ALTCP_TLS
463   if (smtp_server_tls_config) {
464     pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip));
465   } else
466 #endif
467   {
468     pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
469   }
470   if (pcb != NULL) {
471     altcp_arg(pcb, s);
472     altcp_recv(pcb, smtp_tcp_recv);
473     altcp_err(pcb, smtp_tcp_err);
474     altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
475     altcp_sent(pcb, smtp_tcp_sent);
476   }
477   return pcb;
478 }
479 
480 /** The actual mail-sending function, called by smtp_send_mail and
481  * smtp_send_mail_static after setting up the struct smtp_session.
482  */
483 static err_t
smtp_send_mail_alloced(struct smtp_session * s)484 smtp_send_mail_alloced(struct smtp_session *s)
485 {
486   err_t err;
487   struct altcp_pcb* pcb = NULL;
488   ip_addr_t addr;
489 
490   LWIP_ASSERT("no smtp_session supplied", s != NULL);
491 
492 #if SMTP_CHECK_DATA
493   /* check that body conforms to RFC:
494    * - convert all single-CR or -LF in body to CRLF
495    * - only 7-bit ASCII is allowed
496    */
497   if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
498     err = ERR_ARG;
499     goto leave;
500   }
501   if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
502     err = ERR_ARG;
503     goto leave;
504   }
505   if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
506     err = ERR_ARG;
507     goto leave;
508   }
509 #if SMTP_BODYDH
510   if (s->bodydh == NULL)
511 #endif /* SMTP_BODYDH */
512   {
513     if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
514       err = ERR_ARG;
515       goto leave;
516     }
517   }
518 #endif /* SMTP_CHECK_DATA */
519 
520 #if SMTP_COPY_AUTHDATA
521   /* copy auth data, ensuring the first byte is always zero */
522   MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
523   s->auth_plain_len = smtp_auth_plain_len;
524   /* default username and pass is empty string */
525   s->username = s->auth_plain;
526   s->pass = s->auth_plain;
527   if (smtp_username != NULL) {
528     s->username += smtp_username - smtp_auth_plain;
529   }
530   if (smtp_pass != NULL) {
531     s->pass += smtp_pass - smtp_auth_plain;
532   }
533 #endif /* SMTP_COPY_AUTHDATA */
534 
535   s->state = SMTP_NULL;
536   s->timer = SMTP_TIMEOUT;
537 
538 #if LWIP_DNS
539   err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
540 #else /* LWIP_DNS */
541   err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
542 #endif /* LWIP_DNS */
543   if (err == ERR_OK) {
544     pcb = smtp_setup_pcb(s, &addr);
545     if (pcb == NULL) {
546       err = ERR_MEM;
547       goto leave;
548     }
549     err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
550     if (err != ERR_OK) {
551       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
552       goto deallocate_and_leave;
553     }
554   } else if (err != ERR_INPROGRESS) {
555     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
556     goto deallocate_and_leave;
557   }
558   return ERR_OK;
559 
560 deallocate_and_leave:
561   if (pcb != NULL) {
562     altcp_arg(pcb, NULL);
563     altcp_close(pcb);
564   }
565 leave:
566   smtp_free_struct(s);
567   /* no need to call the callback here since we return != ERR_OK */
568   return err;
569 }
570 
571 /** @ingroup smtp
572  *  Send an email via the currently selected server, username and password.
573  *
574  * @param from source email address (must be NULL-terminated)
575  * @param to target email address (must be NULL-terminated)
576  * @param subject email subject (must be NULL-terminated)
577  * @param body email body (must be NULL-terminated)
578  * @param callback_fn callback function
579  * @param callback_arg user argument to callback_fn
580  * @returns - ERR_OK if structures were allocated and no error occurred starting the connection
581  *            (this does not mean the email has been successfully sent!)
582  *          - another err_t on error.
583  */
584 err_t
smtp_send_mail(const char * from,const char * to,const char * subject,const char * body,smtp_result_fn callback_fn,void * callback_arg)585 smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
586                smtp_result_fn callback_fn, void* callback_arg)
587 {
588   struct smtp_session* s;
589   size_t from_len = strlen(from);
590   size_t to_len = strlen(to);
591   size_t subject_len = strlen(subject);
592   size_t body_len = strlen(body);
593   size_t mem_len = sizeof(struct smtp_session);
594   char *sfrom, *sto, *ssubject, *sbody;
595 
596   LWIP_ASSERT_CORE_LOCKED();
597 
598   mem_len += from_len + to_len + subject_len + body_len + 4;
599   if (mem_len > 0xffff) {
600     /* too long! */
601     return ERR_MEM;
602   }
603 
604   /* Allocate memory to keep this email's session state */
605   s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
606   if (s == NULL) {
607     return ERR_MEM;
608   }
609   /* initialize the structure */
610   memset(s, 0, mem_len);
611   s->from = sfrom = (char*)s + sizeof(struct smtp_session);
612   s->from_len = (u16_t)from_len;
613   s->to = sto = sfrom + from_len + 1;
614   s->to_len = (u16_t)to_len;
615   s->subject = ssubject = sto + to_len + 1;
616   s->subject_len = (u16_t)subject_len;
617   s->body = sbody = ssubject + subject_len + 1;
618   s->body_len = (u16_t)body_len;
619   /* copy source and target email address */
620   /* cast to size_t is a hack to cast away constness */
621   MEMCPY(sfrom, from, from_len + 1);
622   MEMCPY(sto, to, to_len + 1);
623   MEMCPY(ssubject, subject, subject_len + 1);
624   MEMCPY(sbody, body, body_len + 1);
625 
626   s->callback_fn = callback_fn;
627   s->callback_arg = callback_arg;
628 
629   /* call the actual implementation of this function */
630   return smtp_send_mail_alloced(s);
631 }
632 
633 /** @ingroup smtp
634  * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
635  * an internal buffer to save memory.
636  * WARNING: the above data must stay untouched until the callback function is
637  *          called (unless the function returns != ERR_OK)
638  */
639 err_t
smtp_send_mail_static(const char * from,const char * to,const char * subject,const char * body,smtp_result_fn callback_fn,void * callback_arg)640 smtp_send_mail_static(const char *from, const char* to, const char* subject,
641   const char* body, smtp_result_fn callback_fn, void* callback_arg)
642 {
643   struct smtp_session* s;
644   size_t len;
645 
646   LWIP_ASSERT_CORE_LOCKED();
647 
648   s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
649   if (s == NULL) {
650     return ERR_MEM;
651   }
652   memset(s, 0, sizeof(struct smtp_session));
653   /* initialize the structure */
654   s->from = from;
655   len = strlen(from);
656   LWIP_ASSERT("string is too long", len <= 0xffff);
657   s->from_len = (u16_t)len;
658   s->to = to;
659   len = strlen(to);
660   LWIP_ASSERT("string is too long", len <= 0xffff);
661   s->to_len = (u16_t)len;
662   s->subject = subject;
663   len = strlen(subject);
664   LWIP_ASSERT("string is too long", len <= 0xffff);
665   s->subject_len = (u16_t)len;
666   s->body = body;
667   len = strlen(body);
668   LWIP_ASSERT("string is too long", len <= 0xffff);
669   s->body_len = (u16_t)len;
670   s->callback_fn = callback_fn;
671   s->callback_arg = callback_arg;
672   /* call the actual implementation of this function */
673   return smtp_send_mail_alloced(s);
674 }
675 
676 
677 /** @ingroup smtp
678  * Same as smtp_send_mail but takes a struct smtp_send_request as single
679  * parameter which contains all the other parameters.
680  * To be used with tcpip_callback to send mail from interrupt context or from
681  * another thread.
682  *
683  * WARNING: server and authentication must stay untouched until this function has run!
684  *
685  * Usage example:
686  * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
687  * - fill the members of the struct as if calling smtp_send_mail
688  * - specify a callback_function
689  * - set callback_arg to the structure itself
690  * - call this function
691  * - wait for the callback function to be called
692  * - in the callback function, deallocate the structure (passed as arg)
693  */
694 void
smtp_send_mail_int(void * arg)695 smtp_send_mail_int(void *arg)
696 {
697   struct smtp_send_request *req = (struct smtp_send_request*)arg;
698   err_t err;
699 
700   LWIP_ASSERT_CORE_LOCKED();
701   LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
702 
703   if (req->static_data) {
704     err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
705       req->callback_fn, req->callback_arg);
706   } else {
707     err = smtp_send_mail(req->from, req->to, req->subject, req->body,
708       req->callback_fn, req->callback_arg);
709   }
710   if ((err != ERR_OK) && (req->callback_fn != NULL)) {
711     req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
712   }
713 }
714 
715 #if SMTP_CHECK_DATA
716 /** Verify that a given string conforms to the SMTP rules
717  * (7-bit only, no single CR or LF,
718  *  @todo: no line consisting of a single dot only)
719  */
720 static err_t
smtp_verify(const char * data,size_t data_len,u8_t linebreaks_allowed)721 smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
722 {
723   size_t i;
724   u8_t last_was_cr = 0;
725   for (i = 0; i < data_len; i++) {
726     char current = data[i];
727     if ((current & 0x80) != 0) {
728       LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
729       return ERR_ARG;
730     }
731     if (current == '\r') {
732       if (!linebreaks_allowed) {
733         LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
734         return ERR_ARG;
735       }
736       if (last_was_cr) {
737         LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
738         return ERR_ARG;
739       }
740       last_was_cr = 1;
741     } else {
742       if (current == '\n') {
743         if (!last_was_cr) {
744           LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
745           return ERR_ARG;
746         }
747       }
748       last_was_cr = 0;
749     }
750   }
751   return ERR_OK;
752 }
753 #endif /* SMTP_CHECK_DATA */
754 
755 /** Frees the smtp_session and calls the callback function */
756 static void
smtp_free(struct smtp_session * s,u8_t result,u16_t srv_err,err_t err)757 smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
758 {
759   smtp_result_fn fn = s->callback_fn;
760   void *arg = s->callback_arg;
761   if (s->p != NULL) {
762     pbuf_free(s->p);
763   }
764   smtp_free_struct(s);
765   if (fn != NULL) {
766     fn(arg, result, srv_err, err);
767   }
768 }
769 
770 /** Try to close a pcb and free the arg if successful */
771 static void
smtp_close(struct smtp_session * s,struct altcp_pcb * pcb,u8_t result,u16_t srv_err,err_t err)772 smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
773            u16_t srv_err, err_t err)
774 {
775   if (pcb != NULL) {
776      altcp_arg(pcb, NULL);
777      if (altcp_close(pcb) == ERR_OK) {
778        if (s != NULL) {
779          smtp_free(s, result, srv_err, err);
780        }
781      } else {
782        /* close failed, set back arg */
783        altcp_arg(pcb, s);
784      }
785   } else {
786     if (s != NULL) {
787       smtp_free(s, result, srv_err, err);
788     }
789   }
790 }
791 
792 /** Raw API TCP err callback: pcb is already deallocated */
793 static void
smtp_tcp_err(void * arg,err_t err)794 smtp_tcp_err(void *arg, err_t err)
795 {
796   LWIP_UNUSED_ARG(err);
797   if (arg != NULL) {
798     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
799     smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
800   }
801 }
802 
803 /** Raw API TCP poll callback */
804 static err_t
smtp_tcp_poll(void * arg,struct altcp_pcb * pcb)805 smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
806 {
807   if (arg != NULL) {
808     struct smtp_session *s = (struct smtp_session*)arg;
809     if (s->timer != 0) {
810       s->timer--;
811     }
812   }
813   smtp_process(arg, pcb, NULL);
814   return ERR_OK;
815 }
816 
817 /** Raw API TCP sent callback */
818 static err_t
smtp_tcp_sent(void * arg,struct altcp_pcb * pcb,u16_t len)819 smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
820 {
821   LWIP_UNUSED_ARG(len);
822 
823   smtp_process(arg, pcb, NULL);
824 
825   return ERR_OK;
826 }
827 
828 /** Raw API TCP recv callback */
829 static err_t
smtp_tcp_recv(void * arg,struct altcp_pcb * pcb,struct pbuf * p,err_t err)830 smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
831 {
832   LWIP_UNUSED_ARG(err);
833   if (p != NULL) {
834     altcp_recved(pcb, p->tot_len);
835     smtp_process(arg, pcb, p);
836   } else {
837     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
838     smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
839   }
840   return ERR_OK;
841 }
842 
843 static err_t
smtp_tcp_connected(void * arg,struct altcp_pcb * pcb,err_t err)844 smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
845 {
846   LWIP_UNUSED_ARG(arg);
847 
848   if (err == ERR_OK) {
849     LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
850   } else {
851     /* shouldn't happen, but we still check 'err', only to be sure */
852     LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
853     smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
854   }
855   return ERR_OK;
856 }
857 
858 #if LWIP_DNS
859 /** DNS callback
860  * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
861  */
862 static void
smtp_dns_found(const char * hostname,const ip_addr_t * ipaddr,void * arg)863 smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
864 {
865   struct smtp_session *s = (struct smtp_session*)arg;
866   struct altcp_pcb *pcb;
867   err_t err;
868   u8_t result;
869 
870   LWIP_UNUSED_ARG(hostname);
871 
872   if (ipaddr != NULL) {
873     pcb = smtp_setup_pcb(s, ipaddr);
874     if (pcb != NULL) {
875       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
876       err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
877       if (err == ERR_OK) {
878         return;
879       }
880       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
881       result = SMTP_RESULT_ERR_CONNECT;
882     } else {
883       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
884       result = SMTP_RESULT_ERR_MEM;
885       err = ERR_MEM;
886     }
887   } else {
888     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
889       hostname));
890     pcb = NULL;
891     result = SMTP_RESULT_ERR_HOSTNAME;
892     err = ERR_ARG;
893   }
894   smtp_close(s, pcb, result, 0, err);
895 }
896 #endif /* LWIP_DNS */
897 
898 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
899 
900 /** Table 6-bit-index-to-ASCII used for base64-encoding */
901 static const char base64_table[] = {
902   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
903   'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
904   'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
905   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
906   'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
907   'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
908   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
909   '+', '/'
910 };
911 
912 /** Base64 encoding */
913 static size_t
smtp_base64_encode(char * target,size_t target_len,const char * source,size_t source_len)914 smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
915 {
916   size_t i;
917   s8_t j;
918   size_t target_idx = 0;
919   size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
920   size_t source_len_b64 = source_len + longer;
921   size_t len = (((source_len_b64) * 4) / 3);
922   u8_t x = 5;
923   u8_t current = 0;
924   LWIP_UNUSED_ARG(target_len);
925 
926   LWIP_ASSERT("target_len is too short", target_len >= len);
927 
928   for (i = 0; i < source_len_b64; i++) {
929     u8_t b = (i < source_len ? (u8_t)source[i] : 0);
930     for (j = 7; j >= 0; j--, x--) {
931       if ((b & (1 << j)) != 0) {
932         current = (u8_t)(current | (1U << x));
933       }
934       if (x == 0) {
935         target[target_idx++] = base64_table[current];
936         x = 6;
937         current = 0;
938       }
939     }
940   }
941   for (i = len - longer; i < len; i++) {
942     target[i] = '=';
943   }
944   return len;
945 }
946 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
947 
948 /** Parse pbuf to see if it contains the beginning of an answer.
949  * If so, it returns the contained response code as number between 1 and 999.
950  * If not, zero is returned.
951  *
952  * @param s smtp session struct
953  */
954 static u16_t
smtp_is_response(struct smtp_session * s)955 smtp_is_response(struct smtp_session *s)
956 {
957   char digits[4];
958   long num;
959 
960   if (s->p == NULL) {
961     return 0;
962   }
963   /* copy three digits and convert them to int */
964   if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
965     /* pbuf was too short */
966     return 0;
967   }
968   digits[3] = 0;
969   num = strtol(digits, NULL, 10);
970   if ((num <= 0) || (num >= 1000)) {
971     /* failed to find response code at start of line */
972     return 0;
973   }
974   return (u16_t)num;
975 }
976 
977 /** Parse pbuf to see if it contains a fully received answer.
978  * If one is found, ERR_OK is returned.
979  * If none is found, ERR_VAL is returned.
980  *
981  * A fully received answer is a 3-digit number followed by a space,
982  * some string and a CRLF as line ending.
983  *
984  * @param s smtp session struct
985  */
986 static err_t
smtp_is_response_finished(struct smtp_session * s)987 smtp_is_response_finished(struct smtp_session *s)
988 {
989   u8_t sp;
990   u16_t crlf;
991   u16_t offset;
992 
993   if (s->p == NULL) {
994     return ERR_VAL;
995   }
996   offset = 0;
997 again:
998   /* We could check the response number here, but we trust the
999    * protocol definition which says the client can rely on it being
1000    * the same on every line. */
1001 
1002   /* find CRLF */
1003   if (offset > 0xFFFF - 4) {
1004     /* would overflow */
1005     return ERR_VAL;
1006   }
1007   crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
1008   if (crlf == 0xFFFF) {
1009     /* no CRLF found */
1010     return ERR_VAL;
1011   }
1012   sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
1013   if (sp == '-') {
1014     /* no space after response code -> try next line */
1015     offset = (u16_t)(crlf + 2);
1016     if (offset < crlf) {
1017       /* overflow */
1018       return ERR_VAL;
1019     }
1020     goto again;
1021   } else if (sp == ' ') {
1022     /* CRLF found after response code + space -> valid response */
1023     return ERR_OK;
1024   }
1025   /* sp contains invalid character */
1026   return ERR_VAL;
1027 }
1028 
1029 /** Prepare HELO/EHLO message */
1030 static enum smtp_session_state
smtp_prepare_helo(struct smtp_session * s,u16_t * tx_buf_len,struct altcp_pcb * pcb)1031 smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
1032 {
1033   size_t ipa_len;
1034   const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
1035   LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
1036   ipa_len = strlen(ipa);
1037   LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
1038 
1039   *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
1040   LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1041 
1042   SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
1043   MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
1044   SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
1045   return SMTP_HELO;
1046 }
1047 
1048 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1049 /** Parse last server response (in rx_buf) for supported authentication method,
1050  * create data to send out (to tx_buf), set tx_data_len correctly
1051  * and return the next state.
1052  */
1053 static enum smtp_session_state
smtp_prepare_auth_or_mail(struct smtp_session * s,u16_t * tx_buf_len)1054 smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
1055 {
1056   /* check response for supported authentication method */
1057   u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
1058   if (auth == 0xFFFF) {
1059     auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
1060   }
1061   if (auth != 0xFFFF) {
1062     u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
1063     if ((crlf != 0xFFFF) && (crlf > auth)) {
1064       /* use tx_buf temporarily */
1065       u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
1066       if (copied != 0) {
1067         char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
1068         s->tx_buf[copied] = 0;
1069 #if SMTP_SUPPORT_AUTH_PLAIN
1070         /* favour PLAIN over LOGIN since it involves less requests */
1071         if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
1072           size_t auth_len;
1073           /* server supports AUTH PLAIN */
1074           SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
1075 
1076           /* add base64-encoded string "\0username\0password" */
1077           auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
1078             SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
1079             SMTP_AUTH_PLAIN_LEN(s));
1080           LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
1081           *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
1082           SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
1083             SMTP_CMD_AUTHPLAIN_2_LEN);
1084           return SMTP_AUTH_PLAIN;
1085         } else
1086 #endif /* SMTP_SUPPORT_AUTH_PLAIN */
1087         {
1088 #if SMTP_SUPPORT_AUTH_LOGIN
1089           if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
1090             /* server supports AUTH LOGIN */
1091             *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
1092             SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
1093             return SMTP_AUTH_LOGIN_UNAME;
1094           }
1095 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1096         }
1097       }
1098     }
1099   }
1100   /* server didnt's send correct keywords for AUTH, try sending directly */
1101   return smtp_prepare_mail(s, tx_buf_len);
1102 }
1103 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1104 
1105 #if SMTP_SUPPORT_AUTH_LOGIN
1106 /** Send base64-encoded username */
1107 static enum smtp_session_state
smtp_prepare_auth_login_uname(struct smtp_session * s,u16_t * tx_buf_len)1108 smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
1109 {
1110   size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1111     SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
1112   /* @todo: support base64-encoded longer than 64k */
1113   LWIP_ASSERT("string too long", base64_len <= 0xffff);
1114   LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1115   *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1116 
1117   SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1118   s->tx_buf[*tx_buf_len] = 0;
1119   return SMTP_AUTH_LOGIN_PASS;
1120 }
1121 
1122 /** Send base64-encoded password */
1123 static enum smtp_session_state
smtp_prepare_auth_login_pass(struct smtp_session * s,u16_t * tx_buf_len)1124 smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
1125 {
1126   size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1127     SMTP_PASS(s), strlen(SMTP_PASS(s)));
1128   /* @todo: support base64-encoded longer than 64k */
1129   LWIP_ASSERT("string too long", base64_len <= 0xffff);
1130   LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1131   *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1132 
1133   SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1134   s->tx_buf[*tx_buf_len] = 0;
1135   return SMTP_AUTH_LOGIN;
1136 }
1137 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1138 
1139 /** Prepare MAIL message */
1140 static enum smtp_session_state
smtp_prepare_mail(struct smtp_session * s,u16_t * tx_buf_len)1141 smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
1142 {
1143   char *target = s->tx_buf;
1144   LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
1145   *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
1146   target[*tx_buf_len] = 0;
1147 
1148   SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
1149   target += SMTP_CMD_MAIL_1_LEN;
1150   MEMCPY(target, s->from, s->from_len);
1151   target += s->from_len;
1152   SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
1153   return SMTP_MAIL;
1154 }
1155 
1156 /** Prepare RCPT message */
1157 static enum smtp_session_state
smtp_prepare_rcpt(struct smtp_session * s,u16_t * tx_buf_len)1158 smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
1159 {
1160   char *target = s->tx_buf;
1161   LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
1162   *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
1163   target[*tx_buf_len] = 0;
1164 
1165   SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
1166   target += SMTP_CMD_RCPT_1_LEN;
1167   MEMCPY(target, s->to, s->to_len);
1168   target += s->to_len;
1169   SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
1170   return SMTP_RCPT;
1171 }
1172 
1173 /** Prepare header of body */
1174 static enum smtp_session_state
smtp_prepare_header(struct smtp_session * s,u16_t * tx_buf_len)1175 smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
1176 {
1177   char *target = s->tx_buf;
1178   int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
1179     SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
1180     s->subject_len;
1181   LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
1182   *tx_buf_len = (u16_t)len;
1183   target[*tx_buf_len] = 0;
1184 
1185   SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
1186   target += SMTP_CMD_HEADER_1_LEN;
1187   MEMCPY(target, s->from, s->from_len);
1188   target += s->from_len;
1189   SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
1190   target += SMTP_CMD_HEADER_2_LEN;
1191   MEMCPY(target, s->to, s->to_len);
1192   target += s->to_len;
1193   SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
1194   target += SMTP_CMD_HEADER_3_LEN;
1195   MEMCPY(target, s->subject, s->subject_len);
1196   target += s->subject_len;
1197   SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
1198 
1199   return SMTP_BODY;
1200 }
1201 
1202 /** Prepare QUIT message */
1203 static enum smtp_session_state
smtp_prepare_quit(struct smtp_session * s,u16_t * tx_buf_len)1204 smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
1205 {
1206   *tx_buf_len = SMTP_CMD_QUIT_LEN;
1207   s->tx_buf[*tx_buf_len] = 0;
1208   SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
1209   LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1210   return SMTP_CLOSED;
1211 }
1212 
1213 /** If in state SMTP_BODY, try to send more body data */
1214 static void
smtp_send_body(struct smtp_session * s,struct altcp_pcb * pcb)1215 smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
1216 {
1217   err_t err;
1218 
1219   if (s->state == SMTP_BODY) {
1220 #if SMTP_BODYDH
1221     if (s->bodydh) {
1222       smtp_send_body_data_handler(s, pcb);
1223     } else
1224 #endif /* SMTP_BODYDH */
1225     {
1226       u16_t send_len = (u16_t)(s->body_len - s->body_sent);
1227       if (send_len > 0) {
1228         u16_t snd_buf = altcp_sndbuf(pcb);
1229         if (send_len > snd_buf) {
1230           send_len = snd_buf;
1231         }
1232         if (send_len > 0) {
1233           /* try to send something out */
1234           err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
1235           if (err == ERR_OK) {
1236             s->timer = SMTP_TIMEOUT_DATABLOCK;
1237             s->body_sent = (u16_t)(s->body_sent + send_len);
1238             if (s->body_sent < s->body_len) {
1239               LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
1240                 s->body_sent, s->body_len));
1241             }
1242           }
1243         }
1244       }
1245     }
1246     if (s->body_sent == s->body_len) {
1247       /* the whole body has been written, write last line */
1248       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
1249         s->body_len));
1250       err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
1251       if (err == ERR_OK) {
1252         s->timer = SMTP_TIMEOUT_DATATERM;
1253         LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
1254           smtp_state_str[SMTP_QUIT]));
1255         /* last line written, change state, wait for confirmation */
1256         s->state = SMTP_QUIT;
1257       }
1258     }
1259   }
1260 }
1261 
1262 /** State machine-like implementation of an SMTP client.
1263  */
1264 static void
smtp_process(void * arg,struct altcp_pcb * pcb,struct pbuf * p)1265 smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
1266 {
1267   struct smtp_session* s = (struct smtp_session*)arg;
1268   u16_t response_code = 0;
1269   u16_t tx_buf_len = 0;
1270   enum smtp_session_state next_state;
1271 
1272   if (arg == NULL) {
1273     /* already closed SMTP connection */
1274     if (p != NULL) {
1275       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
1276         p->tot_len, smtp_pbuf_str(p)));
1277       pbuf_free(p);
1278     }
1279     return;
1280   }
1281 
1282   next_state = s->state;
1283 
1284   if (p != NULL) {
1285     /* received data */
1286     if (s->p == NULL) {
1287       s->p = p;
1288     } else {
1289       pbuf_cat(s->p, p);
1290     }
1291   } else {
1292     /* idle timer, close connection if timed out */
1293     if (s->timer == 0) {
1294       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
1295       smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
1296       return;
1297     }
1298     if (s->state == SMTP_BODY) {
1299       smtp_send_body(s, pcb);
1300       return;
1301     }
1302   }
1303   response_code = smtp_is_response(s);
1304   if (response_code) {
1305     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
1306     if (smtp_is_response_finished(s) != ERR_OK) {
1307       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
1308       /* wait for next packet to complete the respone */
1309       return;
1310     }
1311   } else {
1312     if (s->p != NULL) {
1313       LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
1314         smtp_pbuf_str(s->p)));
1315       pbuf_free(s->p);
1316       s->p = NULL;
1317     }
1318     return;
1319   }
1320 
1321   switch(s->state)
1322   {
1323   case(SMTP_NULL):
1324     /* wait for 220 */
1325     if (response_code == 220) {
1326       /* then send EHLO */
1327       next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
1328     }
1329     break;
1330   case(SMTP_HELO):
1331     /* wait for 250 */
1332     if (response_code == 250) {
1333 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1334       /* then send AUTH or MAIL */
1335       next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
1336     }
1337     break;
1338   case(SMTP_AUTH_LOGIN):
1339   case(SMTP_AUTH_PLAIN):
1340     /* wait for 235 */
1341     if (response_code == 235) {
1342 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1343       /* send MAIL */
1344       next_state = smtp_prepare_mail(s, &tx_buf_len);
1345     }
1346     break;
1347 #if SMTP_SUPPORT_AUTH_LOGIN
1348   case(SMTP_AUTH_LOGIN_UNAME):
1349     /* wait for 334 Username */
1350     if (response_code == 334) {
1351       if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
1352         /* send username */
1353         next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
1354       }
1355     }
1356     break;
1357   case(SMTP_AUTH_LOGIN_PASS):
1358     /* wait for 334 Password */
1359     if (response_code == 334) {
1360       if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
1361         /* send username */
1362         next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
1363       }
1364     }
1365     break;
1366 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1367   case(SMTP_MAIL):
1368     /* wait for 250 */
1369     if (response_code == 250) {
1370       /* send RCPT */
1371       next_state = smtp_prepare_rcpt(s, &tx_buf_len);
1372     }
1373     break;
1374   case(SMTP_RCPT):
1375     /* wait for 250 */
1376     if (response_code == 250) {
1377       /* send DATA */
1378       SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
1379       tx_buf_len = SMTP_CMD_DATA_LEN;
1380       next_state = SMTP_DATA;
1381     }
1382     break;
1383   case(SMTP_DATA):
1384     /* wait for 354 */
1385     if (response_code == 354) {
1386       /* send email header */
1387       next_state = smtp_prepare_header(s, &tx_buf_len);
1388     }
1389     break;
1390   case(SMTP_BODY):
1391     /* nothing to be done here, handled somewhere else */
1392     break;
1393   case(SMTP_QUIT):
1394     /* wait for 250 */
1395     if (response_code == 250) {
1396       /* send QUIT */
1397       next_state = smtp_prepare_quit(s, &tx_buf_len);
1398     }
1399     break;
1400   case(SMTP_CLOSED):
1401     /* nothing to do, wait for connection closed from server */
1402     return;
1403   default:
1404     LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
1405       smtp_state_str[s->state]));
1406     break;
1407   }
1408   if (s->state == next_state) {
1409     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
1410       smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1411     /* close connection */
1412     smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
1413     return;
1414   }
1415   if (tx_buf_len > 0) {
1416     SMTP_TX_BUF_MAX(tx_buf_len);
1417     if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
1418       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
1419         smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1420       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
1421         smtp_state_str[s->state], tx_buf_len, s->tx_buf));
1422       s->timer = SMTP_TIMEOUT;
1423       pbuf_free(s->p);
1424       s->p = NULL;
1425       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
1426         smtp_state_str[s->state], smtp_state_str[next_state]));
1427       s->state = next_state;
1428       if (next_state == SMTP_BODY) {
1429         /* try to stream-send body data right now */
1430         smtp_send_body(s, pcb);
1431       } else if (next_state == SMTP_CLOSED) {
1432         /* sent out all data, delete structure */
1433         altcp_arg(pcb, NULL);
1434         smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
1435       }
1436     }
1437   }
1438 }
1439 
1440 #if SMTP_BODYDH
1441 /** Elementary sub-function to send data
1442  *
1443  * @returns: BDHALLDATASENT all data has been written
1444  *           BDHSOMEDATASENT some data has been written
1445  *           0 no data has been written
1446  */
1447 static int
smtp_send_bodyh_data(struct altcp_pcb * pcb,const char ** from,u16_t * howmany)1448 smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
1449 {
1450   err_t err;
1451   u16_t len = *howmany;
1452 
1453   len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
1454   err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
1455   if (err == ERR_OK) {
1456     *from += len;
1457     if ((*howmany -= len) > 0) {
1458       return BDHSOMEDATASENT;
1459     }
1460     return BDHALLDATASENT;
1461   }
1462   return 0;
1463 }
1464 
1465 /** Same as smtp_send_mail_static, but uses a callback function to send body data
1466  */
1467 err_t
smtp_send_mail_bodycback(const char * from,const char * to,const char * subject,smtp_bodycback_fn bodycback_fn,smtp_result_fn callback_fn,void * callback_arg)1468 smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
1469   smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
1470 {
1471   struct smtp_session* s;
1472   size_t len;
1473 
1474   LWIP_ASSERT_CORE_LOCKED();
1475 
1476   s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
1477   if (s == NULL) {
1478     return ERR_MEM;
1479   }
1480   memset(s, 0, sizeof(struct smtp_session));
1481   s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
1482   if (s->bodydh == NULL) {
1483     SMTP_STATE_FREE(s);
1484     return ERR_MEM;
1485   }
1486   memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state));
1487   /* initialize the structure */
1488   s->from = from;
1489   len = strlen(from);
1490   LWIP_ASSERT("string is too long", len <= 0xffff);
1491   s->from_len = (u16_t)len;
1492   s->to = to;
1493   len = strlen(to);
1494   LWIP_ASSERT("string is too long", len <= 0xffff);
1495   s->to_len = (u16_t)len;
1496   s->subject = subject;
1497   len = strlen(subject);
1498   LWIP_ASSERT("string is too long", len <= 0xffff);
1499   s->subject_len = (u16_t)len;
1500   s->body = NULL;
1501   s->callback_fn = callback_fn;
1502   s->callback_arg = callback_arg;
1503   s->bodydh->callback_fn = bodycback_fn;
1504   s->bodydh->state = BDH_SENDING;
1505   /* call the actual implementation of this function */
1506   return smtp_send_mail_alloced(s);
1507 }
1508 
1509 static void
smtp_send_body_data_handler(struct smtp_session * s,struct altcp_pcb * pcb)1510 smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
1511 {
1512   struct smtp_bodydh_state *bdh;
1513   int res = 0, ret;
1514   LWIP_ASSERT("s != NULL", s != NULL);
1515   bdh = s->bodydh;
1516   LWIP_ASSERT("bodydh != NULL", bdh != NULL);
1517 
1518   /* resume any leftovers from prior memory constraints */
1519   if (s->body_len) {
1520     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
1521     if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
1522         != BDHALLDATASENT) {
1523       s->body_sent = s->body_len - 1;
1524       return;
1525     }
1526   }
1527   ret = res;
1528   /* all data on buffer has been queued, resume execution */
1529   if (bdh->state == BDH_SENDING) {
1530     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
1531     do {
1532       ret |= res; /* remember if we once queued something to send */
1533       bdh->exposed.length = 0;
1534       if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
1535         bdh->state = BDH_STOP;
1536       }
1537       s->body = bdh->exposed.buffer;
1538       s->body_len = bdh->exposed.length;
1539       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
1540     } while (s->body_len &&
1541             ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
1542             && (bdh->state != BDH_STOP));
1543   }
1544   if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
1545     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
1546     s->body_sent = s->body_len;
1547   } else {
1548     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
1549     s->body_sent = s->body_len - 1;
1550   }
1551 }
1552 #endif /* SMTP_BODYDH */
1553 
1554 #endif /* LWIP_TCP && LWIP_CALLBACK_API */
1555