/**
 * @file BAddr.h
 * @author Ambroz Bizjak <ambrop7@gmail.com>
 * 
 * @section LICENSE
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @section DESCRIPTION
 * 
 * Network address abstractions.
 */

#ifndef BADVPN_SYSTEM_BADDR_H
#define BADVPN_SYSTEM_BADDR_H

#include <stdint.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifdef BADVPN_USE_WINAPI
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif

#include <misc/byteorder.h>
#include <misc/debug.h>
#include <misc/print_macros.h>
#include <misc/read_write_int.h>
#include <misc/compare.h>

#define BADDR_TYPE_NONE 0
#define BADDR_TYPE_IPV4 1
#define BADDR_TYPE_IPV6 2
#ifdef BADVPN_LINUX
    #define BADDR_TYPE_PACKET 5
#endif

#define BADDR_MAX_ADDR_LEN 128

#define BIPADDR_MAX_PRINT_LEN 40
#define BADDR_MAX_PRINT_LEN 120

#define BADDR_PACKET_HEADER_TYPE_ETHERNET 1

#define BADDR_PACKET_PACKET_TYPE_HOST 1
#define BADDR_PACKET_PACKET_TYPE_BROADCAST 2
#define BADDR_PACKET_PACKET_TYPE_MULTICAST 3
#define BADDR_PACKET_PACKET_TYPE_OTHERHOST 4
#define BADDR_PACKET_PACKET_TYPE_OUTGOING 5

typedef struct {
    int type;
    union {
        uint32_t ipv4;
        uint8_t ipv6[16];
    };
} BIPAddr;

static void BIPAddr_InitInvalid (BIPAddr *addr);

static void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip);

static void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip);

static void BIPAddr_Assert (BIPAddr *addr);

static int BIPAddr_IsInvalid (BIPAddr *addr);

static int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) WARN_UNUSED;

static int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2);

/**
 * Converts an IP address to human readable form.
 *
 * @param addr IP address to convert
 * @param out destination buffer. Must be at least BIPADDR_MAX_PRINT_LEN characters long.
 */
static void BIPAddr_Print (BIPAddr *addr, char *out);

/**
 * Socket address - IP address and transport protocol port number
 */
typedef struct {
    int type;
    union {
        struct {
            uint32_t ip;
            uint16_t port;
        } ipv4;
        struct {
            uint8_t ip[16];
            uint16_t port;
        } ipv6;
        struct {
            uint16_t phys_proto;
            int interface_index;
            int header_type;
            int packet_type;
            uint8_t phys_addr[8];
        } packet;
    };
} BAddr;

/**
 * Makes an invalid address.
 */
static BAddr BAddr_MakeNone (void);

/**
 * Makes an IPv4 address.
 *
 * @param ip IP address in network byte order
 * @param port port number in network byte order
 */
static BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port);

/**
 * Makes an IPv6 address.
 *
 * @param ip IP address (16 bytes)
 * @param port port number in network byte order
 */
static BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port);

/**
 * Makes an address from a BIPAddr and port number.
 *
 * @param ipaddr the BIPAddr
 * @param port port number in network byte order
 */
static BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port);

/**
 * Deprecated, use BAddr_MakeNone.
 */
static void BAddr_InitNone (BAddr *addr);

/**
 * Deprecated, use BAddr_MakeIPv4.
 */
static void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port);

/**
 * Deprecated, use BAddr_MakeIPv6.
 */
static void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port);

/**
 * Deprecated, use BAddr_MakeFromIpaddrAndPort.
 */
static void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port);

/**
 * Initializes a packet socket (data link layer) address.
 * Only Ethernet addresses are supported.
 * 
 * @param addr the object
 * @param phys_proto identifier for the upper protocol, network byte order (EtherType)
 * @param interface_index network interface index
 * @param header_type data link layer header type. Must be BADDR_PACKET_HEADER_TYPE_ETHERNET.
 * @param packet_type the manner in which packets are sent/received. Must be one of
 *   BADDR_PACKET_PACKET_TYPE_HOST, BADDR_PACKET_PACKET_TYPE_BROADCAST,
 *   BADDR_PACKET_PACKET_TYPE_MULTICAST, BADDR_PACKET_PACKET_TYPE_OTHERHOST,
 *   BADDR_PACKET_PACKET_TYPE_OUTGOING.
 * @param phys_addr data link layer address (MAC address)
 */
