/** Send callback function called from mbedtls (set via mbedtls_ssl_set_bio) * This function is either called during handshake or when sending application * data via @ref altcp_mbedtls_write (or altcp_write) */ static int altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size) { struct altcp_pcb *conn = (struct altcp_pcb *) ctx; int written = 0; size_t size_left = size; u8_t apiflags = TCP_WRITE_FLAG_COPY; LWIP_ASSERT("conn != NULL", conn != NULL); if ((conn == NULL) || (conn->inner_conn == NULL)) { return MBEDTLS_ERR_NET_INVALID_CONTEXT; } while (size_left) { u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF); err_t err = altcp_write(conn->inner_conn, (const void *)dataptr, write_len, apiflags); if (err == ERR_OK) { written += write_len; size_left -= write_len; } else if (err == ERR_MEM) { if (written) { return written; } return 0; /* MBEDTLS_ERR_SSL_WANT_WRITE; */ } else { LWIP_ASSERT("tls_write, tcp_write: err != ERR MEM", 0); /* @todo: return MBEDTLS_ERR_NET_CONN_RESET or MBEDTLS_ERR_NET_SEND_FAILED */ return MBEDTLS_ERR_NET_SEND_FAILED; } } return written; }
/** If in state SMTP_BODY, try to send more body data */ static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb) { err_t err; if (s->state == SMTP_BODY) { #if SMTP_BODYDH if (s->bodydh) { smtp_send_body_data_handler(s, pcb); } else #endif /* SMTP_BODYDH */ { u16_t send_len = (u16_t)(s->body_len - s->body_sent); if (send_len > 0) { u16_t snd_buf = altcp_sndbuf(pcb); if (send_len > snd_buf) { send_len = snd_buf; } if (send_len > 0) { /* try to send something out */ err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY); if (err == ERR_OK) { s->timer = SMTP_TIMEOUT_DATABLOCK; s->body_sent = (u16_t)(s->body_sent + send_len); if (s->body_sent < s->body_len) { LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n", s->body_sent, s->body_len)); } } } } } if (s->body_sent == s->body_len) { /* the whole body has been written, write last line */ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n", s->body_len)); err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0); if (err == ERR_OK) { s->timer = SMTP_TIMEOUT_DATATERM; LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n", smtp_state_str[SMTP_QUIT])); /* last line written, change state, wait for confirmation */ s->state = SMTP_QUIT; } } } }
err_t altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags) { if (conn && conn->inner_conn) { return altcp_write(conn->inner_conn, dataptr, len, apiflags); } return ERR_VAL; }
/** * Try send as many bytes as possible from output ring buffer * @param rb Output ring buffer * @param tpcb TCP connection handle */ static void mqtt_output_send(struct mqtt_ringbuf_t *rb, struct altcp_pcb *tpcb) { err_t err; u8_t wrap = 0; u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb); u16_t send_len = altcp_sndbuf(tpcb); LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL); if (send_len == 0 || ringbuf_lin_len == 0) { return; } LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n", send_len, ringbuf_lin_len, rb->get, rb->put)); if (send_len > ringbuf_lin_len) { /* Space in TCP output buffer is larger than available in ring buffer linear portion */ send_len = ringbuf_lin_len; /* Wrap around if more data in ring buffer after linear portion */ wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len); } err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0)); if ((err == ERR_OK) && wrap) { mqtt_ringbuf_advance_get_idx(rb, send_len); /* Use the lesser one of ring buffer linear length and TCP send buffer size */ send_len = LWIP_MIN(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb)); err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY); } if (err == ERR_OK) { mqtt_ringbuf_advance_get_idx(rb, send_len); /* Flush */ altcp_output(tpcb); } else { LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); } }
/** Elementary sub-function to send data * * @returns: BDHALLDATASENT all data has been written * BDHSOMEDATASENT some data has been written * 0 no data has been written */ static int smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany) { err_t err; u16_t len = *howmany; len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb)); err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY); if (err == ERR_OK) { *from += len; if ((*howmany -= len) > 0) { return BDHSOMEDATASENT; } return BDHALLDATASENT; } return 0; }
/** http client tcp connected callback */ static err_t httpc_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err) { err_t r; httpc_state_t* req = (httpc_state_t*)arg; LWIP_UNUSED_ARG(pcb); LWIP_UNUSED_ARG(err); /* send request; last char is zero termination */ r = altcp_write(req->pcb, req->request->payload, req->request->len - 1, TCP_WRITE_FLAG_COPY); if (r != ERR_OK) { /* could not write the single small request -> fail, don't retry */ return httpc_close(req, HTTPC_RESULT_ERR_MEM, 0, r); } /* everything written, we can free the request */ pbuf_free(req->request); req->request = NULL; altcp_output(req->pcb); return ERR_OK; }
/** 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); } } } }