/***************************************************************************
 *
 * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: pysockaddr.c,v 1.16.2.1.2.2 2001/09/19 19:22:17 bazsi Exp $
 *
 * Author  : bazsi
 * Auditor : kisza
 * Last version: 1.4
 * Notes:
 *   - comments
 *
 ***************************************************************************/

#include <zorp/pysockaddr.h>
#include <zorp/log.h>

#include <netdb.h>

extern PyTypeObject z_py_zorp_sockaddr_type;

static PyObject *
z_py_ntohl(PyObject *self, PyObject *args)
{
  long i;
  
  if (!PyArg_ParseTuple(args, "i", &i))
    return NULL;
    
  return PyInt_FromLong(ntohl(i));
}


static PyObject *
z_py_htonl(PyObject *self, PyObject *args)
{
  int i;
  
  if (!PyArg_ParseTuple(args, "i", &i))
    return NULL;
  return PyInt_FromLong(htonl(i));
}

static PyObject *
z_py_gethostbyname(PyObject *self, PyObject *args)
{
  char *hostname;
  struct hostent *he;
  char buf[32];
  static GStaticMutex lock = G_STATIC_MUTEX_INIT;
  
  if (!PyArg_ParseTuple(args, "s", &hostname))
    return NULL;
  
  Py_BEGIN_ALLOW_THREADS
  g_static_mutex_lock(&lock);
  he = gethostbyname(hostname);
  g_static_mutex_unlock(&lock);
  Py_END_ALLOW_THREADS
  if (he)
    {
      z_inet_ntoa(buf, sizeof(buf), *((struct in_addr *) he->h_addr));
      return PyString_FromString(buf);
    }
  else
    {
      PyErr_SetString(PyExc_IOError, "Error resolving hostname.");
      return NULL;
    }
}


PyObject *
z_py_zorp_sockaddr_new(ZSockAddr *sa)
{
  ZorpSockAddr *self;

  if (!sa)
    {
      PyErr_SetString(PyExc_ValueError, "z_py_zorp_sockaddr_new, sa is NULL");
      return NULL;
    }
  self = PyObject_NEW(ZorpSockAddr, &z_py_zorp_sockaddr_type);
  self->sa = z_sockaddr_ref(sa);
  return (PyObject *) self;
}

static PyObject *
z_py_zorp_sockaddr_inet_new_instance(PyObject *s, PyObject *args)
{
  ZorpSockAddr *self;
  gchar *ip;
  gint port;
  
  if (!PyArg_Parse(args, "(si)", &ip, &port))
    return NULL;
  
  self = PyObject_NEW(ZorpSockAddr, &z_py_zorp_sockaddr_type);
  self->sa = z_sockaddr_inet_new(ip, port);
  return (PyObject *) self;
}

static PyObject *
z_py_zorp_sockaddr_inet_range_new_instance(PyObject *s, PyObject *args)
{
  ZorpSockAddr *self;
  gchar *ip;
  gint min_port, max_port;
  
  if (!PyArg_Parse(args, "(sii)", &ip, &min_port, &max_port))
    return NULL;
  
  self = PyObject_NEW(ZorpSockAddr, &z_py_zorp_sockaddr_type);
  self->sa = z_sockaddr_inet_range_new(ip, min_port, max_port);
  return (PyObject *) self;
}

static PyObject *
z_py_zorp_sockaddr_unix_new_instance(PyObject *s, PyObject *args)
{
  ZorpSockAddr *self;
  gchar *path;
  
  if (!PyArg_Parse(args, "(s)", &path))
    return NULL;
  
  self = PyObject_NEW(ZorpSockAddr, &z_py_zorp_sockaddr_type);
  self->sa = z_sockaddr_unix_new(path);
  return (PyObject *) self;
}

static void
z_py_zorp_sockaddr_free(ZorpSockAddr *self)
{
  z_sockaddr_unref(self->sa);
  PyMem_DEL(self);
}

static PyObject *
z_py_zorp_sockaddr_format(ZorpSockAddr *self)
{
  char buf[128];
  
  return PyString_FromString(z_sockaddr_format(self->sa, buf, sizeof(buf)));
}

static PyMethodDef py_zorp_sockaddr_methods[] =
{
  { "format",      (PyCFunction) z_py_zorp_sockaddr_format, 0 },
  { NULL,          NULL }   /* sentinel*/
};

PyMethodDef z_py_zorp_sockaddr_funcs[] =
{
  { "SockAddrInet",  z_py_zorp_sockaddr_inet_new_instance, METH_VARARGS },
  { "SockAddrInetRange",  z_py_zorp_sockaddr_inet_range_new_instance, METH_VARARGS },
  { "SockAddrUnix",  z_py_zorp_sockaddr_unix_new_instance, METH_VARARGS },
  { "ntohl", z_py_ntohl, METH_VARARGS },
  { "htonl", z_py_htonl, METH_VARARGS },
  { "getHostByName", z_py_gethostbyname, METH_VARARGS },
  { NULL,            NULL }   /* sentinel*/
};

