/* Copyright (C) 2009-2010 by Daniel Stenberg
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */


#include "ares_setup.h"

#include <stddef.h>

#include "ares.h"
#include "ares_data.h"
#include "ares_private.h"


/*
** ares_free_data() - c-ares external API function.
**
** This function must be used by the application to free data memory that
** has been internally allocated by some c-ares function and for which a
** pointer has already been returned to the calling application. The list
** of c-ares functions returning pointers that must be free'ed using this
** function is:
**
**   ares_get_servers()
**   ares_parse_srv_reply()
**   ares_parse_txt_reply()
*/

void ares_free_data(void *dataptr)
{
  struct ares_data *ptr;

  if (!dataptr)
    return;

#ifdef __INTEL_COMPILER
#  pragma warning(push)
#  pragma warning(disable:1684)
   /* 1684: conversion from pointer to same-sized integral type */
#endif

  ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data));

#ifdef __INTEL_COMPILER
#  pragma warning(pop)
#endif

  if (ptr->mark != ARES_DATATYPE_MARK)
    return;

  switch (ptr->type)
    {
      case ARES_DATATYPE_MX_REPLY:

        if (ptr->data.mx_reply.next)
          ares_free_data(ptr->data.mx_reply.next);
        if (ptr->data.mx_reply.host)
          free(ptr->data.mx_reply.host);
        break;

      case ARES_DATATYPE_SRV_REPLY:

        if (ptr->data.srv_reply.next)
          ares_free_data(ptr->data.srv_reply.next);
        if (ptr->data.srv_reply.host)
          free(ptr->data.srv_reply.host);
        break;

      case ARES_DATATYPE_TXT_REPLY:

        if (ptr->data.txt_reply.next)
          ares_free_data(ptr->data.txt_reply.next);
        if (ptr->data.txt_reply.txt)
          free(ptr->data.txt_reply.txt);
        break;

      case ARES_DATATYPE_ADDR_NODE:

        if (ptr->data.addr_node.next)
          ares_free_data(ptr->data.addr_node.next);
        break;

      default:
        return;
    }

  free(ptr);
}


/*
** ares_malloc_data() - c-ares internal helper function.
**
** This function allocates memory for a c-ares private ares_data struct
** for the specified ares_datatype, initializes c-ares private fields
** and zero initializes those which later might be used from the public
** API. It returns an interior pointer which can be passed by c-ares
** functions to the calling application, and that must be free'ed using
** c-ares external API function ares_free_data().
*/

void *ares_malloc_data(ares_datatype type)
{
  struct ares_data *ptr;

  ptr = malloc(sizeof(struct ares_data));
  if (!ptr)
    return NULL;

  switch (type)
    {
      case ARES_DATATYPE_MX_REPLY:
        ptr->data.mx_reply.next = NULL;
        ptr->data.mx_reply.host = NULL;
        ptr->data.mx_reply.priority = 0;
        break;

      case ARES_DATATYPE_SRV_REPLY:
        ptr->data.srv_reply.next = NULL;
        ptr->data.srv_reply.host = NULL;
        ptr->data.srv_reply.priority = 0;
        ptr->data.srv_reply.weight = 0;
        ptr->data.srv_reply.port = 0;
        break;

      case ARES_DATATYPE_TXT_REPLY:
        ptr->data.txt_reply.next = NULL;
        ptr->data.txt_reply.txt = NULL;
        ptr->data.txt_reply.length  = 0;
        break;

      case ARES_DATATYPE_ADDR_NODE:
        ptr->data.addr_node.next = NULL;
        ptr->data.addr_node.family = 0;
        memset(&ptr->data.addr_node.addrV6, 0,
               sizeof(ptr->data.addr_node.addrV6));
        break;

      default:
        free(ptr);
        return NULL;
    }

  ptr->mark = ARES_DATATYPE_MARK;
  ptr->type = type;

  return &ptr->data;
}


/*
** ares_get_datatype() - c-ares internal helper function.
**
** This function returns the ares_datatype of the data stored in a
** private ares_data struct when given the public API pointer.
*/

ares_datatype ares_get_datatype(void * dataptr)
{
  struct ares_data *ptr;

#ifdef __INTEL_COMPILER
#  pragma warning(push)
#  pragma warning(disable:1684)
   /* 1684: conversion from pointer to same-sized integral type */
#endif

  ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data));

#ifdef __INTEL_COMPILER
#  pragma warning(pop)
#endif

  if (ptr->mark == ARES_DATATYPE_MARK)
    return ptr->type;

  return ARES_DATATYPE_UNKNOWN;
}