static void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr);

/**
 * Does nothing.
 *
 * @param addr the object
 */
static void BAddr_Assert (BAddr *addr);

/**
 * Determines whether the address is an invalid address.
 *
 * @param addr the object
 * @return 1 if invalid, 0 if invalid
 **/
static int BAddr_IsInvalid (BAddr *addr);

/**
 * Returns the port number in the address.
 *
 * @param addr the object
 *             Must be an IPv4 or IPv6 address.
 * @return port number, in network byte order
 */
static uint16_t BAddr_GetPort (BAddr *addr);

/**
 * Returns the IP address in the address.
 *
 * @param addr the object
 * @param ipaddr IP address will be returned here. If \a addr is not
 *               an IPv4 or IPv6 address, an invalid address will be
 *               returned.
 */
static void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr);

/**
 * Sets the port number in the address.
 *
 * @param addr the object
 *             Must be an IPv4 or IPv6 address.
 * @param port port number, in network byte order
 */
static void BAddr_SetPort (BAddr *addr, uint16_t port);

/**
 * Converts an IP address to human readable form.
 *
 * @param addr address to convert
 * @param out destination buffer. Must be at least BADDR_MAX_PRINT_LEN characters long.
 */
static void BAddr_Print (BAddr *addr, char *out);

/**
 * Resolves an address string.
 * Format is "addr:port" for IPv4, "[addr]:port" for IPv6.
 * addr is be a numeric address or a name.
 * port is a numeric port number.
 *
 * @param addr output address
 * @param name if not NULL, the name portion of the address will be
 *             stored here
 * @param name_len if name is not NULL, the size of the name buffer
 * @param noresolve only accept numeric addresses. Avoids blocking the caller.
 * @return 1 on success, 0 on parse error
 */
static int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) WARN_UNUSED;

/**
 * Resolves an address string.
 * IPv4 input format is "a.b.c.d:p", where a.b.c.d is the IP address
 * and d is the port number.
 * IPv6 input format is "[addr]:p", where addr is an IPv6 addres in
 * standard notation and p is the port number.
 *
 * @param addr output address
 * @param name if not NULL, the name portion of the address will be
 *             stored here
 * @param name_len if name is not NULL, the size of the name buffer
 * @return 1 on success, 0 on parse error
 */
static int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) WARN_UNUSED;

static int BAddr_Compare (BAddr *addr1, BAddr *addr2);

static int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2);

void BIPAddr_InitInvalid (BIPAddr *addr)
{
    addr->type = BADDR_TYPE_NONE;
}

void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip)
{
    addr->type = BADDR_TYPE_IPV4;
    addr->ipv4 = ip;
}

void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip)
{
    addr->type = BADDR_TYPE_IPV6;
    memcpy(addr->ipv6, ip, 16);
}

void BIPAddr_Assert (BIPAddr *addr)
{
    switch (addr->type) {
        case BADDR_TYPE_NONE:
        case BADDR_TYPE_IPV4:
        case BADDR_TYPE_IPV6:
            return;
        default:
            ASSERT(0);
    }
}

int BIPAddr_IsInvalid (BIPAddr *addr)
{
    BIPAddr_Assert(addr);
    
    return (addr->type == BADDR_TYPE_NONE);
}

int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve)
{
    int len = strlen(str);
    
    char *addr_start;
    int addr_len;
    
    // determine address type
    if (len >= 1 && str[0] == '[' && str[len - 1] == ']') {
        addr->type = BADDR_TYPE_IPV6;
        addr_start = str + 1;
        addr_len = len - 2;
    } else {
        addr->type = BADDR_TYPE_IPV4;
        addr_start = str;
        addr_len = len;
    }
    
    // copy
    char addr_str[BADDR_MAX_ADDR_LEN + 1];
    if (addr_len > BADDR_MAX_ADDR_LEN) {
        return 0;
    }
    memcpy(addr_str, addr_start, addr_len);
    addr_str[addr_len] = '\0';
    
    // initialize hints
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    switch (addr->type) {
        case BADDR_TYPE_IPV6:
            hints.ai_family = AF_INET6;
            break;
        case BADDR_TYPE_IPV4:
            hints.ai_family = AF_INET;
            break;
    }
    if (noresolve) {
        hints.ai_flags |= AI_NUMERICHOST;
    }
    
    // call getaddrinfo
    struct addrinfo *addrs;
    int res;
    if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) {
        return 0;
    }
    
    // set address
    switch (addr->type) {
        case BADDR_TYPE_IPV6:
            memcpy(addr->ipv6, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6));
            break;
        case BADDR_TYPE_IPV4:
            addr->ipv4 = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr;
            break;
    }
    
    freeaddrinfo(addrs);
    
    return 1;
}

