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