/*
 * Copyright (c) 2020, Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nsconfig.h"
#include "ns_types.h"
#include "eventOS_event.h"
#include "ns_trace.h"
#include "string.h"
#include "common_functions.h"
#include "Security/protocols/radius_sec_prot/avp_helper.h"

#ifdef HAVE_WS

#define TRACE_GROUP "avp"

// RFC 2865

// 1 User-Name
#define AVP_TYPE_USER_NAME              1
// 4 for NAS-IP-Address
#define AVP_TYPE_NAS_IP_ADDRESS         4
// 5 NAS-Port
#define AVP_TYPE_NAS_PORT               5
// 12 Framed-MTU
#define AVP_TYPE_FRAMED_MTU             12
// 24 State
#define AVP_TYPE_STATE                  24
// 26 Vendor-Specific
#define AVP_TYPE_VENDOR_SPECIFIC        26
// 30 Called-Station-Id
#define AVP_TYPE_CALLED_STATION_ID      30
// 31 Calling-Station-Id
#define AVP_TYPE_CALLING_STATION_ID     31
// 32 NAS-Identifier
#define AVP_TYPE_NAS_IDENTIFIER         32
// 61 NAS-Port-Type
#define AVP_TYPE_NAS_PORT_TYPE          61

// RFC 3579

// 79 EAP-Message
#define AVP_TYPE_EAP_MESSAGE            79
// 80 Message-Authenticator
#define AVP_TYPE_MESSAGE_AUTHENTICATOR  80

// RFC 3162

// 95 NAS-IPv6-Address
#define AVP_TYPE_NAS_IPV6_ADDRESS       95


static uint8_t *avp_header_write(uint8_t *ptr, const uint8_t type, const uint8_t data_length)
{
    *ptr++ = type;
    *ptr++ = data_length + AVP_FIXED_LEN;

    return ptr;
}

static uint8_t *avp_search(uint8_t *ptr, uint16_t len, const uint8_t type, uint8_t *avp_len)
{
    while (len >= AVP_FIXED_LEN) {
        *avp_len = ptr[1];

        // Validates length field
        if (*avp_len > len) {
            return NULL;
        }

        if (ptr[0] == type) {
            return ptr + AVP_FIXED_LEN;
        }

        if (len > *avp_len) {
            len -= *avp_len;
            ptr += *avp_len;
        } else {
            return NULL;
        }
    }

    return NULL;
}

static uint8_t *avp_vpa_search(uint8_t *ptr, uint16_t len, const uint32_t vendor_id, const uint8_t vendor_type, uint8_t *vendor_len)
{
    uint8_t avp_len = 0;

    while (len >= AVP_FIXED_LEN) {
        avp_len = ptr[1];

        // Validates length field
        if (avp_len > len) {
            return NULL;
        }

        if (ptr[0] == AVP_TYPE_VENDOR_SPECIFIC && avp_len >= 9) {
            ptr[2] = 0;
            uint32_t avp_vendor_id = common_read_32_bit(&ptr[2]);
            *vendor_len = ptr[7];
            if (avp_vendor_id == vendor_id && ptr[6] == vendor_type) {
                return &ptr[8];
            }
        }

        if (len > avp_len) {
            len -= avp_len;
            ptr += avp_len;
        } else {
            return NULL;
        }
    }

    return NULL;
}

uint8_t *avp_user_name_write(uint8_t *ptr, const uint8_t name_len, const uint8_t *name)
{
    ptr = avp_header_write(ptr, AVP_TYPE_USER_NAME, name_len);
    memcpy(ptr, name, name_len);
    return ptr + name_len;
}

uint8_t *avp_nas_ip_address_write(uint8_t *ptr, uint32_t addr)
{
    ptr = avp_header_write(ptr, AVP_TYPE_NAS_IP_ADDRESS, 4);
    memcpy(ptr, &addr, 4);
    return ptr + 4;
}

uint8_t *avp_nas_port_write(uint8_t *ptr, const uint32_t port)
{
    ptr = avp_header_write(ptr, AVP_TYPE_NAS_PORT, 4);
    ptr = common_write_32_bit(port, ptr);
    return ptr;
}

uint8_t *avp_framed_mtu_write(uint8_t *ptr, const uint32_t mtu)
{
    ptr = avp_header_write(ptr, AVP_TYPE_FRAMED_MTU, 4);
    ptr = common_write_32_bit(mtu, ptr);
    return ptr;
}

uint8_t *avp_state_write(uint8_t *ptr, const uint8_t state_len, const uint8_t *state)
{
    ptr = avp_header_write(ptr, AVP_TYPE_STATE, state_len);
    memcpy(ptr, state, state_len);
    return ptr + state_len;
}

uint8_t *avp_called_station_id_write(uint8_t *ptr, const uint8_t id_len, const uint8_t *id)
{
    ptr = avp_header_write(ptr, AVP_TYPE_CALLED_STATION_ID, id_len);
    memcpy(ptr, id, id_len);
    return ptr + id_len;
}

uint8_t *avp_calling_station_id_write(uint8_t *ptr, const uint8_t id_len, const uint8_t *id)
{
    ptr = avp_header_write(ptr, AVP_TYPE_CALLING_STATION_ID, id_len);
    memcpy(ptr, id, id_len);
    return ptr + id_len;
}

uint8_t *avp_nas_identifier_write(uint8_t *ptr, const uint8_t id_len, const uint8_t *id)
{
    ptr = avp_header_write(ptr, AVP_TYPE_NAS_IDENTIFIER, id_len);
    memcpy(ptr, id, id_len);
    return ptr + id_len;
}

uint8_t *avp_nas_port_type_write(uint8_t *ptr, const uint32_t port_type)
{
    ptr = avp_header_write(ptr, AVP_TYPE_NAS_PORT_TYPE, 4);
    ptr = common_write_32_bit(port_type, ptr);
    return ptr;
}

uint8_t *avp_eap_message_write(uint8_t *ptr, const uint8_t eap_len, const uint8_t *eap)
{
    ptr = avp_header_write(ptr, AVP_TYPE_EAP_MESSAGE, eap_len);
    memcpy(ptr, eap, eap_len);
    return ptr + eap_len;
}

uint8_t *avp_message_authenticator_write(uint8_t *ptr, const uint8_t *auth)
{
    ptr = avp_header_write(ptr, AVP_TYPE_MESSAGE_AUTHENTICATOR, 16);
    memcpy(ptr, auth, 16);
    return ptr + 16;
}

uint8_t *avp_nas_ipv6_address_write(uint8_t *ptr, const uint8_t *address)
{
    ptr = avp_header_write(ptr, AVP_TYPE_NAS_IPV6_ADDRESS, 16);
    memcpy(ptr, address, 16);
    return ptr + 16;
}

uint8_t *avp_eap_message_read(uint8_t *ptr, uint16_t len, uint8_t *eap_len)
{
    ptr = avp_search(ptr, len, AVP_TYPE_EAP_MESSAGE, eap_len);
    if (ptr == NULL) {
        return NULL;
    }
    return ptr;
}

uint8_t *avp_message_authenticator_read(uint8_t *ptr, uint16_t len)
{
    uint8_t auth_len = 0;
    ptr = avp_search(ptr, len, AVP_TYPE_MESSAGE_AUTHENTICATOR, &auth_len);
    if (ptr == NULL) {
        return NULL;
    }
    if (auth_len < 18) {
        return NULL;
    }
    return ptr;
}

uint8_t *avp_state_read(uint8_t *ptr, uint16_t len, uint8_t *state_len)
{
    ptr = avp_search(ptr, len, AVP_TYPE_STATE, state_len);
    if (ptr == NULL) {
        return NULL;
    }
    return ptr;
}

uint8_t *avp_vsa_ms_mppe_recv_key_read(uint8_t *ptr, uint16_t len, uint8_t *recv_key_len)
{
    const uint32_t vendor_id = 311;
    const uint8_t vendor_type = 17;

    ptr = avp_vpa_search(ptr, len, vendor_id, vendor_type, recv_key_len);
    if (ptr == NULL) {
        return NULL;
    }
    return ptr;
}

#endif

