/** Allow caller of altcp_write() to limit to negotiated chunk size * or remaining sndbuf space of inner_conn. */ static u16_t altcp_mbedtls_sndbuf(struct altcp_pcb *conn) { if (conn) { altcp_mbedtls_state_t *state; state = (altcp_mbedtls_state_t*)conn->state; if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) { return 0; } if (conn->inner_conn) { u16_t sndbuf = altcp_sndbuf(conn->inner_conn); /* Take care of record header, IV, AuthTag */ int ssl_expan = mbedtls_ssl_get_record_expansion(&state->ssl_context); if (ssl_expan > 0) { size_t ssl_added = (u16_t)LWIP_MIN(ssl_expan, 0xFFFF); /* internal sndbuf smaller than our offset */ if (ssl_added < sndbuf) { size_t max_len = 0xFFFF; size_t ret; #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) /* @todo: adjust ssl_added to real value related to negociated cipher */ size_t max_frag_len = mbedtls_ssl_get_max_frag_len(&state->ssl_context); max_len = LWIP_MIN(max_frag_len, max_len); #endif /* Adjust sndbuf of inner_conn with what added by SSL */ ret = LWIP_MIN(sndbuf - ssl_added, max_len); LWIP_ASSERT("sndbuf overflow", ret <= 0xFFFF); return (u16_t)ret; } } } } /* fallback: use sendbuf of the inner connection */ return altcp_default_sndbuf(conn); }
u16_t altcp_default_sndbuf(struct altcp_pcb *conn) { if (conn && conn->inner_conn) { return altcp_sndbuf(conn->inner_conn); } return 0; }
/** * 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))); } }
/** 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; } } } }
/** 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; }