smtp.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * libwebsockets - small server side websockets and web server implementation
  3. *
  4. * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to
  8. * deal in the Software without restriction, including without limitation the
  9. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  22. * IN THE SOFTWARE.
  23. */
  24. #include "private-lib-core.h"
  25. #include "private-lib-abstract.h"
  26. /** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */
  27. typedef enum lwsgs_smtp_states {
  28. LGSSMTP_IDLE, /**< awaiting new email */
  29. LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */
  30. LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */
  31. /* (server sends greeting) */
  32. LGSSMTP_SENT_HELO, /**< sent the HELO */
  33. LGSSMTP_SENT_FROM, /**< sent FROM */
  34. LGSSMTP_SENT_TO, /**< sent TO */
  35. LGSSMTP_SENT_DATA, /**< sent DATA request */
  36. LGSSMTP_SENT_BODY, /**< sent the email body */
  37. /*
  38. * (server sends, eg, "250 Ok: queued as 12345")
  39. * at this point we can return to LGSSMTP_SENT_HELO and send a
  40. * new email, or continue below to QUIT, or just wait
  41. */
  42. LGSSMTP_SENT_QUIT, /**< sent the session quit */
  43. /* (server sends, eg, "221 Bye" and closes the connection) */
  44. } lwsgs_smtp_states_t;
  45. /** abstract protocol instance data */
  46. typedef struct lws_smtp_client_protocol {
  47. const struct lws_abs *abs;
  48. lwsgs_smtp_states_t estate;
  49. lws_smtp_email_t *e; /* the email we are trying to send */
  50. const char *helo;
  51. unsigned char send_pending:1;
  52. } lws_smtpcp_t;
  53. static const short retcodes[] = {
  54. 0, /* idle */
  55. 0, /* connecting */
  56. 220, /* connected */
  57. 250, /* helo */
  58. 250, /* from */
  59. 250, /* to */
  60. 354, /* data */
  61. 250, /* body */
  62. 221, /* quit */
  63. };
  64. static void
  65. lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s)
  66. {
  67. lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
  68. c->estate = s;
  69. }
  70. static lws_smtp_email_t *
  71. lws_smtpc_get_email(lws_smtpcp_t *c)
  72. {
  73. const lws_token_map_t *tm;
  74. /* ... the email we want to send */
  75. tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T);
  76. if (!tm) {
  77. assert(0);
  78. return NULL;
  79. }
  80. return (lws_smtp_email_t *)tm->u.value;
  81. }
  82. /*
  83. * Called when something happened so that we know now the final disposition of
  84. * the email send attempt, for good or ill.
  85. *
  86. * Inform the owner via the done callback and set up the next queued one if any.
  87. *
  88. * Returns nonzero if we queued a new one
  89. */
  90. static int
  91. lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf,
  92. size_t len)
  93. {
  94. lws_smtpcp_t *ch;
  95. lws_abs_t *ach;
  96. lws_dll2_t *d;
  97. lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
  98. /* lifetime of the email object is handled by done callback */
  99. c->e->done(c->e, c->e->data, disp, buf, len);
  100. c->e = NULL;
  101. /* this may not be the time to try to send anything else... */
  102. if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY)
  103. return 0;
  104. /* ... otherwise... do we have another queued? */
  105. d = lws_dll2_get_tail(&c->abs->children_owner);
  106. if (!d)
  107. return 0;
  108. ach = lws_container_of(d, lws_abs_t, bound);
  109. ch = (lws_smtpcp_t *)ach->api;
  110. c->e = lws_smtpc_get_email(ch);
  111. /* since we took it on, remove it from the queue */
  112. lws_dll2_remove(d);
  113. return 1;
  114. }
  115. /*
  116. * we became connected
  117. */
  118. static int
  119. lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api)
  120. {
  121. lws_smtpcp_t *c = (lws_smtpcp_t *)api;
  122. /* we have become connected in the tcp sense */
  123. lws_smtpc_state_transition(c, LGSSMTP_CONNECTED);
  124. /*
  125. * From the accept(), the next thing that should happen is the SMTP
  126. * server sends its greeting like "220 smtp2.example.com ESMTP Postfix",
  127. * we'll hear about it in the rx callback, or time out
  128. */
  129. c->abs->at->set_timeout(c->abs->ati,
  130. PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3);
  131. return 0;
  132. }
  133. static int
  134. lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len)
  135. {
  136. lws_smtpcp_t *c = (lws_smtpcp_t *)api;
  137. char dotstar[96], at[5];
  138. int n;
  139. c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0);
  140. lws_strncpy(at, (const char *)buf, sizeof(at));
  141. n = atoi(at);
  142. switch (c->estate) {
  143. case LGSSMTP_CONNECTED:
  144. if (n != 220) {
  145. /*
  146. * The server did not properly greet us... we can't
  147. * even get started, so fail the transport connection
  148. * (and anything queued on it)
  149. */
  150. lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar));
  151. lwsl_err("%s: server: %s\n", __func__, dotstar);
  152. return 1;
  153. }
  154. break;
  155. case LGSSMTP_SENT_BODY:
  156. /*
  157. * We finished one way or another... let's prepare to send a
  158. * new one... or wait until server hangs up on us
  159. */
  160. if (!lws_smtpc_email_disposition(c,
  161. n == 250 ? LWS_SMTP_DISPOSITION_SENT :
  162. LWS_SMTP_DISPOSITION_FAILED,
  163. "destroyed", 0))
  164. return 0; /* become idle */
  165. break; /* ask to send */
  166. case LGSSMTP_SENT_QUIT:
  167. lwsl_debug("%s: done\n", __func__);
  168. lws_smtpc_state_transition(c, LGSSMTP_IDLE);
  169. return 1;
  170. default:
  171. if (n != retcodes[c->estate]) {
  172. lws_strnncpy(dotstar, buf, len, sizeof(dotstar));
  173. lwsl_notice("%s: bad response: %d (state %d) %s\n",
  174. __func__, n, c->estate, dotstar);
  175. lws_smtpc_email_disposition(c,
  176. LWS_SMTP_DISPOSITION_FAILED, buf, len);
  177. return 0;
  178. }
  179. break;
  180. }
  181. c->send_pending = 1;
  182. c->abs->at->ask_for_writeable(c->abs->ati);
  183. return 0;
  184. }
  185. static int
  186. lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
  187. {
  188. char b[256 + LWS_PRE], *p = b + LWS_PRE;
  189. lws_smtpcp_t *c = (lws_smtpcp_t *)api;
  190. int n;
  191. if (!c->send_pending || !c->e)
  192. return 0;
  193. c->send_pending = 0;
  194. lwsl_debug("%s: writing response for state %d\n", __func__, c->estate);
  195. switch (c->estate) {
  196. case LGSSMTP_CONNECTED:
  197. n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo);
  198. lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
  199. break;
  200. case LGSSMTP_SENT_HELO:
  201. n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n",
  202. c->e->from);
  203. lws_smtpc_state_transition(c, LGSSMTP_SENT_FROM);
  204. break;
  205. case LGSSMTP_SENT_FROM:
  206. n = lws_snprintf(p, sizeof(b) - LWS_PRE,
  207. "RCPT TO: <%s>\n", c->e->to);
  208. lws_smtpc_state_transition(c, LGSSMTP_SENT_TO);
  209. break;
  210. case LGSSMTP_SENT_TO:
  211. n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n");
  212. lws_smtpc_state_transition(c, LGSSMTP_SENT_DATA);
  213. break;
  214. case LGSSMTP_SENT_DATA:
  215. p = (char *)&c->e[1];
  216. n = strlen(p);
  217. lws_smtpc_state_transition(c, LGSSMTP_SENT_BODY);
  218. break;
  219. case LGSSMTP_SENT_BODY:
  220. n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n");
  221. lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT);
  222. break;
  223. case LGSSMTP_SENT_QUIT:
  224. return 0;
  225. default:
  226. return 0;
  227. }
  228. //puts(p);
  229. c->abs->at->tx(c->abs->ati, (uint8_t *)p, n);
  230. return 0;
  231. }
  232. static int
  233. lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api)
  234. {
  235. lws_smtpcp_t *c = (lws_smtpcp_t *)api;
  236. if (c)
  237. lws_smtpc_state_transition(c, LGSSMTP_IDLE);
  238. return 0;
  239. }
  240. /*
  241. * Creating for initial transport and for piggybacking on another transport
  242. * both get created here the same. But piggybackers have ai->bound attached.
  243. */
  244. static int
  245. lws_smtpc_create(const lws_abs_t *ai)
  246. {
  247. lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api;
  248. memset(c, 0, sizeof(*c));
  249. c->abs = ai;
  250. c->e = lws_smtpc_get_email(c);
  251. lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ?
  252. LGSSMTP_CONNECTING : LGSSMTP_IDLE);
  253. /* If we are initiating the transport, we will get an accept() next...
  254. *
  255. * If we are piggybacking, the parent will get a .child_bind() after
  256. * this to give it a chance to act on us joining (eg, it was completely
  257. * idle and we joined).
  258. */
  259. return 0;
  260. }
  261. static void
  262. lws_smtpc_destroy(lws_abs_protocol_inst_t **_c)
  263. {
  264. lws_smtpcp_t *c = (lws_smtpcp_t *)*_c;
  265. if (!c)
  266. return;
  267. /* so if we are still holding on to c->e, we have failed to send it */
  268. if (c->e)
  269. lws_smtpc_email_disposition(c,
  270. LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0);
  271. *_c = NULL;
  272. }
  273. static int
  274. lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2)
  275. {
  276. return 0;
  277. }
  278. static int
  279. lws_smtpc_child_bind(lws_abs_t *abs)
  280. {
  281. return 0;
  282. }
  283. /* events the transport invokes (handled by abstract protocol) */
  284. const lws_abs_protocol_t lws_abs_protocol_smtp = {
  285. .name = "smtp",
  286. .alloc = sizeof(lws_smtpcp_t),
  287. .flags = LWSABSPR_FLAG_PIPELINE,
  288. .create = lws_smtpc_create,
  289. .destroy = lws_smtpc_destroy,
  290. .compare = lws_smtpc_compare,
  291. .accept = lws_smtpc_abs_accept,
  292. .rx = lws_smtpc_abs_rx,
  293. .writeable = lws_smtpc_abs_writeable,
  294. .closed = lws_smtpc_abs_closed,
  295. .heartbeat = NULL,
  296. .child_bind = lws_smtpc_child_bind,
  297. };