static void test_pdu_to_host_byte_order(void **state) { struct pdu_serial_notify pdu_serial; struct pdu_end_of_data_v1 pdu_eod; UNUSED(state); pdu_serial.ver = 1; pdu_serial.type = SERIAL_NOTIFY; pdu_serial.session_id = 0xDDFF; pdu_serial.len = 0xC; pdu_serial.sn = 0xDF; rtr_pdu_footer_to_host_byte_order(&pdu_serial); rtr_pdu_header_to_host_byte_order(&pdu_serial); assert_int_equal(pdu_serial.ver, 1); assert_int_equal(pdu_serial.type, SERIAL_NOTIFY); assert_int_equal(pdu_serial.len, 0xC000000); assert_int_equal(pdu_serial.sn, 0xDF000000); pdu_eod.ver = 1; pdu_eod.type = EOD; pdu_eod.session_id = 0xFDDF; pdu_eod.len = 0x18; pdu_eod.sn = 0xFEDCBA; pdu_eod.refresh_interval = 0xAF; pdu_eod.retry_interval = 0xDC; pdu_eod.expire_interval = 0xCCF; rtr_pdu_header_to_host_byte_order(&pdu_eod); rtr_pdu_footer_to_host_byte_order(&pdu_eod); assert_int_equal(pdu_eod.ver, 1); assert_int_equal(pdu_eod.type, EOD); assert_int_equal(pdu_eod.session_id, 0xDFFD); assert_int_equal(pdu_eod.len, 0x18000000); assert_int_equal(pdu_eod.sn, 0xBADCFE00); assert_int_equal(pdu_eod.refresh_interval, 0xAF000000); assert_int_equal(pdu_eod.retry_interval, 0xDC000000); assert_int_equal(pdu_eod.expire_interval, 0xCF0C0000); }
/* * 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; }