static gint
z_py_zorp_sockaddr_setattr(PyObject *o, char *name, PyObject *value)
{
  ZorpSockAddr *self = (ZorpSockAddr *) o;
  switch (self->sa->sa.sa_family)
    {
    case AF_INET:
      {    
	struct sockaddr_in *sin;
	
	sin = (struct sockaddr_in *) &self->sa->sa;
	if (strcmp(name, "ip") == 0)
	  {
	    if (!PyArg_Parse(value, "i", &sin->sin_addr.s_addr))
	      return -1;
	  }
	else if (strcmp(name, "ip_s") == 0)
	  {
	    gchar *ip;
	    
	    if (!PyArg_Parse(value, "s", &ip))
	      return -1;
	    z_inet_aton(ip, &sin->sin_addr);
	  }
	else if (strcmp(name, "port") == 0)
	  {
	    gint port;
	    if (!PyArg_Parse(value, "i", &port))
	      return -1;

	    sin->sin_port = htons(port);
	  }
#if ENABLE_IPOPTIONS
	else if (strcmp(name, "options") == 0)
	  {
	    gpointer options;
	    guint options_length;

	    /* IP options */
	    if (!PyArg_Parse(value, "s#", &options, &options_length))
	      return -1;
	    z_sockaddr_inet_set_options(self->sa, options, options_length);
	  }
#endif
	break;
      }
    case AF_UNIX:
      break;
    default:
      /*LOG
        This message indicates an internal error, because Zorp tried to
        use an address family it doesn't currently support.
       */
      z_log(NULL, CORE_ERROR, 3, "Internal error in z_py_zorp_sockaddr_set_addr, unknown address family;");
      break;
    }
  return 0;
}

static PyObject *
z_py_zorp_sockaddr_getattr(PyObject *o, char *name)
{
  ZorpSockAddr *self = (ZorpSockAddr *) o;
  
  if (strcmp(name, "family") == 0)
    {
      return PyInt_FromLong(self->sa->sa.sa_family);
    }
  switch (self->sa->sa.sa_family)
    {
    case AF_INET:
      if (strcmp(name, "type") == 0)
        {
          return PyString_FromString("inet");
        }
      else if (strcmp(name, "ip") == 0)
        {
          return PyInt_FromLong(((struct sockaddr_in *) &self->sa->sa)->sin_addr.s_addr);
        }
      else if (strcmp(name, "ip_s") == 0)
        {
          gchar buf[17];
          
          z_inet_ntoa(buf, sizeof(buf), ((struct sockaddr_in *) &self->sa->sa)->sin_addr);
          return PyString_FromString(buf);
        }
      else if (strcmp(name, "port") == 0)
        {
          return PyInt_FromLong(ntohs(((struct sockaddr_in *) &self->sa->sa)->sin_port));
        }
#if ENABLE_IPOPTIONS
      else if (strcmp(name, "options") == 0)
        {
	  gpointer options;
	  guint options_length;

	  z_sockaddr_inet_get_options(self->sa, &options, &options_length);
          if (options)
            return PyString_FromStringAndSize(options, options_length);
          else
            return PyString_FromString("");
        }
#endif
      break;
    case AF_UNIX:
      if (strcmp(name, "type") == 0)
        {
          return PyString_FromString("unix");
        }
      break;
    default:
      /*LOG
        This message indicates an internal error, because Zorp tried to
        use an address family it doesn't currently support.
       */
      z_log(NULL, CORE_ERROR, 3, "Internal error in z_py_zorp_sockaddr_get_addr, unknown address family;");
      break;
    }
  return Py_FindMethod(py_zorp_sockaddr_methods, o, name);
}

PyTypeObject z_py_zorp_sockaddr_type =
{
  PyObject_HEAD_INIT(&PyType_Type)
  0,                                        /*ob_size*/
  "ZorpSockAddr",                           /*tp_name*/
  sizeof(ZorpSockAddr),                     /*tp_basicsize*/
  0,                                        /*tp_itemsize*/
  /* methods */
  (destructor)z_py_zorp_sockaddr_free,      /*tp_dealloc*/
  (printfunc)0,                             /*tp_print*/
  (getattrfunc)z_py_zorp_sockaddr_getattr,  /*tp_getattr*/
  (setattrfunc)z_py_zorp_sockaddr_setattr,  /*tp_setattr*/
  (cmpfunc)0,                               /*tp_compare*/
  (reprfunc)0,                              /*tp_repr*/
  0,                                        /*tp_as_number*/
  0,                   		            /*tp_as_sequence*/
  0,                   		            /*tp_as_mapping*/
  (hashfunc)0,         		            /*tp_hash*/
  (ternaryfunc)0,      		            /*tp_call*/
  (reprfunc)z_py_zorp_sockaddr_format,      /*tp_str*/

  /* Space for future expansion */
  0L,0L,0L,0L,
  "ZorpSockAddr class", /* Documentation string */
};

void
z_py_zorp_sockaddr_init(void)
{
  Py_InitModule("Zorp.SockAddr", z_py_zorp_sockaddr_funcs);
}