int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2)
{
    BIPAddr_Assert(addr1);
    BIPAddr_Assert(addr2);
    
    if (addr1->type != addr2->type) {
        return 0;
    }
    
    switch (addr1->type) {
        case BADDR_TYPE_NONE:
            return 0;
        case BADDR_TYPE_IPV4:
            return (addr1->ipv4 == addr2->ipv4);
        case BADDR_TYPE_IPV6:
            return (!memcmp(addr1->ipv6, addr2->ipv6, sizeof(addr1->ipv6)));
        default:
            ASSERT(0)
            return 0;
    }
}

uint16_t BAddr_GetPort (BAddr *addr)
{
    BAddr_Assert(addr);
    ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6)
    
    switch (addr->type) {
        case BADDR_TYPE_IPV4:
            return addr->ipv4.port;
        case BADDR_TYPE_IPV6:
            return addr->ipv6.port;
        default:
            ASSERT(0)
            return 0;
    }
}

void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr)
{
    BAddr_Assert(addr);
    
    switch (addr->type) {
        case BADDR_TYPE_IPV4:
            BIPAddr_InitIPv4(ipaddr, addr->ipv4.ip);
            return;
        case BADDR_TYPE_IPV6:
            BIPAddr_InitIPv6(ipaddr, addr->ipv6.ip);
            return;
        default:
            BIPAddr_InitInvalid(ipaddr);
    }
}

void BAddr_SetPort (BAddr *addr, uint16_t port)
{
    BAddr_Assert(addr);
    ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6)
    
    switch (addr->type) {
        case BADDR_TYPE_IPV4:
            addr->ipv4.port = port;
            break;
        case BADDR_TYPE_IPV6:
            addr->ipv6.port = port;
            break;
        default:
            ASSERT(0);
    }
}

void BIPAddr_Print (BIPAddr *addr, char *out)
{
    switch (addr->type) {
        case BADDR_TYPE_NONE:
            sprintf(out, "(none)");
            break;
        case BADDR_TYPE_IPV4:
            sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8,
                *((uint8_t *)&addr->ipv4 + 0),
                *((uint8_t *)&addr->ipv4 + 1),
                *((uint8_t *)&addr->ipv4 + 2),
                *((uint8_t *)&addr->ipv4 + 3)
            );
            break;
        case BADDR_TYPE_IPV6: {
            const char *ptr = (const char *)addr->ipv6;
            sprintf(out,
                "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16":"
                "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16,
                badvpn_read_be16(ptr + 0),
                badvpn_read_be16(ptr + 2),
                badvpn_read_be16(ptr + 4),
                badvpn_read_be16(ptr + 6),
                badvpn_read_be16(ptr + 8),
                badvpn_read_be16(ptr + 10),
                badvpn_read_be16(ptr + 12),
                badvpn_read_be16(ptr + 14)
            );
        } break;
        default:
            ASSERT(0);
    }
}

BAddr BAddr_MakeNone (void)
{
    BAddr addr;
    addr.type = BADDR_TYPE_NONE;
    return addr;
}

BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port)
{
    BAddr addr;
    addr.type = BADDR_TYPE_IPV4;
    addr.ipv4.ip = ip;
    addr.ipv4.port = port;
    return addr;
}

BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port)
{
    BAddr addr;
    addr.type = BADDR_TYPE_IPV6;
    memcpy(addr.ipv6.ip, ip, 16);
    addr.ipv6.port = port;
    return addr;
}

BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port)
{
    BIPAddr_Assert(&ipaddr);
    
    switch (ipaddr.type) {
        case BADDR_TYPE_NONE:
            return BAddr_MakeNone();
        case BADDR_TYPE_IPV4:
            return BAddr_MakeIPv4(ipaddr.ipv4, port);
        case BADDR_TYPE_IPV6:
            return BAddr_MakeIPv6(ipaddr.ipv6, port);
        default:
            ASSERT(0);
            return BAddr_MakeNone();
    }
}

void BAddr_InitNone (BAddr *addr)
{
    *addr = BAddr_MakeNone();
}

void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port)
{
    *addr = BAddr_MakeIPv4(ip, port);
}

void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port)
{
    *addr = BAddr_MakeIPv6(ip, port);
}

void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port)
{
    BIPAddr_Assert(&ipaddr);
    
    *addr = BAddr_MakeFromIpaddrAndPort(ipaddr, port);
}

#ifdef BADVPN_LINUX

void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr)
{
    ASSERT(header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
    ASSERT(packet_type == BADDR_PACKET_PACKET_TYPE_HOST || packet_type == BADDR_PACKET_PACKET_TYPE_BROADCAST || 
           packet_type == BADDR_PACKET_PACKET_TYPE_MULTICAST || packet_type == BADDR_PACKET_PACKET_TYPE_OTHERHOST ||
           packet_type == BADDR_PACKET_PACKET_TYPE_OUTGOING)
    
    addr->type = BADDR_TYPE_PACKET;
    addr->packet.phys_proto = phys_proto;
    addr->packet.interface_index = interface_index;
    addr->packet.header_type = header_type;
    addr->packet.packet_type = packet_type;
    memcpy(addr->packet.phys_addr, phys_addr, 6);
}

#endif

void BAddr_Assert (BAddr *addr)
{
    switch (addr->type) {
        case BADDR_TYPE_NONE:
        case BADDR_TYPE_IPV4:
        case BADDR_TYPE_IPV6:
        #ifdef BADVPN_LINUX
        case BADDR_TYPE_PACKET:
        #endif
            return;
        default:
            ASSERT(0);
    }
}

int BAddr_IsInvalid (BAddr *addr)
{
    BAddr_Assert(addr);
    
    return (addr->type == BADDR_TYPE_NONE);
}

void BAddr_Print (BAddr *addr, char *out)
{
    BAddr_Assert(addr);
    
    BIPAddr ipaddr;
    
    switch (addr->type) {
        case BADDR_TYPE_NONE:
            sprintf(out, "(none)");
            break;
        case BADDR_TYPE_IPV4:
            BIPAddr_InitIPv4(&ipaddr, addr->ipv4.ip);
            BIPAddr_Print(&ipaddr, out);
            sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv4.port));
            break;
        case BADDR_TYPE_IPV6:
            BIPAddr_InitIPv6(&ipaddr, addr->ipv6.ip);
            BIPAddr_Print(&ipaddr, out);
            sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv6.port));
            break;
        #ifdef BADVPN_LINUX
        case BADDR_TYPE_PACKET:
            ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
            sprintf(out, "proto=%"PRIu16",ifindex=%d,htype=eth,ptype=%d,addr=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8,
                    addr->packet.phys_proto, (int)addr->packet.interface_index, (int)addr->packet.packet_type,
                    addr->packet.phys_addr[0], addr->packet.phys_addr[1], addr->packet.phys_addr[2],
                    addr->packet.phys_addr[3], addr->packet.phys_addr[4], addr->packet.phys_addr[5]);
            break;
        #endif
        default:
            ASSERT(0);
    }
}

