/*
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 2008 Karsten Keil
 * Copyright (c) 2026 Intel Corporation; Author: H. Peter Anvin
 */

/*
 * getaddrinfo.c
 *
 * Simple version of getaddrinfo()
 *
 */

#include "config.h"

extern int errno;
extern int h_errno;

void freeaddrinfo(struct addrinfo *res)
{
    if (!res)
        return;
    if (res->ai_next)
        freeaddrinfo(res->ai_next);
    if (res->ai_addr)
        free(res->ai_addr);
    if (res->ai_canonname)
        free(res->ai_canonname);
    free(res);
}

int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints,
                struct addrinfo **res)
{
    struct hostent  *host;
    struct sockaddr *sa;
    int size = 0;

    if ((!node) || (!res)) {
        errno = EINVAL;
        return EAI_SYSTEM;
    }
    *res = NULL;
    /* we do not support service in this version */
    if (service) {
        errno = EINVAL;
        return EAI_SYSTEM;
    }
    host = gethostbyname(node);
    if (!host)
        return EAI_NONAME;
    if (hints) {
        if (hints->ai_family != AF_UNSPEC) {
            if (hints->ai_family != host->h_addrtype)
                return EAI_ADDRFAMILY;
        }
    }
    *res =  malloc(sizeof(struct addrinfo));
    if (!*res) {
        return EAI_MEMORY;
    }
    memset(*res, 0, sizeof(struct addrinfo));
    (*res)->ai_family = host->h_addrtype;
    if (host->h_length) {
        if (host->h_addrtype == AF_INET)
            size = sizeof(struct sockaddr_in);
#ifdef HAVE_IPV6
        else if (host->h_addrtype == AF_INET6)
            size = sizeof(struct sockaddr_in6);
#endif
        else {
            free(*res);
            *res = NULL;
            return EAI_ADDRFAMILY;
        }
        sa = malloc(size);
        if (!sa) {
            free(*res);
            *res = NULL;
            return EAI_MEMORY;
        }
        memset(sa, 0, size);
        (*res)->ai_addr = sa;
        (*res)->ai_addrlen = size;
        sa->sa_family = host->h_addrtype;
        if (host->h_addrtype == AF_INET) {
            if ((size_t)host->h_length > sizeof(((struct sockaddr_in *)sa)->sin_addr)) {
                free(sa);
                free(*res);
                *res = NULL;
                return EAI_SYSTEM;
            }
            memcpy(&((struct sockaddr_in *)sa)->sin_addr, host->h_addr, host->h_length);
        }
#ifdef HAVE_IPV6
        else {
            if ((size_t)host->h_length > sizeof(((struct sockaddr_in6 *)sa)->sin6_addr)) {
                free(sa);
                free(*res);
                *res = NULL;
                return EAI_SYSTEM;
            }
            memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, host->h_addr, host->h_length);
        }
#endif
    }
    if (host->h_name)
       (*res)->ai_canonname = strdup(host->h_name);

    /* we only handle the first address entry and do not build a list now */
    return 0;
}



const char *gai_strerror(int errcode)
{
    const char *s = NULL;

    switch(errcode) {
    case 0:
        s = "no error";
        break;
    case EAI_MEMORY:
        s = "no memory";
        break;
    case EAI_SYSTEM:
        s = strerror(errno);
        break;
    case EAI_NONAME:
        s = hstrerror(h_errno);
        break;
    case EAI_ADDRFAMILY:
        s = "address does not match address family";
        break;
    default:
        s = "unknown error code";
        break;
    }
    return s;
}
