xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.1.0/src/apps/smtp/smtp.c (revision 104654410c56c573564690304ae786df310c91fc)
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> /* strnlen, 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     /* strnlen: returns length WITHOUT terminating 0 byte OR
357      * SMTP_MAX_SERVERNAME_LEN+1 when string is too long */
358     len = strnlen(server, SMTP_MAX_SERVERNAME_LEN+1);
359   }
360   if (len > SMTP_MAX_SERVERNAME_LEN) {
361     return ERR_MEM;
362   }
363   if (len != 0) {
364     MEMCPY(smtp_server, server, len);
365   }
366   smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
367   return ERR_OK;
368 }
369 
370 /** @ingroup smtp
371  * Set TCP port for next SMTP connection
372  *
373  * @param port TCP port
374  */
375 void
smtp_set_server_port(u16_t port)376 smtp_set_server_port(u16_t port)
377 {
378   LWIP_ASSERT_CORE_LOCKED();
379   smtp_server_port = port;
380 }
381 
382 #if LWIP_ALTCP && LWIP_ALTCP_TLS
383 /** @ingroup smtp
384  * Set TLS configuration for next SMTP connection
385  *
386  * @param tls_config TLS configuration
387  */
388 void
smtp_set_tls_config(struct altcp_tls_config * tls_config)389 smtp_set_tls_config(struct altcp_tls_config *tls_config)
390 {
391   LWIP_ASSERT_CORE_LOCKED();
392   smtp_server_tls_config = tls_config;
393 }
394 #endif
395 
396 /** @ingroup smtp
397  * Set authentication parameters for next SMTP connection
398  *
399  * @param username login name as passed to the server
400  * @param pass password passed to the server together with username
401  */
402 err_t
smtp_set_auth(const char * username,const char * pass)403 smtp_set_auth(const char* username, const char* pass)
404 {
405   size_t uname_len = 0;
406   size_t pass_len = 0;
407 
408   LWIP_ASSERT_CORE_LOCKED();
409 
410   memset(smtp_auth_plain, 0xfa, 64);
411   if (username != NULL) {
412     uname_len = strlen(username);
413     if (uname_len > SMTP_MAX_USERNAME_LEN) {
414       LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
415         (int)uname_len, SMTP_MAX_USERNAME_LEN));
416       return ERR_ARG;
417     }
418   }
419   if (pass != NULL) {
420 #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
421     pass_len = strlen(pass);
422     if (pass_len > SMTP_MAX_PASS_LEN) {
423       LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
424         (int)uname_len, SMTP_MAX_USERNAME_LEN));
425       return ERR_ARG;
426     }
427 #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
428     LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
429 #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
430   }
431   *smtp_auth_plain = 0;
432   if (username != NULL) {
433     smtp_username = smtp_auth_plain + 1;
434     strcpy(smtp_username, username);
435   }
436   if (pass != NULL) {
437     smtp_pass = smtp_auth_plain + uname_len + 2;
438     strcpy(smtp_pass, pass);
439   }
440   smtp_auth_plain_len = uname_len + pass_len + 2;
441 
442   return ERR_OK;
443 }
444 
445 #if SMTP_BODYDH
smtp_free_struct(struct smtp_session * s)446 static void smtp_free_struct(struct smtp_session *s)
447 {
448   if (s->bodydh != NULL) {
449     SMTP_BODYDH_FREE(s->bodydh);
450   }
451   SMTP_STATE_FREE(s);
452 }
453 #else /* SMTP_BODYDH */
454 #define smtp_free_struct(x) SMTP_STATE_FREE(x)
455 #endif /* SMTP_BODYDH */
456 
457 static struct altcp_pcb*
smtp_setup_pcb(struct smtp_session * s,const ip_addr_t * remote_ip)458 smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
459 {
460   struct altcp_pcb* pcb;
461   LWIP_UNUSED_ARG(remote_ip);
462 
463 #if LWIP_ALTCP && LWIP_ALTCP_TLS
464   if (smtp_server_tls_config) {
465     pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip));
466   } else
467 #endif
468   {
469     pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
470   }
471   if (pcb != NULL) {
472     altcp_arg(pcb, s);
473     altcp_recv(pcb, smtp_tcp_recv);
474     altcp_err(pcb, smtp_tcp_err);
475     altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
476     altcp_sent(pcb, smtp_tcp_sent);
477   }
478   return pcb;
479 }
480 
481 /** The actual mail-sending function, called by smtp_send_mail and
482  * smtp_send_mail_static after setting up the struct smtp_session.
483  */
484 static err_t
smtp_send_mail_alloced(struct smtp_session * s)485 smtp_send_mail_alloced(struct smtp_session *s)
486 {
487   err_t err;
488   struct altcp_pcb* pcb = NULL;
489   ip_addr_t addr;
490 
491   LWIP_ASSERT("no smtp_session supplied", s != NULL);
492 
493 #if SMTP_CHECK_DATA
494   /* check that body conforms to RFC:
495    * - convert all single-CR or -LF in body to CRLF
496    * - only 7-bit ASCII is allowed
497    */
498   if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
499     err = ERR_ARG;
500     goto leave;
501   }
502   if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
503     err = ERR_ARG;
504     goto leave;
505   }
506   if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
507     err = ERR_ARG;
508     goto leave;
509   }
510 #if SMTP_BODYDH
511   if (s->bodydh == NULL)
512 #endif /* SMTP_BODYDH */
513   {
514     if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
515       err = ERR_ARG;
516       goto leave;
517     }
518   }
519 #endif /* SMTP_CHECK_DATA */
520 
521 #if SMTP_COPY_AUTHDATA
522   /* copy auth data, ensuring the first byte is always zero */
523   MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
524   s->auth_plain_len = smtp_auth_plain_len;
525   /* default username and pass is empty string */
526   s->username = s->auth_plain;
527   s->pass = s->auth_plain;
528   if (smtp_username != NULL) {
529     s->username += smtp_username - smtp_auth_plain;
530   }
531   if (smtp_pass != NULL) {
532     s->pass += smtp_pass - smtp_auth_plain;
533   }
534 #endif /* SMTP_COPY_AUTHDATA */
535 
536   s->state = SMTP_NULL;
537   s->timer = SMTP_TIMEOUT;
538 
539 #if LWIP_DNS
540   err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
541 #else /* LWIP_DNS */
542   err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
543 #endif /* LWIP_DNS */
544   if (err == ERR_OK) {
545     pcb = smtp_setup_pcb(s, &addr);
546     if (pcb == NULL) {
547       err = ERR_MEM;
548       goto leave;
549     }
550     err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
551     if (err != ERR_OK) {
552       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
553       goto deallocate_and_leave;
554     }
555   } else if (err != ERR_INPROGRESS) {
556     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
557     goto deallocate_and_leave;
558   }
559   return ERR_OK;
560 
561 deallocate_and_leave:
562   if (pcb != NULL) {
563     altcp_arg(pcb, NULL);
564     altcp_close(pcb);
565   }
566 leave:
567   smtp_free_struct(s);
568   /* no need to call the callback here since we return != ERR_OK */
569   return err;
570 }
571 
572 /** @ingroup smtp
573  *  Send an email via the currently selected server, username and password.
574  *
575  * @param from source email address (must be NULL-terminated)
576  * @param to target email address (must be NULL-terminated)
577  * @param subject email subject (must be NULL-terminated)
578  * @param body email body (must be NULL-terminated)
579  * @param callback_fn callback function
580  * @param callback_arg user argument to callback_fn
581  * @returns - ERR_OK if structures were allocated and no error occured starting the connection
582  *            (this does not mean the email has been successfully sent!)
583  *          - another err_t on error.
584  */
585 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)586 smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
587                smtp_result_fn callback_fn, void* callback_arg)
588 {
589   struct smtp_session* s;
590   size_t from_len = strlen(from);
591   size_t to_len = strlen(to);
592   size_t subject_len = strlen(subject);
593   size_t body_len = strlen(body);
594   size_t mem_len = sizeof(struct smtp_session);
595   char *sfrom, *sto, *ssubject, *sbody;
596 
597   LWIP_ASSERT_CORE_LOCKED();
598 
599   mem_len += from_len + to_len + subject_len + body_len + 4;
600   if (mem_len > 0xffff) {
601     /* too long! */
602     return ERR_MEM;
603   }
604 
605   /* Allocate memory to keep this email's session state */
606   s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
607   if (s == NULL) {
608     return ERR_MEM;
609   }
610   /* initialize the structure */
611   memset(s, 0, mem_len);
612   s->from = sfrom = (char*)s + sizeof(struct smtp_session);
613   s->from_len = (u16_t)from_len;
614   s->to = sto = sfrom + from_len + 1;
615   s->to_len = (u16_t)to_len;
616   s->subject = ssubject = sto + to_len + 1;
617   s->subject_len = (u16_t)subject_len;
618   s->body = sbody = ssubject + subject_len + 1;
619   s->body_len = (u16_t)body_len;
620   /* copy source and target email address */
621   /* cast to size_t is a hack to cast away constness */
622   MEMCPY(sfrom, from, from_len + 1);
623   MEMCPY(sto, to, to_len + 1);
624   MEMCPY(ssubject, subject, subject_len + 1);
625   MEMCPY(sbody, body, body_len + 1);
626 
627   s->callback_fn = callback_fn;
628   s->callback_arg = callback_arg;
629 
630   /* call the actual implementation of this function */
631   return smtp_send_mail_alloced(s);
632 }
633 
634 /** @ingroup smtp
635  * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
636  * an internal buffer to save memory.
637  * WARNING: the above data must stay untouched until the callback function is
638  *          called (unless the function returns != ERR_OK)
639  */
640 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)641 smtp_send_mail_static(const char *from, const char* to, const char* subject,
642   const char* body, smtp_result_fn callback_fn, void* callback_arg)
643 {
644   struct smtp_session* s;
645   size_t len;
646 
647   LWIP_ASSERT_CORE_LOCKED();
648 
649   s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
650   if (s == NULL) {
651     return ERR_MEM;
652   }
653   memset(s, 0, sizeof(struct smtp_session));
654   /* initialize the structure */
655   s->from = from;
656   len = strlen(from);
657   LWIP_ASSERT("string is too long", len <= 0xffff);
658   s->from_len = (u16_t)len;
659   s->to = to;
660   len = strlen(to);
661   LWIP_ASSERT("string is too long", len <= 0xffff);
662   s->to_len = (u16_t)len;
663   s->subject = subject;
664   len = strlen(subject);
665   LWIP_ASSERT("string is too long", len <= 0xffff);
666   s->subject_len = (u16_t)len;
667   s->body = body;
668   len = strlen(body);
669   LWIP_ASSERT("string is too long", len <= 0xffff);
670   s->body_len = (u16_t)len;
671   s->callback_fn = callback_fn;
672   s->callback_arg = callback_arg;
673   /* call the actual implementation of this function */
674   return smtp_send_mail_alloced(s);
675 }
676 
677 
678 /** @ingroup smtp
679  * Same as smtp_send_mail but takes a struct smtp_send_request as single
680  * parameter which contains all the other parameters.
681  * To be used with tcpip_callback to send mail from interrupt context or from
682  * another thread.
683  *
684  * WARNING: server and authentication must stay untouched until this function has run!
685  *
686  * Usage example:
687  * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
688  * - fill the members of the struct as if calling smtp_send_mail
689  * - specify a callback_function
690  * - set callback_arg to the structure itself
691  * - call this function
692  * - wait for the callback function to be called
693  * - in the callback function, deallocate the structure (passed as arg)
694  */
695 void
smtp_send_mail_int(void * arg)696 smtp_send_mail_int(void *arg)
697 {
698   struct smtp_send_request *req = (struct smtp_send_request*)arg;
699   err_t err;
700 
701   LWIP_ASSERT_CORE_LOCKED();
702   LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
703 
704   if (req->static_data) {
705     err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
706       req->callback_fn, req->callback_arg);
707   } else {
708     err = smtp_send_mail(req->from, req->to, req->subject, req->body,
709       req->callback_fn, req->callback_arg);
710   }
711   if ((err != ERR_OK) && (req->callback_fn != NULL)) {
712     req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
713   }
714 }
715 
716 #if SMTP_CHECK_DATA
717 /** Verify that a given string conforms to the SMTP rules
718  * (7-bit only, no single CR or LF,
719  *  @todo: no line consisting of a single dot only)
720  */
721 static err_t
smtp_verify(const char * data,size_t data_len,u8_t linebreaks_allowed)722 smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
723 {
724   size_t i;
725   u8_t last_was_cr = 0;
726   for (i = 0; i < data_len; i++) {
727     char current = data[i];
728     if ((current & 0x80) != 0) {
729       LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
730       return ERR_ARG;
731     }
732     if (current == '\r') {
733       if (!linebreaks_allowed) {
734         LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
735         return ERR_ARG;
736       }
737       if (last_was_cr) {
738         LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
739         return ERR_ARG;
740       }
741       last_was_cr = 1;
742     } else {
743       if (current == '\n') {
744         if (!last_was_cr) {
745           LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
746           return ERR_ARG;
747         }
748       }
749       last_was_cr = 0;
750     }
751   }
752   return ERR_OK;
753 }
754 #endif /* SMTP_CHECK_DATA */
755 
756 /** Frees the smtp_session and calls the callback function */
757 static void
smtp_free(struct smtp_session * s,u8_t result,u16_t srv_err,err_t err)758 smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
759 {
760   smtp_result_fn fn = s->callback_fn;
761   void *arg = s->callback_arg;
762   if (s->p != NULL) {
763     pbuf_free(s->p);
764   }
765   smtp_free_struct(s);
766   if (fn != NULL) {
767     fn(arg, result, srv_err, err);
768   }
769 }
770 
771 /** Try to close a pcb and free the arg if successful */
772 static void
smtp_close(struct smtp_session * s,struct altcp_pcb * pcb,u8_t result,u16_t srv_err,err_t err)773 smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
774            u16_t srv_err, err_t err)
775 {
776   if (pcb != NULL) {
777      altcp_arg(pcb, NULL);
778      if (altcp_close(pcb) == ERR_OK) {
779        if (s != NULL) {
780          smtp_free(s, result, srv_err, err);
781        }
782      } else {
783        /* close failed, set back arg */
784        altcp_arg(pcb, s);
785      }
786   } else {
787     if (s != NULL) {
788       smtp_free(s, result, srv_err, err);
789     }
790   }
791 }
792 
793 /** Raw API TCP err callback: pcb is already deallocated */
794 static void
smtp_tcp_err(void * arg,err_t err)795 smtp_tcp_err(void *arg, err_t err)
796 {
797   LWIP_UNUSED_ARG(err);
798   if (arg != NULL) {
799     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
800     smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
801   }
802 }
803 
804 /** Raw API TCP poll callback */
805 static err_t
smtp_tcp_poll(void * arg,struct altcp_pcb * pcb)806 smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
807 {
808   if (arg != NULL) {
809     struct smtp_session *s = (struct smtp_session*)arg;
810     if (s->timer != 0) {
811       s->timer--;
812     }
813   }
814   smtp_process(arg, pcb, NULL);
815   return ERR_OK;
816 }
817 
818 /** Raw API TCP sent callback */
819 static err_t
smtp_tcp_sent(void * arg,struct altcp_pcb * pcb,u16_t len)820 smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
821 {
822   LWIP_UNUSED_ARG(len);
823 
824   smtp_process(arg, pcb, NULL);
825 
826   return ERR_OK;
827 }
828 
829 /** Raw API TCP recv callback */
830 static err_t
smtp_tcp_recv(void * arg,struct altcp_pcb * pcb,struct pbuf * p,err_t err)831 smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
832 {
833   LWIP_UNUSED_ARG(err);
834   if (p != NULL) {
835     altcp_recved(pcb, p->tot_len);
836     smtp_process(arg, pcb, p);
837   } else {
838     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
839     smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
840   }
841   return ERR_OK;
842 }
843 
844 static err_t
smtp_tcp_connected(void * arg,struct altcp_pcb * pcb,err_t err)845 smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
846 {
847   LWIP_UNUSED_ARG(arg);
848 
849   if (err == ERR_OK) {
850     LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
851   } else {
852     /* shouldn't happen, but we still check 'err', only to be sure */
853     LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
854     smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
855   }
856   return ERR_OK;
857 }
858 
859 #if LWIP_DNS
860 /** DNS callback
861  * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
862  */
863 static void
smtp_dns_found(const char * hostname,const ip_addr_t * ipaddr,void * arg)864 smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
865 {
866   struct smtp_session *s = (struct smtp_session*)arg;
867   struct altcp_pcb *pcb;
868   err_t err;
869   u8_t result;
870 
871   LWIP_UNUSED_ARG(hostname);
872 
873   if (ipaddr != NULL) {
874     pcb = smtp_setup_pcb(s, ipaddr);
875     if (pcb != NULL) {
876       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
877       err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
878       if (err == ERR_OK) {
879         return;
880       }
881       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
882       result = SMTP_RESULT_ERR_CONNECT;
883     } else {
884       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
885       result = SMTP_RESULT_ERR_MEM;
886       err = ERR_MEM;
887     }
888   } else {
889     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
890       hostname));
891     pcb = NULL;
892     result = SMTP_RESULT_ERR_HOSTNAME;
893     err = ERR_ARG;
894   }
895   smtp_close(s, pcb, result, 0, err);
896 }
897 #endif /* LWIP_DNS */
898 
899 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
900 
901 /** Table 6-bit-index-to-ASCII used for base64-encoding */
902 static const char base64_table[] = {
903   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
904   'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
905   'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
906   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
907   'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
908   'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
909   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
910   '+', '/'
911 };
912 
913 /** Base64 encoding */
914 static size_t
smtp_base64_encode(char * target,size_t target_len,const char * source,size_t source_len)915 smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
916 {
917   size_t i;
918   s8_t j;
919   size_t target_idx = 0;
920   size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
921   size_t source_len_b64 = source_len + longer;
922   size_t len = (((source_len_b64) * 4) / 3);
923   u8_t x = 5;
924   u8_t current = 0;
925   LWIP_UNUSED_ARG(target_len);
926 
927   LWIP_ASSERT("target_len is too short", target_len >= len);
928 
929   for (i = 0; i < source_len_b64; i++) {
930     u8_t b = (i < source_len ? (u8_t)source[i] : 0);
931     for (j = 7; j >= 0; j--, x--) {
932       if ((b & (1 << j)) != 0) {
933         current = (u8_t)(current | (1U << x));
934       }
935       if (x == 0) {
936         target[target_idx++] = base64_table[current];
937         x = 6;
938         current = 0;
939       }
940     }
941   }
942   for (i = len - longer; i < len; i++) {
943     target[i] = '=';
944   }
945   return len;
946 }
947 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
948 
949 /** Parse pbuf to see if it contains the beginning of an answer.
950  * If so, it returns the contained response code as number between 1 and 999.
951  * If not, zero is returned.
952  *
953  * @param s smtp session struct
954  */
955 static u16_t
smtp_is_response(struct smtp_session * s)956 smtp_is_response(struct smtp_session *s)
957 {
958   char digits[4];
959   long num;
960 
961   if (s->p == NULL) {
962     return 0;
963   }
964   /* copy three digits and convert them to int */
965   if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
966     /* pbuf was too short */
967     return 0;
968   }
969   digits[3] = 0;
970   num = strtol(digits, NULL, 10);
971   if ((num <= 0) || (num >= 1000)) {
972     /* failed to find response code at start of line */
973     return 0;
974   }
975   return (u16_t)num;
976 }
977 
978 /** Parse pbuf to see if it contains a fully received answer.
979  * If one is found, ERR_OK is returned.
980  * If none is found, ERR_VAL is returned.
981  *
982  * A fully received answer is a 3-digit number followed by a space,
983  * some string and a CRLF as line ending.
984  *
985  * @param s smtp session struct
986  */
987 static err_t
smtp_is_response_finished(struct smtp_session * s)988 smtp_is_response_finished(struct smtp_session *s)
989 {
990   u8_t sp;
991   u16_t crlf;
992   u16_t offset;
993 
994   if (s->p == NULL) {
995     return ERR_VAL;
996   }
997   offset = 0;
998 again:
999   /* We could check the response number here, but we trust the
1000    * protocol definition which says the client can rely on it being
1001    * the same on every line. */
1002 
1003   /* find CRLF */
1004   if (offset > 0xFFFF - 4) {
1005     /* would overflow */
1006     return ERR_VAL;
1007   }
1008   crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
1009   if (crlf == 0xFFFF) {
1010     /* no CRLF found */
1011     return ERR_VAL;
1012   }
1013   sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
1014   if (sp == '-') {
1015     /* no space after response code -> try next line */
1016     offset = (u16_t)(crlf + 2);
1017     if (offset < crlf) {
1018       /* overflow */
1019       return ERR_VAL;
1020     }
1021     goto again;
1022   } else if (sp == ' ') {
1023     /* CRLF found after response code + space -> valid response */
1024     return ERR_OK;
1025   }
1026   /* sp contains invalid character */
1027   return ERR_VAL;
1028 }
1029 
1030 /** Prepare HELO/EHLO message */
1031 static enum smtp_session_state
smtp_prepare_helo(struct smtp_session * s,u16_t * tx_buf_len,struct altcp_pcb * pcb)1032 smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
1033 {
1034   size_t ipa_len;
1035   const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
1036   LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
1037   ipa_len = strlen(ipa);
1038   LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
1039 
1040   *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
1041   LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1042 
1043   SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
1044   MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
1045   SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
1046   return SMTP_HELO;
1047 }
1048 
1049 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1050 /** Parse last server response (in rx_buf) for supported authentication method,
1051  * create data to send out (to tx_buf), set tx_data_len correctly
1052  * and return the next state.
1053  */
1054 static enum smtp_session_state
smtp_prepare_auth_or_mail(struct smtp_session * s,u16_t * tx_buf_len)1055 smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
1056 {
1057   /* check response for supported authentication method */
1058   u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
1059   if (auth == 0xFFFF) {
1060     auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
1061   }
1062   if (auth != 0xFFFF) {
1063     u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
1064     if ((crlf != 0xFFFF) && (crlf > auth)) {
1065       /* use tx_buf temporarily */
1066       u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
1067       if (copied != 0) {
1068         char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
1069         s->tx_buf[copied] = 0;
1070 #if SMTP_SUPPORT_AUTH_PLAIN
1071         /* favour PLAIN over LOGIN since it involves less requests */
1072         if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
1073           size_t auth_len;
1074           /* server supports AUTH PLAIN */
1075           SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
1076 
1077           /* add base64-encoded string "\0username\0password" */
1078           auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
1079             SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
1080             SMTP_AUTH_PLAIN_LEN(s));
1081           LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
1082           *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
1083           SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
1084             SMTP_CMD_AUTHPLAIN_2_LEN);
1085           return SMTP_AUTH_PLAIN;
1086         } else
1087 #endif /* SMTP_SUPPORT_AUTH_PLAIN */
1088         {
1089 #if SMTP_SUPPORT_AUTH_LOGIN
1090           if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
1091             /* server supports AUTH LOGIN */
1092             *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
1093             SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
1094             return SMTP_AUTH_LOGIN_UNAME;
1095           }
1096 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1097         }
1098       }
1099     }
1100   }
1101   /* server didnt's send correct keywords for AUTH, try sending directly */
1102   return smtp_prepare_mail(s, tx_buf_len);
1103 }
1104 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1105 
1106 #if SMTP_SUPPORT_AUTH_LOGIN
1107 /** Send base64-encoded username */
1108 static enum smtp_session_state
smtp_prepare_auth_login_uname(struct smtp_session * s,u16_t * tx_buf_len)1109 smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
1110 {
1111   size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1112     SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
1113   /* @todo: support base64-encoded longer than 64k */
1114   LWIP_ASSERT("string too long", base64_len <= 0xffff);
1115   LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1116   *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1117 
1118   SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1119   s->tx_buf[*tx_buf_len] = 0;
1120   return SMTP_AUTH_LOGIN_PASS;
1121 }
1122 
1123 /** Send base64-encoded password */
1124 static enum smtp_session_state
smtp_prepare_auth_login_pass(struct smtp_session * s,u16_t * tx_buf_len)1125 smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
1126 {
1127   size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
1128     SMTP_PASS(s), strlen(SMTP_PASS(s)));
1129   /* @todo: support base64-encoded longer than 64k */
1130   LWIP_ASSERT("string too long", base64_len <= 0xffff);
1131   LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
1132   *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
1133 
1134   SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
1135   s->tx_buf[*tx_buf_len] = 0;
1136   return SMTP_AUTH_LOGIN;
1137 }
1138 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1139 
1140 /** Prepare MAIL message */
1141 static enum smtp_session_state
smtp_prepare_mail(struct smtp_session * s,u16_t * tx_buf_len)1142 smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
1143 {
1144   char *target = s->tx_buf;
1145   LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
1146   *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
1147   target[*tx_buf_len] = 0;
1148 
1149   SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
1150   target += SMTP_CMD_MAIL_1_LEN;
1151   MEMCPY(target, s->from, s->from_len);
1152   target += s->from_len;
1153   SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
1154   return SMTP_MAIL;
1155 }
1156 
1157 /** Prepare RCPT message */
1158 static enum smtp_session_state
smtp_prepare_rcpt(struct smtp_session * s,u16_t * tx_buf_len)1159 smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
1160 {
1161   char *target = s->tx_buf;
1162   LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
1163   *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
1164   target[*tx_buf_len] = 0;
1165 
1166   SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
1167   target += SMTP_CMD_RCPT_1_LEN;
1168   MEMCPY(target, s->to, s->to_len);
1169   target += s->to_len;
1170   SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
1171   return SMTP_RCPT;
1172 }
1173 
1174 /** Prepare header of body */
1175 static enum smtp_session_state
smtp_prepare_header(struct smtp_session * s,u16_t * tx_buf_len)1176 smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
1177 {
1178   char *target = s->tx_buf;
1179   int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
1180     SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
1181     s->subject_len;
1182   LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
1183   *tx_buf_len = (u16_t)len;
1184   target[*tx_buf_len] = 0;
1185 
1186   SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
1187   target += SMTP_CMD_HEADER_1_LEN;
1188   MEMCPY(target, s->from, s->from_len);
1189   target += s->from_len;
1190   SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
1191   target += SMTP_CMD_HEADER_2_LEN;
1192   MEMCPY(target, s->to, s->to_len);
1193   target += s->to_len;
1194   SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
1195   target += SMTP_CMD_HEADER_3_LEN;
1196   MEMCPY(target, s->subject, s->subject_len);
1197   target += s->subject_len;
1198   SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
1199 
1200   return SMTP_BODY;
1201 }
1202 
1203 /** Prepare QUIT message */
1204 static enum smtp_session_state
smtp_prepare_quit(struct smtp_session * s,u16_t * tx_buf_len)1205 smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
1206 {
1207   *tx_buf_len = SMTP_CMD_QUIT_LEN;
1208   s->tx_buf[*tx_buf_len] = 0;
1209   SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
1210   LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
1211   return SMTP_CLOSED;
1212 }
1213 
1214 /** If in state SMTP_BODY, try to send more body data */
1215 static void
smtp_send_body(struct smtp_session * s,struct altcp_pcb * pcb)1216 smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
1217 {
1218   err_t err;
1219 
1220   if (s->state == SMTP_BODY) {
1221 #if SMTP_BODYDH
1222     if (s->bodydh) {
1223       smtp_send_body_data_handler(s, pcb);
1224     } else
1225 #endif /* SMTP_BODYDH */
1226     {
1227       u16_t send_len = (u16_t)(s->body_len - s->body_sent);
1228       if (send_len > 0) {
1229         u16_t snd_buf = altcp_sndbuf(pcb);
1230         if (send_len > snd_buf) {
1231           send_len = snd_buf;
1232         }
1233         if (send_len > 0) {
1234           /* try to send something out */
1235           err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
1236           if (err == ERR_OK) {
1237             s->timer = SMTP_TIMEOUT_DATABLOCK;
1238             s->body_sent = (u16_t)(s->body_sent + send_len);
1239             if (s->body_sent < s->body_len) {
1240               LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
1241                 s->body_sent, s->body_len));
1242             }
1243           }
1244         }
1245       }
1246     }
1247     if (s->body_sent == s->body_len) {
1248       /* the whole body has been written, write last line */
1249       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
1250         s->body_len));
1251       err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
1252       if (err == ERR_OK) {
1253         s->timer = SMTP_TIMEOUT_DATATERM;
1254         LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
1255           smtp_state_str[SMTP_QUIT]));
1256         /* last line written, change state, wait for confirmation */
1257         s->state = SMTP_QUIT;
1258       }
1259     }
1260   }
1261 }
1262 
1263 /** State machine-like implementation of an SMTP client.
1264  */
1265 static void
smtp_process(void * arg,struct altcp_pcb * pcb,struct pbuf * p)1266 smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
1267 {
1268   struct smtp_session* s = (struct smtp_session*)arg;
1269   u16_t response_code = 0;
1270   u16_t tx_buf_len = 0;
1271   enum smtp_session_state next_state;
1272 
1273   if (arg == NULL) {
1274     /* already closed SMTP connection */
1275     if (p != NULL) {
1276       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
1277         p->tot_len, smtp_pbuf_str(p)));
1278       pbuf_free(p);
1279     }
1280     return;
1281   }
1282 
1283   next_state = s->state;
1284 
1285   if (p != NULL) {
1286     /* received data */
1287     if (s->p == NULL) {
1288       s->p = p;
1289     } else {
1290       pbuf_cat(s->p, p);
1291     }
1292   } else {
1293     /* idle timer, close connection if timed out */
1294     if (s->timer == 0) {
1295       LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
1296       smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
1297       return;
1298     }
1299     if (s->state == SMTP_BODY) {
1300       smtp_send_body(s, pcb);
1301       return;
1302     }
1303   }
1304   response_code = smtp_is_response(s);
1305   if (response_code) {
1306     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
1307     if (smtp_is_response_finished(s) != ERR_OK) {
1308       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
1309       /* wait for next packet to complete the respone */
1310       return;
1311     }
1312   } else {
1313     if (s->p != NULL) {
1314       LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
1315         smtp_pbuf_str(s->p)));
1316       pbuf_free(s->p);
1317       s->p = NULL;
1318     }
1319     return;
1320   }
1321 
1322   switch(s->state)
1323   {
1324   case(SMTP_NULL):
1325     /* wait for 220 */
1326     if (response_code == 220) {
1327       /* then send EHLO */
1328       next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
1329     }
1330     break;
1331   case(SMTP_HELO):
1332     /* wait for 250 */
1333     if (response_code == 250) {
1334 #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
1335       /* then send AUTH or MAIL */
1336       next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
1337     }
1338     break;
1339   case(SMTP_AUTH_LOGIN):
1340   case(SMTP_AUTH_PLAIN):
1341     /* wait for 235 */
1342     if (response_code == 235) {
1343 #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
1344       /* send MAIL */
1345       next_state = smtp_prepare_mail(s, &tx_buf_len);
1346     }
1347     break;
1348 #if SMTP_SUPPORT_AUTH_LOGIN
1349   case(SMTP_AUTH_LOGIN_UNAME):
1350     /* wait for 334 Username */
1351     if (response_code == 334) {
1352       if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
1353         /* send username */
1354         next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
1355       }
1356     }
1357     break;
1358   case(SMTP_AUTH_LOGIN_PASS):
1359     /* wait for 334 Password */
1360     if (response_code == 334) {
1361       if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
1362         /* send username */
1363         next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
1364       }
1365     }
1366     break;
1367 #endif /* SMTP_SUPPORT_AUTH_LOGIN */
1368   case(SMTP_MAIL):
1369     /* wait for 250 */
1370     if (response_code == 250) {
1371       /* send RCPT */
1372       next_state = smtp_prepare_rcpt(s, &tx_buf_len);
1373     }
1374     break;
1375   case(SMTP_RCPT):
1376     /* wait for 250 */
1377     if (response_code == 250) {
1378       /* send DATA */
1379       SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
1380       tx_buf_len = SMTP_CMD_DATA_LEN;
1381       next_state = SMTP_DATA;
1382     }
1383     break;
1384   case(SMTP_DATA):
1385     /* wait for 354 */
1386     if (response_code == 354) {
1387       /* send email header */
1388       next_state = smtp_prepare_header(s, &tx_buf_len);
1389     }
1390     break;
1391   case(SMTP_BODY):
1392     /* nothing to be done here, handled somewhere else */
1393     break;
1394   case(SMTP_QUIT):
1395     /* wait for 250 */
1396     if (response_code == 250) {
1397       /* send QUIT */
1398       next_state = smtp_prepare_quit(s, &tx_buf_len);
1399     }
1400     break;
1401   case(SMTP_CLOSED):
1402     /* nothing to do, wait for connection closed from server */
1403     return;
1404   default:
1405     LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
1406       smtp_state_str[s->state]));
1407     break;
1408   }
1409   if (s->state == next_state) {
1410     LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
1411       smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1412     /* close connection */
1413     smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
1414     return;
1415   }
1416   if (tx_buf_len > 0) {
1417     SMTP_TX_BUF_MAX(tx_buf_len);
1418     if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
1419       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
1420         smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
1421       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
1422         smtp_state_str[s->state], tx_buf_len, s->tx_buf));
1423       s->timer = SMTP_TIMEOUT;
1424       pbuf_free(s->p);
1425       s->p = NULL;
1426       LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
1427         smtp_state_str[s->state], smtp_state_str[next_state]));
1428       s->state = next_state;
1429       if (next_state == SMTP_BODY) {
1430         /* try to stream-send body data right now */
1431         smtp_send_body(s, pcb);
1432       } else if (next_state == SMTP_CLOSED) {
1433         /* sent out all data, delete structure */
1434         altcp_arg(pcb, NULL);
1435         smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
1436       }
1437     }
1438   }
1439 }
1440 
1441 #if SMTP_BODYDH
1442 /** Elementary sub-function to send data
1443  *
1444  * @returns: BDHALLDATASENT all data has been written
1445  *           BDHSOMEDATASENT some data has been written
1446  *           0 no data has been written
1447  */
1448 static int
smtp_send_bodyh_data(struct altcp_pcb * pcb,const char ** from,u16_t * howmany)1449 smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
1450 {
1451   err_t err;
1452   u16_t len = *howmany;
1453 
1454   len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
1455   err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
1456   if (err == ERR_OK) {
1457     *from += len;
1458     if ((*howmany -= len) > 0) {
1459       return BDHSOMEDATASENT;
1460     }
1461     return BDHALLDATASENT;
1462   }
1463   return 0;
1464 }
1465 
1466 /** Same as smtp_send_mail_static, but uses a callback function to send body data
1467  */
1468 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)1469 smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
1470   smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
1471 {
1472   struct smtp_session* s;
1473   size_t len;
1474 
1475   LWIP_ASSERT_CORE_LOCKED();
1476 
1477   s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
1478   if (s == NULL) {
1479     return ERR_MEM;
1480   }
1481   memset(s, 0, sizeof(struct smtp_session));
1482   s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
1483   if (s->bodydh == NULL) {
1484     SMTP_STATE_FREE(s);
1485     return ERR_MEM;
1486   }
1487   memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state));
1488   /* initialize the structure */
1489   s->from = from;
1490   len = strlen(from);
1491   LWIP_ASSERT("string is too long", len <= 0xffff);
1492   s->from_len = (u16_t)len;
1493   s->to = to;
1494   len = strlen(to);
1495   LWIP_ASSERT("string is too long", len <= 0xffff);
1496   s->to_len = (u16_t)len;
1497   s->subject = subject;
1498   len = strlen(subject);
1499   LWIP_ASSERT("string is too long", len <= 0xffff);
1500   s->subject_len = (u16_t)len;
1501   s->body = NULL;
1502   LWIP_ASSERT("string is too long", len <= 0xffff);
1503   s->callback_fn = callback_fn;
1504   s->callback_arg = callback_arg;
1505   s->bodydh->callback_fn = bodycback_fn;
1506   s->bodydh->state = BDH_SENDING;
1507   /* call the actual implementation of this function */
1508   return smtp_send_mail_alloced(s);
1509 }
1510 
1511 static void
smtp_send_body_data_handler(struct smtp_session * s,struct altcp_pcb * pcb)1512 smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
1513 {
1514   struct smtp_bodydh_state *bdh;
1515   int res = 0, ret;
1516   LWIP_ASSERT("s != NULL", s != NULL);
1517   bdh = s->bodydh;
1518   LWIP_ASSERT("bodydh != NULL", bdh != NULL);
1519 
1520   /* resume any leftovers from prior memory constraints */
1521   if (s->body_len) {
1522     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
1523     if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
1524         != BDHALLDATASENT) {
1525       s->body_sent = s->body_len - 1;
1526       return;
1527     }
1528   }
1529   ret = res;
1530   /* all data on buffer has been queued, resume execution */
1531   if (bdh->state == BDH_SENDING) {
1532     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
1533     do {
1534       ret |= res; /* remember if we once queued something to send */
1535       bdh->exposed.length = 0;
1536       if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
1537         bdh->state = BDH_STOP;
1538       }
1539       s->body = bdh->exposed.buffer;
1540       s->body_len = bdh->exposed.length;
1541       LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
1542     } while (s->body_len &&
1543             ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
1544             && (bdh->state != BDH_STOP));
1545   }
1546   if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
1547     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
1548     s->body_sent = s->body_len;
1549   } else {
1550     LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
1551     s->body_sent = s->body_len - 1;
1552   }
1553 }
1554 #endif /* SMTP_BODYDH */
1555 
1556 #endif /* LWIP_TCP && LWIP_CALLBACK_API */
1557