/* Copyright (c) 2011 Denis Bilenko. See LICENSE for details. */
#include "Python.h"
#ifdef CARES_EMBED
#include "ares_setup.h"
#endif

#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif

#include "ares.h"

#include "cares_ntop.h"
#include "cares_pton.h"

#if PY_VERSION_HEX < 0x02060000
  #define PyBytes_FromString           PyString_FromString
#endif


static PyObject* _socket_error = 0;

static PyObject*
get_socket_object(PyObject** pobject, const char* name)
{
    if (!*pobject) {
        PyObject* _socket;
        _socket = PyImport_ImportModule("_socket");
        if (_socket) {
            *pobject = PyObject_GetAttrString(_socket, name);
            if (!*pobject) {
                PyErr_WriteUnraisable(Py_None);
            }
            Py_DECREF(_socket);
        }
        else {
            PyErr_WriteUnraisable(Py_None);
        }
        if (!*pobject) {
            *pobject = PyExc_IOError;
        }
    }
    return *pobject;
}


static int
gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t tmpsize) {
    int status = -1;
    PyObject* tmp;
    if (ares_inet_ntop(family, src, tmpbuf, tmpsize)) {
        tmp = PyBytes_FromString(tmpbuf);
        if (tmp) {
            status = PyList_Append(list, tmp);
            Py_DECREF(tmp);
        }
    }
    return status;
}


static PyObject*
parse_h_aliases(struct hostent *h)
{
    char **pch;
    PyObject *result = NULL;
    PyObject *tmp;

    result = PyList_New(0);

    if (result && h->h_aliases) {
        for (pch = h->h_aliases; *pch != NULL; pch++) {
            if (*pch != h->h_name && strcmp(*pch, h->h_name)) {
                int status;
                tmp = PyBytes_FromString(*pch);
                if (tmp == NULL) {
                    break;
                }

                status = PyList_Append(result, tmp);
                Py_DECREF(tmp);

                if (status) {
                    break;
                }
            }
        }
    }

    return result;
}


static PyObject *
parse_h_addr_list(struct hostent *h)
{
    char **pch;
    PyObject *result = NULL;

    result = PyList_New(0);

    if (result) {
        switch (h->h_addrtype) {
            case AF_INET:
            {
                char tmpbuf[sizeof "255.255.255.255"];
                for (pch = h->h_addr_list; *pch != NULL; pch++) {
                    if (gevent_append_addr(result, AF_INET, *pch, tmpbuf, sizeof(tmpbuf))) {
                        break;
                    }
                }
                break;
            }
            case AF_INET6:
            {
                char tmpbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
                for (pch = h->h_addr_list; *pch != NULL; pch++) {
                    if (gevent_append_addr(result, AF_INET6, *pch, tmpbuf, sizeof(tmpbuf))) {
                        break;
                    }
                }
                break;
            }
            default:
                PyErr_SetString(get_socket_object(&_socket_error, "error"), "unsupported address family");
                Py_DECREF(result);
                result = NULL;
        }
    }

    return result;
}


static int
gevent_make_sockaddr(char* hostp, int port, int flowinfo, int scope_id, struct sockaddr_in6* sa6) {
    if ( ares_inet_pton(AF_INET, hostp, &((struct sockaddr_in*)sa6)->sin_addr.s_addr) > 0 ) {
        ((struct sockaddr_in*)sa6)->sin_family = AF_INET;
        ((struct sockaddr_in*)sa6)->sin_port = htons(port);
        return sizeof(struct sockaddr_in);
    }
    else if ( ares_inet_pton(AF_INET6, hostp, &sa6->sin6_addr.s6_addr) > 0 ) {
        sa6->sin6_family = AF_INET6;
        sa6->sin6_port = htons(port);
        sa6->sin6_flowinfo = flowinfo;
        sa6->sin6_scope_id = scope_id;
        return sizeof(struct sockaddr_in6);
    }
    return -1;
}
