Пример #1
0
/*
 * 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;
}
Пример #2
0
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));
}