/** Free http client state and deallocate all resources within */ static err_t httpc_free_state(httpc_state_t* req) { struct altcp_pcb* tpcb; if (req->request != NULL) { pbuf_free(req->request); req->request = NULL; } if (req->rx_hdrs != NULL) { pbuf_free(req->rx_hdrs); req->rx_hdrs = NULL; } tpcb = req->pcb; mem_free(req); req = NULL; if (tpcb != NULL) { err_t r; altcp_arg(tpcb, NULL); altcp_recv(tpcb, NULL); altcp_err(tpcb, NULL); altcp_poll(tpcb, NULL, 0); altcp_sent(tpcb, NULL); r = altcp_close(tpcb); if (r != ERR_OK) { altcp_abort(tpcb); return ERR_ABRT; } } return ERR_OK; }
static void altcp_mbedtls_remove_callbacks(struct altcp_pcb *inner_conn) { altcp_arg(inner_conn, NULL); altcp_recv(inner_conn, NULL); altcp_sent(inner_conn, NULL); altcp_err(inner_conn, NULL); altcp_poll(inner_conn, NULL, inner_conn->pollinterval); }
static void altcp_mbedtls_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn) { altcp_arg(inner_conn, conn); altcp_recv(inner_conn, altcp_mbedtls_lower_recv); altcp_sent(inner_conn, altcp_mbedtls_lower_sent); altcp_err(inner_conn, altcp_mbedtls_lower_err); /* tcp_poll is set when interval is set by application */ /* listen is set totally different :-) */ }
/** Try to close a pcb and free the arg if successful */ static void smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result, u16_t srv_err, err_t err) { if (pcb != NULL) { altcp_arg(pcb, NULL); if (altcp_close(pcb) == ERR_OK) { if (s != NULL) { smtp_free(s, result, srv_err, err); } } else { /* close failed, set back arg */ altcp_arg(pcb, s); } } else { if (s != NULL) { smtp_free(s, result, srv_err, err); } } }
static struct altcp_pcb* smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip) { struct altcp_pcb* pcb; LWIP_UNUSED_ARG(remote_ip); #if LWIP_ALTCP && LWIP_ALTCP_TLS if (smtp_server_tls_config) { pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip)); } else #endif { pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip)); } if (pcb != NULL) { altcp_arg(pcb, s); altcp_recv(pcb, smtp_tcp_recv); altcp_err(pcb, smtp_tcp_err); altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL); altcp_sent(pcb, smtp_tcp_sent); } return pcb; }
/** The actual mail-sending function, called by smtp_send_mail and * smtp_send_mail_static after setting up the struct smtp_session. */ static err_t smtp_send_mail_alloced(struct smtp_session *s) { err_t err; struct altcp_pcb* pcb = NULL; ip_addr_t addr; LWIP_ASSERT("no smtp_session supplied", s != NULL); #if SMTP_CHECK_DATA /* check that body conforms to RFC: * - convert all single-CR or -LF in body to CRLF * - only 7-bit ASCII is allowed */ if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) { err = ERR_ARG; goto leave; } if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) { err = ERR_ARG; goto leave; } if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) { err = ERR_ARG; goto leave; } #if SMTP_BODYDH if (s->bodydh == NULL) #endif /* SMTP_BODYDH */ { if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) { err = ERR_ARG; goto leave; } } #endif /* SMTP_CHECK_DATA */ #if SMTP_COPY_AUTHDATA /* copy auth data, ensuring the first byte is always zero */ MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1); s->auth_plain_len = smtp_auth_plain_len; /* default username and pass is empty string */ s->username = s->auth_plain; s->pass = s->auth_plain; if (smtp_username != NULL) { s->username += smtp_username - smtp_auth_plain; } if (smtp_pass != NULL) { s->pass += smtp_pass - smtp_auth_plain; } #endif /* SMTP_COPY_AUTHDATA */ s->state = SMTP_NULL; s->timer = SMTP_TIMEOUT; #if LWIP_DNS err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s); #else /* LWIP_DNS */ err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG; #endif /* LWIP_DNS */ if (err == ERR_OK) { pcb = smtp_setup_pcb(s, &addr); if (pcb == NULL) { err = ERR_MEM; goto leave; } err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected); if (err != ERR_OK) { LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err)); goto deallocate_and_leave; } } else if (err != ERR_INPROGRESS) { LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err)); goto deallocate_and_leave; } return ERR_OK; deallocate_and_leave: if (pcb != NULL) { altcp_arg(pcb, NULL); altcp_close(pcb); } leave: smtp_free_struct(s); /* no need to call the callback here since we return != ERR_OK */ return err; }
/** State machine-like implementation of an SMTP client. */ static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p) { struct smtp_session* s = (struct smtp_session*)arg; u16_t response_code = 0; u16_t tx_buf_len = 0; enum smtp_session_state next_state; if (arg == NULL) { /* already closed SMTP connection */ if (p != NULL) { LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n", p->tot_len, smtp_pbuf_str(p))); pbuf_free(p); } return; } next_state = s->state; if (p != NULL) { /* received data */ if (s->p == NULL) { s->p = p; } else { pbuf_cat(s->p, p); } } else { /* idle timer, close connection if timed out */ if (s->timer == 0) { LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n")); smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT); return; } if (s->state == SMTP_BODY) { smtp_send_body(s, pcb); return; } } response_code = smtp_is_response(s); if (response_code) { LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code)); if (smtp_is_response_finished(s) != ERR_OK) { LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code)); /* wait for next packet to complete the respone */ return; } } else { if (s->p != NULL) { LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n", smtp_pbuf_str(s->p))); pbuf_free(s->p); s->p = NULL; } return; } switch(s->state) { case(SMTP_NULL): /* wait for 220 */ if (response_code == 220) { /* then send EHLO */ next_state = smtp_prepare_helo(s, &tx_buf_len, pcb); } break; case(SMTP_HELO): /* wait for 250 */ if (response_code == 250) { #if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN /* then send AUTH or MAIL */ next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len); } break; case(SMTP_AUTH_LOGIN): case(SMTP_AUTH_PLAIN): /* wait for 235 */ if (response_code == 235) { #endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */ /* send MAIL */ next_state = smtp_prepare_mail(s, &tx_buf_len); } break; #if SMTP_SUPPORT_AUTH_LOGIN case(SMTP_AUTH_LOGIN_UNAME): /* wait for 334 Username */ if (response_code == 334) { if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) { /* send username */ next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len); } } break; case(SMTP_AUTH_LOGIN_PASS): /* wait for 334 Password */ if (response_code == 334) { if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) { /* send username */ next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len); } } break; #endif /* SMTP_SUPPORT_AUTH_LOGIN */ case(SMTP_MAIL): /* wait for 250 */ if (response_code == 250) { /* send RCPT */ next_state = smtp_prepare_rcpt(s, &tx_buf_len); } break; case(SMTP_RCPT): /* wait for 250 */ if (response_code == 250) { /* send DATA */ SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN); tx_buf_len = SMTP_CMD_DATA_LEN; next_state = SMTP_DATA; } break; case(SMTP_DATA): /* wait for 354 */ if (response_code == 354) { /* send email header */ next_state = smtp_prepare_header(s, &tx_buf_len); } break; case(SMTP_BODY): /* nothing to be done here, handled somewhere else */ break; case(SMTP_QUIT): /* wait for 250 */ if (response_code == 250) { /* send QUIT */ next_state = smtp_prepare_quit(s, &tx_buf_len); } break; case(SMTP_CLOSED): /* nothing to do, wait for connection closed from server */ return; default: LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state, smtp_state_str[s->state])); break; } if (s->state == next_state) { LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n", smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); /* close connection */ smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK); return; } if (tx_buf_len > 0) { SMTP_TX_BUF_MAX(tx_buf_len); if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) { LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n", smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p))); LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n", smtp_state_str[s->state], tx_buf_len, s->tx_buf)); s->timer = SMTP_TIMEOUT; pbuf_free(s->p); s->p = NULL; LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n", smtp_state_str[s->state], smtp_state_str[next_state])); s->state = next_state; if (next_state == SMTP_BODY) { /* try to stream-send body data right now */ smtp_send_body(s, pcb); } else if (next_state == SMTP_CLOSED) { /* sent out all data, delete structure */ altcp_arg(pcb, NULL); smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK); } } } }
/** Initialize the connection struct */ static err_t httpc_init_connection_common(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name, u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg, int use_host) { size_t alloc_len; mem_size_t mem_alloc_len; int req_len, req_len2; httpc_state_t *req; #if HTTPC_DEBUG_REQUEST size_t server_name_len, uri_len; #endif LWIP_ASSERT("uri != NULL", uri != NULL); /* get request len */ req_len = httpc_create_request_string(settings, server_name, server_port, uri, use_host, NULL, 0); if ((req_len < 0) || (req_len > 0xFFFF)) { return ERR_VAL; } /* alloc state and request in one block */ alloc_len = sizeof(httpc_state_t); #if HTTPC_DEBUG_REQUEST server_name_len = server_name ? strlen(server_name) : 0; uri_len = strlen(uri); alloc_len += server_name_len + 1 + uri_len + 1; #endif mem_alloc_len = (mem_size_t)alloc_len; if ((mem_alloc_len < alloc_len) || (req_len + 1 > 0xFFFF)) { return ERR_VAL; } req = (httpc_state_t*)mem_malloc((mem_size_t)alloc_len); if(req == NULL) { return ERR_MEM; } memset(req, 0, sizeof(httpc_state_t)); req->timeout_ticks = HTTPC_POLL_TIMEOUT; req->request = pbuf_alloc(PBUF_RAW, (u16_t)(req_len + 1), PBUF_RAM); if (req->request == NULL) { httpc_free_state(req); return ERR_MEM; } if (req->request->next != NULL) { /* need a pbuf in one piece */ httpc_free_state(req); return ERR_MEM; } req->hdr_content_len = HTTPC_CONTENT_LEN_INVALID; #if HTTPC_DEBUG_REQUEST req->server_name = (char*)(req + 1); if (server_name) { memcpy(req->server_name, server_name, server_name_len + 1); } req->uri = req->server_name + server_name_len + 1; memcpy(req->uri, uri, uri_len + 1); #endif req->pcb = altcp_new(settings->altcp_allocator); if(req->pcb == NULL) { httpc_free_state(req); return ERR_MEM; } req->remote_port = settings->use_proxy ? settings->proxy_port : server_port; altcp_arg(req->pcb, req); altcp_recv(req->pcb, httpc_tcp_recv); altcp_err(req->pcb, httpc_tcp_err); altcp_poll(req->pcb, httpc_tcp_poll, HTTPC_POLL_INTERVAL); altcp_sent(req->pcb, httpc_tcp_sent); /* set up request buffer */ req_len2 = httpc_create_request_string(settings, server_name, server_port, uri, use_host, (char *)req->request->payload, req_len + 1); if (req_len2 != req_len) { httpc_free_state(req); return ERR_VAL; } req->recv_fn = recv_fn; req->conn_settings = settings; req->callback_arg = callback_arg; *connection = req; return ERR_OK; }