/** Wait for all headers to be received, return its length and content-length (if available) */ static err_t http_wait_headers(struct pbuf *p, u32_t *content_length, u16_t *total_header_len) { u16_t end1 = pbuf_memfind(p, "\r\n\r\n", 4, 0); if (end1 < (0xFFFF - 2)) { /* all headers received */ /* check if we have a content length (@todo: case insensitive?) */ u16_t content_len_hdr; *content_length = HTTPC_CONTENT_LEN_INVALID; *total_header_len = end1 + 4; content_len_hdr = pbuf_memfind(p, "Content-Length: ", 16, 0); if (content_len_hdr != 0xFFFF) { u16_t content_len_line_end = pbuf_memfind(p, "\r\n", 2, content_len_hdr); if (content_len_line_end != 0xFFFF) { char content_len_num[16]; u16_t content_len_num_len = (u16_t)(content_len_line_end - content_len_hdr - 16); memset(content_len_num, 0, sizeof(content_len_num)); if (pbuf_copy_partial(p, content_len_num, content_len_num_len, content_len_hdr + 16) == content_len_num_len) { int len = atoi(content_len_num); if ((len >= 0) && ((u32_t)len < HTTPC_CONTENT_LEN_INVALID)) { *content_length = (u32_t)len; } } } } return ERR_OK; } return ERR_VAL; }
/** Parse pbuf to see if it contains a fully received answer. * If one is found, ERR_OK is returned. * If none is found, ERR_VAL is returned. * * A fully received answer is a 3-digit number followed by a space, * some string and a CRLF as line ending. * * @param s smtp session struct */ static err_t smtp_is_response_finished(struct smtp_session *s) { u8_t sp; u16_t crlf; u16_t offset; if (s->p == NULL) { return ERR_VAL; } offset = 0; again: /* We could check the response number here, but we trust the * protocol definition which says the client can rely on it being * the same on every line. */ /* find CRLF */ crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, offset + 4); if (crlf == 0xFFFF) { /* no CRLF found */ return ERR_VAL; } sp = pbuf_get_at(s->p, offset + 3); if (sp == '-') { /* no space after response code -> try next line */ offset = crlf + 2; goto again; } else if (sp == ' ') { /* CRLF found after response code + space -> valid response */ return ERR_OK; } /* sp contains invalid character */ return ERR_VAL; }
/** Parse http header response line 1 */ static err_t http_parse_response_status(struct pbuf *p, u16_t *http_version, u16_t *http_status, u16_t *http_status_str_offset) { u16_t end1 = pbuf_memfind(p, "\r\n", 2, 0); if (end1 != 0xFFFF) { /* get parts of first line */ u16_t space1, space2; space1 = pbuf_memfind(p, " ", 1, 0); if (space1 != 0xFFFF) { if ((pbuf_memcmp(p, 0, "HTTP/", 5) == 0) && (pbuf_get_at(p, 6) == '.')) { char status_num[10]; size_t status_num_len; /* parse http version */ u16_t version = pbuf_get_at(p, 5) - '0'; version <<= 8; version |= pbuf_get_at(p, 7) - '0'; *http_version = version; /* parse http status number */ space2 = pbuf_memfind(p, " ", 1, space1 + 1); if (space2 != 0xFFFF) { *http_status_str_offset = space2 + 1; status_num_len = space2 - space1 - 1; } else { status_num_len = end1 - space1 - 1; } memset(status_num, 0, sizeof(status_num)); if (pbuf_copy_partial(p, status_num, (u16_t)status_num_len, space1 + 1) == status_num_len) { int status = atoi(status_num); if ((status > 0) && (status <= 0xFFFF)) { *http_status = (u16_t)status; return ERR_OK; } } } } } return ERR_VAL; }
/** Parse last server response (in rx_buf) for supported authentication method, * create data to send out (to tx_buf), set tx_data_len correctly * and return the next state. */ static enum smtp_session_state smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len) { /* check response for supported authentication method */ u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP); if (auth == 0xFFFF) { auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ); } if (auth != 0xFFFF) { u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth); if ((crlf != 0xFFFF) && (crlf > auth)) { /* use tx_buf temporarily */ u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, crlf - auth, auth); if (copied != 0) { char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN; s->tx_buf[copied] = 0; #if SMTP_SUPPORT_AUTH_PLAIN /* favour PLAIN over LOGIN since it involves less requests */ if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) { size_t auth_len; /* server supports AUTH PLAIN */ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN); /* add base64-encoded string "\0username\0password" */ auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN], SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s), SMTP_AUTH_PLAIN_LEN(s)); LWIP_ASSERT("string too long", auth_len <= 0xffff); *tx_buf_len = SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len; LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN); SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2, SMTP_CMD_AUTHPLAIN_2_LEN); return SMTP_AUTH_PLAIN; } else #endif /* SMTP_SUPPORT_AUTH_PLAIN */ { #if SMTP_SUPPORT_AUTH_LOGIN if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) { /* server supports AUTH LOGIN */ *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN; SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN); return SMTP_AUTH_LOGIN_UNAME; } #endif /* SMTP_SUPPORT_AUTH_LOGIN */ } } } } /* server didnt's send correct keywords for AUTH, try sending directly */ return smtp_prepare_mail(s, tx_buf_len); }
static void recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { u16_t *sbuf = (u16_t *) p->payload; int opcode; LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(upcb); if (((tftp_state.port != 0) && (port != tftp_state.port)) || (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); pbuf_free(p); return; } opcode = sbuf[0]; tftp_state.last_pkt = tftp_state.timer; tftp_state.retries = 0; switch (opcode) { case PP_HTONS(TFTP_RRQ): /* fall through */ case PP_HTONS(TFTP_WRQ): { const char tftp_null = 0; char filename[TFTP_MAX_FILENAME_LEN]; char mode[TFTP_MAX_MODE_LEN]; u16_t filename_end_offset; u16_t mode_end_offset; if(tftp_state.handle != NULL) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); break; } sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL); /* find \0 in pbuf -> end of filename string */ filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2); if((u16_t)(filename_end_offset-2) > sizeof(filename)) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated"); break; } pbuf_copy_partial(p, filename, filename_end_offset-2, 2); /* find \0 in pbuf -> end of mode string */ mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1); if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated"); break; } pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1); tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ)); tftp_state.blknum = 1; if (!tftp_state.handle) { send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file."); break; } LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read")); ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr); LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode)); ip_addr_copy(tftp_state.addr, *addr); tftp_state.port = port; if (opcode == PP_HTONS(TFTP_WRQ)) { tftp_state.mode_write = 1; send_ack(0); } else { tftp_state.mode_write = 0; send_data(); } break; } case PP_HTONS(TFTP_DATA): { int ret; u16_t blknum; if (tftp_state.handle == NULL) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); break; } if (tftp_state.mode_write != 1) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection"); break; } blknum = lwip_ntohs(sbuf[1]); pbuf_header(p, -TFTP_HEADER_LENGTH); ret = tftp_state.ctx->write(tftp_state.handle, p); if (ret < 0) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file"); close_handle(); } else { send_ack(blknum); } if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) { close_handle(); } break; } case PP_HTONS(TFTP_ACK): { u16_t blknum; int lastpkt; if (tftp_state.handle == NULL) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); break; } if (tftp_state.mode_write != 0) { send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection"); break; } blknum = lwip_ntohs(sbuf[1]); if (blknum != tftp_state.blknum) { send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number"); break; } lastpkt = 0; if (tftp_state.last_data != NULL) { lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH); } if (!lastpkt) { tftp_state.blknum++; send_data(); } else { close_handle(); } break; } default: send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation"); break; } pbuf_free(p); }