/* * if RTR_ERROR was returned a error PDU was sent, and the socket state changed * @param pdu_len must >= RTR_MAX_PDU_LEN Bytes * @return RTR_SUCCESS * @return RTR_ERROR, error pdu was sent and socket_state changed * @return TR_WOULDBLOCK * \post * If RTR_SUCCESS is returned PDU points to a well formed PDU that has * the appropriate size for the PDU type it pretend to be. Thus, casting it to * the PDU type struct and using it is save. Furthermore all PDU field are * in host-byte-order. */ static int rtr_receive_pdu(struct rtr_socket *rtr_socket, void *pdu, const size_t pdu_len, const time_t timeout) { int error = RTR_SUCCESS; assert(pdu_len >= RTR_MAX_PDU_LEN); if (rtr_socket->state == RTR_SHUTDOWN) return RTR_ERROR; //receive packet header error = tr_recv_all(rtr_socket->tr_socket, pdu, sizeof(struct pdu_header), timeout); if (error < 0) goto error; else error = RTR_SUCCESS; //header in hostbyte order, retain original received pdu, in case we need to detach it to an error pdu struct pdu_header header; memcpy(&header, pdu, sizeof(header)); rtr_pdu_header_to_host_byte_order(&header); //if header->len is < packet_header = corrupt data received if (header.len < sizeof(header)) { error = CORRUPT_DATA; goto error; } else if (header.len > RTR_MAX_PDU_LEN) { //PDU too big, > than MAX_PDU_LEN Bytes error = PDU_TOO_BIG; goto error; } //Handle live downgrading if (rtr_socket->has_received_pdus == false) { if (rtr_socket->version == RTR_PROTOCOL_VERSION_1 && header.ver == RTR_PROTOCOL_VERSION_0 && header.type != ERROR) { RTR_DBG("First received PDU is a version 0 PDU, downgrading to %u", RTR_PROTOCOL_VERSION_0); rtr_socket->version = RTR_PROTOCOL_VERSION_0; } rtr_socket->has_received_pdus = true; } //Handle wrong protocol version //If it is a error PDU, it will be handled by rtr_handle_error_pdu if (header.ver != rtr_socket->version && header.type != ERROR) { error = UNEXPECTED_PROTOCOL_VERSION; goto error; } //receive packet payload const unsigned int remaining_len = header.len - sizeof(header); if (remaining_len > 0) { if (rtr_socket->state == RTR_SHUTDOWN) return RTR_ERROR; error = tr_recv_all(rtr_socket->tr_socket, (((char *) pdu) + sizeof(header)), remaining_len, RTR_RECV_TIMEOUT); if (error < 0) goto error; else error = RTR_SUCCESS; } //copy header in host_byte_order to pdu memcpy(pdu, &header, sizeof(header)); //Check if the header len value is valid if (rtr_pdu_check_size(pdu) == false) { //TODO Restore byteorder for sending error PDU error = CORRUPT_DATA; goto error; } //At this point it is save to cast and use the PDU rtr_pdu_footer_to_host_byte_order(pdu); //Here we should handle error PDUs instead of doing it in //several other places... if (header.type == IPV4_PREFIX || header.type == IPV6_PREFIX) { if (((struct pdu_ipv4 *) pdu)->zero != 0) RTR_DBG1("Warning: Zero field of received Prefix PDU doesn't contain 0"); } if (header.type == ROUTER_KEY && ((struct pdu_router_key *) pdu)->zero != 0) RTR_DBG1("Warning: ROUTER_KEY_PDU zero field is != 0"); return RTR_SUCCESS; error: //send error msg to server, including unmodified pdu header(pdu variable instead header) if (error == -1) { rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT); return RTR_ERROR; } else if (error == TR_WOULDBLOCK) { RTR_DBG1("receive timeout expired"); return TR_WOULDBLOCK; } else if (error == TR_INTR) { RTR_DBG1("receive call interrupted"); return TR_INTR; } else if (error == CORRUPT_DATA) { RTR_DBG1("corrupt PDU received"); const char txt[] = "corrupt data received, length value in PDU is too small"; rtr_send_error_pdu(rtr_socket, pdu, sizeof(header), CORRUPT_DATA, txt, sizeof(txt)); } else if (error == PDU_TOO_BIG) { RTR_DBG1("PDU too big"); char txt[42]; snprintf(txt, sizeof(txt),"PDU too big, max. PDU size is: %u bytes", RTR_MAX_PDU_LEN); RTR_DBG("%s", txt); rtr_send_error_pdu(rtr_socket, pdu, sizeof(header), CORRUPT_DATA, txt, sizeof(txt)); } else if (error == UNSUPPORTED_PDU_TYPE) { RTR_DBG("Unsupported PDU type (%u) received", header.type); rtr_send_error_pdu(rtr_socket, pdu, sizeof(header), UNSUPPORTED_PDU_TYPE, NULL, 0); } else if (error == UNSUPPORTED_PROTOCOL_VER) { RTR_DBG("PDU with unsupported Protocol version (%u) received", header.ver); rtr_send_error_pdu(rtr_socket, pdu, sizeof(header), UNSUPPORTED_PROTOCOL_VER, NULL, 0); return RTR_ERROR; } else if (error == UNEXPECTED_PROTOCOL_VERSION) { RTR_DBG("PDU with unexpected Protocol version (%u) received", header.ver); rtr_send_error_pdu(rtr_socket, pdu, sizeof(header), UNEXPECTED_PROTOCOL_VERSION, NULL, 0); return RTR_ERROR; } rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); return RTR_ERROR; }
static void test_rtr_pdu_check_size(void **state) { struct pdu_header pdu; struct pdu_error *error = malloc(30); UNUSED(state); memset(error, 0, 30); pdu.type = SERIAL_NOTIFY; pdu.len = 20; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 12; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = CACHE_RESPONSE; pdu.len = 5; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 8; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = IPV4_PREFIX; pdu.len = 25; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 20; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = IPV6_PREFIX; pdu.len = 15; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 32; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = CACHE_RESET; pdu.len = 10; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 8; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = SERIAL_QUERY; pdu.len = 14; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 12; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = RESET_QUERY; pdu.len = 10; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 8; assert_true(rtr_pdu_check_size(&pdu)); pdu.type = RESERVED; pdu.len = 0; assert_false(rtr_pdu_check_size(&pdu)); pdu.type = EOD; pdu.ver = RTR_PROTOCOL_VERSION_1; pdu.len = 12; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 24; assert_true(rtr_pdu_check_size(&pdu)); pdu.ver = RTR_PROTOCOL_VERSION_0; pdu.len = 18; assert_false(rtr_pdu_check_size(&pdu)); pdu.len = 12; assert_true(rtr_pdu_check_size(&pdu)); /* Test error pdu size checks */ error->type = ERROR; error->len = 14; error->len_enc_pdu = 0; assert_false(rtr_pdu_check_size((struct pdu_header *)error)); error->len = 20; error->len_enc_pdu = 0x8000000; assert_false(rtr_pdu_check_size((struct pdu_header *)error)); error->len = 24; error->rest[11] = 0xA; assert_false(rtr_pdu_check_size((struct pdu_header *)error)); /* test error pdu error string termination test */ error->len = 25; error->rest[11] = 0x1; error->rest[12] = 0x20; assert_false(rtr_pdu_check_size((struct pdu_header *)error)); }