int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve)
{
    int len = strlen(str);
    if (len < 1 || len > 1000) {
        return 0;
    }
    
    int addr_start;
    int addr_len;
    int port_start;
    int port_len;
    
    // leading '[' indicates an IPv6 address
    if (str[0] == '[') {
        addr->type = BADDR_TYPE_IPV6;
        // find ']'
        int i=1;
        while (i < len && str[i] != ']') i++;
        if (i >= len) {
            return 0;
        }
        addr_start = 1;
        addr_len = i - addr_start;
        // follows ':' and port number
        if (i + 1 >= len || str[i + 1] != ':') {
            return 0;
        }
        port_start = i + 2;
        port_len = len - port_start;
    }
    // otherwise it's an IPv4 address
    else {
        addr->type = BADDR_TYPE_IPV4;
        // find ':'
        int i=0;
        while (i < len && str[i] != ':') i++;
        if (i >= len) {
            return 0;
        }
        addr_start = 0;
        addr_len = i - addr_start;
        port_start = i + 1;
        port_len = len - port_start;
    }
    
    // copy address and port to zero-terminated buffers
    
    char addr_str[128];
    if (addr_len >= sizeof(addr_str)) {
        return 0;
    }
    memcpy(addr_str, str + addr_start, addr_len);
    addr_str[addr_len] = '\0';
    
    char port_str[6];
    if (port_len >= sizeof(port_str)) {
        return 0;
    }
    memcpy(port_str, str + port_start, port_len);
    port_str[port_len] = '\0';
    
    // parse port
    char *err;
    long int conv_res = strtol(port_str, &err, 10);
    if (port_str[0] == '\0' || *err != '\0') {
        return 0;
    }
    if (conv_res < 0 || conv_res > UINT16_MAX) {
        return 0;
    }
    uint16_t port = conv_res;
    port = hton16(port);
    
    // initialize hints
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    switch (addr->type) {
        case BADDR_TYPE_IPV6:
            hints.ai_family = AF_INET6;
            break;
        case BADDR_TYPE_IPV4:
            hints.ai_family = AF_INET;
            break;
    }
    if (noresolve) {
        hints.ai_flags |= AI_NUMERICHOST;
    }
    
    // call getaddrinfo
    struct addrinfo *addrs;
    int res;
    if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) {
        return 0;
    }
    
    // set address
    switch (addr->type) {
        case BADDR_TYPE_IPV6:
            memcpy(addr->ipv6.ip, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6.ip));
            addr->ipv6.port = port;
            break;
        case BADDR_TYPE_IPV4:
            addr->ipv4.ip = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr;
            addr->ipv4.port = port;
            break;
    }
    
    freeaddrinfo(addrs);
    
    if (name) {
        if (strlen(addr_str) >= name_len) {
            return 0;
        }
        strcpy(name, addr_str);
    }
    
    return 1;
}

int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len)
{
    return BAddr_Parse2(addr, str, name, name_len, 0);
}

int BAddr_Compare (BAddr *addr1, BAddr *addr2)
{
    BAddr_Assert(addr1);
    BAddr_Assert(addr2);
    
    if (addr1->type != addr2->type) {
        return 0;
    }
    
    switch (addr1->type) {
        case BADDR_TYPE_IPV4:
            return (addr1->ipv4.ip == addr2->ipv4.ip && addr1->ipv4.port == addr2->ipv4.port);
        case BADDR_TYPE_IPV6:
            return (!memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)) && addr1->ipv6.port == addr2->ipv6.port);
        default:
            return 0;
    }
}

int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2)
{
    BAddr_Assert(addr1);
    BAddr_Assert(addr2);
    
    int cmp = B_COMPARE(addr1->type, addr2->type);
    if (cmp) {
        return cmp;
    }
    
    switch (addr1->type) {
        case BADDR_TYPE_NONE: {
            return 0;
        } break;
        case BADDR_TYPE_IPV4: {
            uint32_t ip1 = ntoh32(addr1->ipv4.ip);
            uint32_t ip2 = ntoh32(addr2->ipv4.ip);
            cmp = B_COMPARE(ip1, ip2);
            if (cmp) {
                return cmp;
            }
            uint16_t port1 = ntoh16(addr1->ipv4.port);
            uint16_t port2 = ntoh16(addr2->ipv4.port);
            return B_COMPARE(port1, port2);
        } break;
        case BADDR_TYPE_IPV6: {
            cmp = memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip));
            if (cmp) {
                return B_COMPARE(cmp, 0);
            }
            uint16_t port1 = ntoh16(addr1->ipv6.port);
            uint16_t port2 = ntoh16(addr2->ipv6.port);
            return B_COMPARE(port1, port2);
        } break;
        default: {
            return 0;
        } break;
    }
}

#endif
