/***************************************************************************
 *
 * Copyright (c) 2000-2015 BalaBit IT Ltd, Budapest, Hungary
 * Copyright (c) 2015-2018 BalaSys 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *
 ***************************************************************************/

#include <zorp/proxy.h>
#include <zorp/policy.h>
#include <zorpll/streamfd.h>
#include <zorpll/streamline.h>
#include <zorpll/streambuf.h>
#include <zorpll/streamssl.h>
#include <zorpll/log.h>
#include <zorpll/io.h>
#include <zorpll/thread.h>
#include <zorp/proxygroup.h>

#include <zorp/policy.h>
#include <zorp/pydict.h>
#include <zorp/pystream.h>
#include <zorp/pysockaddr.h>
#include <zorp/pyproxy.h>
#include <zorp/pydispatch.h>
#include <zorp/szig.h>

#include <stdarg.h>
#include <sys/socket.h>
#include <unistd.h>

/*
 * References between child and parent proxies work as follows
 *
 * When a proxy wants to stack a child proxy a ZStackedProxy structure is
 * created which contains the stream objects towards the child, and also
 * contains references to both the proxy and its child. These references are
 * removed when the ZStackedProxy structure is freed using
 * z_stacked_proxy_destroy.
 *
 * When the child proxy starts up it adds a reference to its parent still in
 * the parent proxy's thread using z_proxy_add_child() in z_proxy_new().
 * This adds a reference from child to parent through its parent_proxy
 * field, and from parent to child through its child_proxies list. This is a
 * circular reference.
 *
 * 1) The child proxy exits first
 *
 *    When the child proxy exits it calls z_proxy_destroy() which in turn
 *    calls z_proxy_set_parent(NULL) which drops the reference to its
 *    parent. The circular reference is now resolved. The parent will detect
 *    that its child exited (probably one of the streams indicates EOF) and
 *    calls z_stacked_proxy_destroy() for the ZStackedProxy structure
 *    associated with this child. It closes and frees streams, and removes
 *    the child proxy from the parent->child_proxies list.
 *
 * 2) The parent proxy exits first
 *
 *    It is assumed that ZStackedProxy structures associated with any child
 *    proxy is freed prior to calling z_proxy_destroy(). This assumption is
 *    valid as ZTransfer or proxy specific transfer code calls
 *    z_stacked_proxy_destroy(), thus the only remaining reference to the
 *    child proxy instance is through child_proxies.
 *
 *    When the parent proxy exits, it calls z_proxy_destroy() which in turn
 *    frees every item on its child_proxies list.
 *
 *  3) The parent and child proxies exit at the same time
 *
 *    In this case the exit is not synchronized by the EOF the parent reads
 *    from the child. Thus z_stacked_proxy_destroy() and z_proxy_destroy()
 *    might race on the following (possibly shared) data accesses:
 *     z_stacked_proxy_destroy:
 *       child is removed from parent->child_list
 *     z_proxy_destroy (parent):
 *       child is removed from parent->child_list (if present)
 *     z_proxy_destroy (child):
 *       child->parent is set to NULL
 *
 * Synchronization during reference counting
 *
 * The general rule is that every proxy instance modifies its own data
 * fields only in order to avoid locking.  The only locks used are the
 * recursive mutexes protecting the reference counts. The only exception to
 * this rule happens when the child proxy starts up and adds itself to its
 * parent's child_proxies list. The synchronization here is also simple as
 * this happens in the parent proxy's thread, thus no locks are necessary.
 *
 * Interface list locking
 *
 * The interface list is manipulated from two threads simoultaneously:
 *   1) the parent might add/remove interface to the list anytime
 *   2) the child queries the list when it wants to communicate with the parent
 *
 * A race might occur when the list is being deleted and the child wants to
 * communicate with the parent (for example: parent exits at the same time
 * the child wants to call set_verdict). This is resolved by using a
 * mutex in ZProxy called interfaces_lock. It is assumed that the
 * ZProxyIface class does not touch the interfaces_lock in its destructor as
 * in that case a deadlock might occur.
 */

/*
 * This hashtable contains the ZProxy instances indexed by their session_id.
 * It is used by the SZIG code to look be able to communicate with actual
 * proxies.
 */
G_LOCK_DEFINE_STATIC(proxy_hash_mutex);
static GHashTable *proxy_hash = NULL;

/**
 * z_proxy_get_service_session_id:
 * @param self the proxy
 *
 * Get the session_id of the service instance where the specified proxy
 * instance belongs to. Each session may contain a stack of proxies, each
 * with a different session id. The first parts of these proxy specific
 * session IDs are the same. This function returns that.
 *
 * For example, the proxy might have the session_id of svc/ssh:0/ssh, the
 * service specific session id is the first two parts, e.g. svc/ssh:0.
 *
 * NOTE: The returned string is duplicated.
 *
 * Returns: th service session id
 *
 */
static gchar *
z_proxy_get_service_session_id(ZProxy *self)
{
  gchar *proxy_session;
  gint len;

  g_assert(self->session_id != NULL);

  proxy_session = strrchr(self->session_id, '/');

  g_assert(proxy_session != NULL);

  len = proxy_session - self->session_id;
  return g_strndup(self->session_id, len);
}

/**
 * z_proxy_register:
 * @param self The proxy to be registered
 *
 * It registers the proxy instance in the proxy_hash, based on its session name.
 */
static void
z_proxy_register(ZProxy *self)
{
  gchar *session_id;
  GList *list = NULL;
  GList *list_new = NULL;

  session_id = z_proxy_get_service_session_id(self);

  G_LOCK(proxy_hash_mutex);

  list = static_cast<GList *>(g_hash_table_lookup(proxy_hash, session_id));

  z_proxy_ref(self);
  list_new = g_list_prepend(list, self);
  if (list_new != list)
    {
      /* NOTE: frees the old session_id in the hash, but leaves the list intact */
      g_hash_table_remove(proxy_hash, session_id);
      g_hash_table_insert(proxy_hash, session_id, list_new);
    }
  else
    g_free(session_id);

  G_UNLOCK(proxy_hash_mutex);
}

/**
 * z_proxy_unregister:
 * @param self the proxy to be unregistered
 *
 * Unregisters the proxy. If the proxy list is no longer used, it is destroyed
 */
static void
z_proxy_unregister(ZProxy *self)
{
  gchar *session_id;
  GList *list, *list_new;

  session_id = z_proxy_get_service_session_id(self);

  G_LOCK(proxy_hash_mutex);
  list = static_cast<GList *>(g_hash_table_lookup(proxy_hash, session_id));
  list_new = g_list_remove(list, self);
  z_proxy_unref(self);

  if (list != list_new)
    {
      g_hash_table_remove(proxy_hash, session_id);

      if (list_new)
        g_hash_table_insert(proxy_hash, session_id, list_new);
      else
        g_free(session_id);
    }
  else
    {
      g_free(session_id);
    }

  G_UNLOCK(proxy_hash_mutex);
}

/**
 * z_proxy_stop_req_cb:
 * @param self proxy instance
 * @param user_data not used
 *
 * Sets the stop request flag for the proxy
 */
static void
z_proxy_stop_req_cb(gpointer s, gpointer  /* user_data */)
{
  ZProxy *self = (ZProxy *)s;

  self->flags |= ZPF_STOP_REQUEST;
  z_proxy_wakeup(self);
}

/**
 * z_proxy_stop_request:
 * @param session_id proxy thread's session_id
 *
 * Sets the stop request flag for each proxy
 *
 * Returns: TRUE if the proxy list found, FALSE otherwise
 *
 */
gboolean
z_proxy_stop_request(const gchar *session_id)
{
  GList *list;
  gboolean verdict = FALSE;

  G_LOCK(proxy_hash_mutex);

  list = static_cast<GList *>(g_hash_table_lookup(proxy_hash, session_id));

  if (list)
    {
      g_list_foreach(list, z_proxy_stop_req_cb, NULL);
      verdict = TRUE;
    }
  G_UNLOCK(proxy_hash_mutex);

  return verdict;
}


/**
 * z_proxy_hash_unref_proxy:
 * @param key not used
 * @param value GList instance
 * @param user_data not used
 *
 * unrefs all proxy in the list
 */
static void
z_proxy_hash_unref_proxy(gpointer  /* key */, gpointer value, gpointer  /* user_data */)
{
  GList *list = static_cast<GList *>(value), *l;

  for (l = list; l; l = l->next)
    z_proxy_unref((ZProxy *) l->data);

  g_list_free(list);
}

/**
 * z_proxy_hash_init:
 *
 * Initalizes the proxy list
 */
void
z_proxy_hash_init(void)
{
  G_LOCK(proxy_hash_mutex);
  proxy_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
  G_UNLOCK(proxy_hash_mutex);
}

/**
 * z_proxy_hash_destroy:
 *
 * Deinitializes proxy list
 */
void
z_proxy_hash_destroy(void)
{
  G_LOCK(proxy_hash_mutex);
  if (proxy_hash)
    {
      g_hash_table_foreach(proxy_hash, z_proxy_hash_unref_proxy, NULL);
      g_hash_table_destroy(proxy_hash);
      proxy_hash = NULL;
    }
  G_UNLOCK(proxy_hash_mutex);
}

/**
 * z_proxy_policy_call_event:
 * @param self this #ZProxy instance
 * @param event the called python event
 *
 * Thiis function call the @event event from current instance.
 *
 * Returns: a boolean value
 */
static gboolean
z_proxy_policy_call_event(ZProxy *self, const gchar *event, const gchar *old_event_name)
{
  ZPolicyObj *res;
  gboolean called;
  /*LOG
    This message reports that Zorp is about to call the proxy's %event() event.
   */
  z_proxy_log(self, CORE_DEBUG, 7, "calling event; %s()", event);
  res = z_policy_call(self->handler, event, NULL, &called, self->session_id);
  if (!called && old_event_name)
    {
      static gboolean obsolete_name_logged = FALSE;

      z_policy_var_unref(res);
      res = z_policy_call(self->handler, old_event_name, NULL, &called, self->session_id);

      if (!obsolete_name_logged && called)
        {
          obsolete_name_logged = TRUE;
          z_proxy_log(self, CORE_POLICY, 0, "Obsolete policy handler in Proxy definition; new_name='%s', old_name='%s'", event, old_event_name);
        }
    }
  if (res == NULL && called)
    {
      z_proxy_report_policy_abort(self);
      z_proxy_leave(self);
      return FALSE;
    }
  z_policy_var_unref(res);
  return TRUE;
}

/**
 * z_proxy_policy_call:
 * @param self this #ZProxy instance
 * @param event the called python event family
 *
 * This function call a python event family.
 * If event named to "event" it's first call
 * __pre_event__. If the call was success it's
 * call event and for last it's call __post_event__.
 *
 * Returns: TRUE if all call is success, FALSE otherwise
 */
static gboolean
z_proxy_policy_call(ZProxy *self, const gchar *event, const gchar *old_event_name)
{
  gchar event_string[512];

  z_proxy_enter(self);

  z_policy_thread_acquire(self->thread);

  g_snprintf(event_string, sizeof(event_string), "__pre_%s__", event);
  if (z_proxy_policy_call_event(self, event_string, NULL))
    {
      if (z_proxy_policy_call_event(self, event, old_event_name))
        {
          g_snprintf(event_string, sizeof(event_string), "__post_%s__", event);
          if (z_proxy_policy_call_event(self, event_string, NULL))
            {
              z_policy_thread_release(self->thread);
              z_proxy_leave(self);
              return TRUE;
            }
        }
    }

  z_policy_thread_release(self->thread);

  z_proxy_leave(self);
  return FALSE;
}

/**
 * z_proxy_policy_config:
 * @param self this ZProxy instance
 *
 * Acquires the thread associated with this Proxy instance and calls
 * the __pre_config__, config and __post_config__ events.
 *
 **/
gboolean
z_proxy_policy_config(ZProxy *self)
{
  z_proxy_enter(self);

  z_proxy_set_state(self, ZPS_CONFIG);
  if (!z_proxy_policy_call(self, "config", NULL))
    {
      z_proxy_leave(self);
      return FALSE;
    }

#if 0
  // FIXME: readd variable dump
  z_policy_thread_acquire(self->thread);
  z_proxy_vars_dump_values(self->vars, self);
  z_policy_thread_release(self->thread);
#endif

  z_policy_thread_acquire(self->thread);
  PyObject *encryption = z_policy_getattr(self->handler, "encryption");
  if (!z_policy_encryption_type_check(encryption))
    {
      z_policy_thread_release(self->thread);
      z_proxy_leave(self);
      return FALSE;
    }

  z_policy_thread_release(self->thread);
  self->encryption = (ZPolicyEncryption *)encryption;
  z_proxy_leave(self);
  return TRUE;
}

/**
 * z_proxy_policy_startup:
 * @param self this ZProxy instance
 *
 * Acquires the thread associated with this TProxy instance and calls
 * the __pre_startup__, startup and __post_startup__ events.
 **/
gboolean
z_proxy_policy_startup(ZProxy *self)
{
  z_proxy_enter(self);
  z_proxy_set_state(self, ZPS_STARTING_UP);

  if (!z_proxy_policy_call(self, "startup", "startUp"))
    {
      z_proxy_leave(self);
      return FALSE;
    }

  z_proxy_set_state(self, ZPS_WORKING);

  z_proxy_leave(self);
  return TRUE;
}

/**
 * z_proxy_policy_shutdown:
 * @param self this ZProxy instance
 *
 * Acquires the thread associated with this TProxy instance and calls
 * the __pre_shutdown__, shutdown and __post_shutdown events.
 **/
void
z_proxy_policy_shutdown(ZProxy *self)
{
  z_proxy_enter(self);
  z_proxy_set_state(self, ZPS_SHUTTING_DOWN);

  z_proxy_policy_call(self, "shutdown", "shutDown");

  z_proxy_leave(self);
}

/**
 * z_proxy_policy_destroy:
 * @param self this ZProxy instance
 *
 * Acquires the thread associated with this TProxy instance and calls
 * the __destroy__ event.
 **/
void
z_proxy_policy_destroy(ZProxy *self)
{
  ZPolicyObj *res;
  gboolean called;

  /* NOTE: this function is also called when thread creation failed, in which case we are unable to call our Python functions */

  z_proxy_enter(self);
  if (z_proxy_get_state(self) > ZPS_THREAD_STARTED)
    {
      /*LOG
        This message reports that Zorp is about to call the proxy's __destroy__() event.
        This method handles the pre destroy tasks, like the shutdown of the server side connection.
       */
      z_proxy_log(self, CORE_DEBUG, 7, "calling __destroy__() event;");
      z_policy_thread_acquire(self->thread);
      res = z_policy_call(self->handler, "__destroy__", NULL, &called, self->session_id);
      z_policy_var_unref(res);
      z_policy_thread_release(self->thread);
      z_proxy_set_state(self, ZPS_DESTROYING);
    }
  z_proxy_leave(self);
}


static void
z_proxy_propagate_channel_props(ZProxy * /* self */)
{
}


static gboolean
z_proxy_set_server_address_no_acquire(ZProxy *self, const gchar *host, gint port)
{
  ZPolicyObj *res = NULL;
  ZPolicyObj *args;
  gint rc = FALSE;
  gboolean called;

  args = z_policy_var_build("(si)", host, port);
  res = z_policy_call(self->handler, "setServerAddress", args, &called, self->session_id);
  if (!res)
    goto out;
  if (!z_policy_var_parse(res, "i", &rc) || !rc)
    goto out;

  rc = TRUE;

 out:
  if (res)
    z_policy_var_unref(res);
  if (!rc)
    z_proxy_report_policy_abort(self);
  return rc;
}

/**
 * Set server address for using by z_proxy_connect_server.
 *
 * @param[in,out] self: ZProxy instance
 * @param[in] host: remote server address
 * @param port: remote server port
 * @return TRUE on success, FALSE otherwise
 */
gboolean
z_proxy_set_server_address(ZProxy *self, const gchar *host, gint port)
{
  gint rc = FALSE;
  z_policy_thread_acquire(self->thread);
  rc = z_proxy_set_server_address_no_acquire(self, host, port);
  z_policy_thread_release(self->thread);
  return rc;
}

/**
 * z_proxy_connect_server:
 * @param self proxy instance
 * @param host host to connect to, used as a hint by the policy layer but may as well be ignored
 * @param port port in host to connect to
 *
 * Send a connectServer event to the associated policy object.  Returns TRUE
 * if the server-side connection is established, otherwise the
 * connection to the client should be closed.
 **/
gboolean
z_proxy_connect_server(ZProxy *self, const gchar *host, gint port)
{
  ZPolicyObj *res;
  gint rc;
  gboolean called;

  z_proxy_enter(self);

  /* It might be possible that we already connected to the server: if
     the SSL handshake order is server-client, we *do* have to connect
     to the server before starting the client handshake. If this is
     the case we simply return the already established connection. */
  if (self->endpoints[EP_SERVER] &&
      !z_stream_broken(self->endpoints[EP_SERVER]))
    {
      z_proxy_log(self, CORE_INFO, 6, "Using already established server connection;");
      z_proxy_return(self, TRUE);
    }

  z_proxy_propagate_channel_props(self);

  if (self->endpoints[EP_SERVER])
    {
      z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_RDWR, NULL);
      z_stream_close(self->endpoints[EP_SERVER], NULL);
      z_stream_unref(self->endpoints[EP_SERVER]);
      self->endpoints[EP_SERVER] = NULL;

      z_proxy_ssl_clear_session(self, EP_SERVER);
    }

  z_policy_thread_acquire(self->thread);
  if (host && host[0] && !z_proxy_set_server_address_no_acquire(self, host, port))
    {
      z_policy_thread_release(self->thread);
      z_proxy_return(self, FALSE);
    }

  res = z_policy_call(self->handler, "connectServer", NULL, &called, self->session_id);

  if (res && z_policy_stream_check(res))
    {
      self->endpoints[EP_SERVER] = z_policy_stream_get_stream(res);
    }
  else
    {
      rc = FALSE;
      goto error;
    }

  z_policy_var_unref(res);
  z_policy_thread_release(self->thread);

  z_proxy_propagate_channel_props(self);

  if (self->endpoints[EP_SERVER])
    rc = z_proxy_ssl_init_stream(self, EP_SERVER);
  else
    rc = FALSE;

  z_proxy_return(self, rc);

 error:
  z_policy_var_unref(res);
  z_policy_thread_release(self->thread);

  z_proxy_propagate_channel_props(self);

  z_proxy_return(self, FALSE);
}

/**
 * Signal that user authentication has been completed.
 *
 * @param self proxy instance
 * @param entity the name of the authenticated entity
 * @param groups list of group names the entity is part of
 * @param type the method how the authentication has been done
 *
 * This function is called by the proxy when it decides that the user is
 * authenticated by some inband authentication method.
 **/
gboolean
z_proxy_user_authenticated(ZProxy *self, const gchar *entity, gchar const **groups, ZProxyUserAuthType type)
{
  ZPolicyObj *res = NULL;
  ZPolicyObj *groups_tuple;
  gboolean called;
  gboolean rc = TRUE;

  z_proxy_enter(self);
  z_policy_thread_acquire(self->thread);

  if (groups)
    {
      groups_tuple = z_policy_convert_strv_to_list(groups);
    }
  else
    {
      groups_tuple = z_policy_none_ref();
    }

  const gchar *auth_info = "";
  switch (type)
    {
      case Z_PROXY_USER_AUTHENTICATED_NONE:
        z_proxy_log(self, CORE_INFO, 6, "Internal problem, NONE authentication should not be used for userAuthenticated function from Zorp; type='%d'", type);
        auth_info = "none";
        break;

      case Z_PROXY_USER_AUTHENTICATED_INBAND:
        auth_info = "inband";
        break;

      case Z_PROXY_USER_AUTHENTICATED_GATEWAY:
        auth_info = "gw-auth";
        break;

      case Z_PROXY_USER_AUTHENTICATED_SERVER:
        auth_info = "server";
        break;
    }

  res = z_policy_call(self->handler, "userAuthenticated", z_policy_var_build("(sOs)", entity, groups_tuple, auth_info), &called, self->session_id);
  z_policy_var_unref(groups_tuple);

  if (!res)
    {
      rc = FALSE;
      z_proxy_report_policy_abort(self);
    }

  z_policy_var_unref(res);
  z_policy_thread_release(self->thread);
  z_proxy_leave(self);
  return rc;
}


/**
 * Query client and server addresses of the proxy without grabbing the Python thread state.
 *
 * @param self proxy instance
 * @param protocol the protocol number (ZD_PROTO_*) is returned here
 * @param client_address the remote address of the client is returned here
 * @param client_local the local address of the connection to the client is returned here
 * @param server_address the remote address of the server is returned here
 * @param server_local the local address of the connection to the server is returned here
 * @param client_listen the address of the listener which initiated this session is returned here
 *
 * This function is used to query the addresses used to connecting the proxy
 * to the client and server. The utilized application protocol is also
 * returned and the listener address which accepted the connection.
 *
 * \note This function assumes that a Python thread state is acquired.
 **/
gboolean
z_proxy_get_addresses_locked(ZProxy *self,
                             guint *protocol,
                             ZSockAddr **client_address, ZSockAddr **client_local,
                             ZSockAddr **server_address, ZSockAddr **server_local,
                             ZDispatchBind **client_listen)
{
  ZPolicyObj *o;

  z_proxy_enter(self);

  if (protocol)
    {
      ZPolicyObj *pyproto;

      pyproto = z_session_getattr(self->handler, "protocol");
      if (PyInt_Check(pyproto))
        *protocol = PyInt_AsLong(pyproto);
      else
        *protocol = ZD_PROTO_TCP;
      z_policy_var_unref(pyproto);
    }

  if (client_address)
    {
      o = z_session_getattr(self->handler, "client_address");
      *client_address = z_policy_sockaddr_get_sa(o);
      z_policy_var_unref(o);
    }

  if (client_local)
    {
      o = z_session_getattr(self->handler, "client_local");
      *client_local = z_policy_sockaddr_get_sa(o);
      z_policy_var_unref(o);
    }

  if (client_listen)
    {
      o = z_session_getattr(self->handler, "client_listen");
      *client_listen = z_policy_dispatch_bind_get_db(o);
      z_policy_var_unref(o);
    }

  if (server_address)
    {
      o = z_session_getattr(self->handler, "server_address");
      *server_address = z_policy_sockaddr_get_sa(o);
      z_policy_var_unref(o);
    }

  if (server_local)
    {
      o = z_session_getattr(self->handler, "server_local");
      *server_local = z_policy_sockaddr_get_sa(o);
      z_policy_var_unref(o);
    }

  z_proxy_leave(self);
  return TRUE;
}

/**
 * Query client and server addresses of the proxy.
 *
 * @param self proxy instance
 * @param protocol the protocol number (ZD_PROTO_*) is returned here
 * @param client_address the remote address of the client is returned here
 * @param client_local the local address of the connection to the client is returned here
 * @param server_address the remote address of the server is returned here
 * @param server_local the local address of the connection to the server is returned here
 * @param client_listen the address of the listener which initiated this session is returned here
 *
 * This function is used to query the addresses used to connecting the proxy
 * to the client and server. The utilized application protocol is also
 * returned and the listener address which accepted the connection.
 *
 * \note This function acquires the thread state associated with self.
 **/
gboolean
z_proxy_get_addresses(ZProxy *self,
                      guint *protocol,
                      ZSockAddr **client_address, ZSockAddr **client_local,
                      ZSockAddr **server_address, ZSockAddr **server_local,
                      ZDispatchBind **client_listen)
{
  gboolean success;
  z_policy_thread_acquire(self->thread);
  success = z_proxy_get_addresses_locked(self, protocol, client_address, client_local, server_address, server_local, client_listen);
  z_policy_thread_release(self->thread);
  return success;
}

/**
 * Set parent proxy reference in a proxy instance.
 *
 * @param self ZProxy instance referring to self
 * @param parent ZProxy instance referring to the parent proxy
 *
 * This function is called to change the reference to the parent proxy.
 * A value of NULL specifies to drop the reference, anything else
 * removes the earlier reference and assigns a new one. See the
 * comment on locking at the beginning of this file for more details.
 *
 * FIXME: either the description here or the code is broken, since it does not seem to modify an already existing reference.
 *
 * @return TRUE if setting the parent proxy was successful.
 **/
gboolean
z_proxy_set_parent(ZProxy *self, ZProxy *parent)
{
  ZProxy *old_parent;

  z_proxy_enter(self);
  if (parent)
    {
      /* establish parent link */
      if (!self->parent_proxy)
        {
          z_proxy_ref(parent);
          self->parent_proxy = parent;
        }
      else
        {
          z_proxy_leave(self);
          return FALSE;
        }
    }
  else
    {
      /* remove parent link */
      if (self->parent_proxy)
        {
          old_parent = self->parent_proxy;
          self->parent_proxy = parent;
          z_proxy_unref(old_parent);
        }
      else
        {
          z_proxy_leave(self);
          return FALSE;
        }
    }
  z_proxy_leave(self);
  return TRUE;
}

/**
 * Add a child proxy instance to the list of child proxies.
 *
 * @param self ZProxy instance referring to self
 * @param child_proxy ZProxy instance to be added to the child list
 *
 * This function adds the specified ZProxy instance to the child_proxies
 * linked list.
 **/
gboolean
z_proxy_add_child(ZProxy *self, ZProxy *child_proxy)
{
  z_enter();
  if (z_proxy_set_parent(child_proxy, self))
    {
      self->child_proxies = g_list_prepend(self->child_proxies, z_proxy_ref(child_proxy));
      z_return(TRUE);
    }
  z_return(FALSE);
}

/**
 * Remove a child proxy from the list of child proxies.
 *
 * @param self ZProxy instance referring to self
 * @param child_proxy ZProxy instance to be deleted from the child_proxies list
 *
 * This function removes @child_proxy from the child_proxies list in @self.
 **/
gboolean
z_proxy_del_child(ZProxy *self, ZProxy *child_proxy)
{
  z_proxy_enter(self);

  self->child_proxies = g_list_remove(self->child_proxies, child_proxy);
  z_proxy_unref(child_proxy);

  z_proxy_leave(self);
  return TRUE;
}

void
z_proxy_set_group(ZProxy *self, ZProxyGroup *group)
{
  self->group = z_proxy_group_ref(group);
}

ZProxyGroup *
z_proxy_get_group(ZProxy *self)
{
  return self->group;
}

/**
 * Register a proxy interface.
 *
 * @param self ZProxy instance
 * @param iface exported interface to add
 *
 * This function adds an exported function interface callable from
 * other proxies to the set of supported interface.
 **/
void
z_proxy_add_iface(ZProxy *self, ZProxyIface *iface)
{
  z_object_ref(&iface->super);
  g_mutex_lock(&self->interfaces_lock);
  self->interfaces = g_list_prepend(self->interfaces, iface);
  g_mutex_unlock(&self->interfaces_lock);
}

/**
 * Unregister a proxy interface.
 *
 * @param self ZProxy instance
 * @param iface exported interface to delete
 *
 * This function deletes the interface specified in @iface from the set of
 * supported interfaces.
 *
 * \note The locking implemented here assumes that the destructor for
 * z_proxy_iface will not touch interfaces lock again.
 **/
void
z_proxy_del_iface(ZProxy *self, ZProxyIface *iface)
{
  g_mutex_lock(&self->interfaces_lock);
  self->interfaces = g_list_remove(self->interfaces, iface);
  g_mutex_unlock(&self->interfaces_lock);
  z_object_unref(&iface->super);
}

/**
 * Look up a proxy interface.
 *
 * @param self ZProxy instance
 * @param compat search for an interface compatible with this class
 *
 * This function iterates on the set of supported interfaces in @self and
 * returns the first compatible with the class specified in @compat.
 **/
ZProxyIface *
z_proxy_find_iface(ZProxy *self, ZClass *compat)
{
  GList *p;

  if (!self)
    return NULL;

  if (!z_object_is_subclass(Z_CLASS(ZProxyIface), compat))
    {
      /*LOG
	This message indicates an internal error, please contact your Zorp support for assistance.
       */
      z_proxy_log(self, CORE_ERROR, 3, "Internal error, trying to look up a non-ZProxyIface compatible interface;");
      return NULL;
    }
  g_mutex_lock(&self->interfaces_lock);
  for (p = self->interfaces; p; p = p->next)
    {
      ZObject *obj;
      ZProxyIface *iface;

      obj = (ZObject *) p->data;
      if (z_object_is_compatible(obj, compat))
        {
          iface = (ZProxyIface *) z_object_ref(obj);
          g_mutex_unlock(&self->interfaces_lock);
          return iface;
        }
    }
  g_mutex_unlock(&self->interfaces_lock);
  return NULL;
}

void
z_proxy_var_register_va(ZProxy *s, ZPolicyDict *dict, const gchar *name, guint flags, va_list args)
{
  guint type = Z_VAR_TYPE(flags);
  flags = flags & 0x0f;
  switch (type)
    {
    case Z_VAR_TYPE_INT:
      z_policy_dict_register(dict,
                             Z_VT_INT, name, flags, va_arg(args, gint *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_INT64:
      z_policy_dict_register(dict,
                             Z_VT_INT64, name, flags, va_arg(args, gint *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_STRING:
      z_policy_dict_register(dict,
                             Z_VT_STRING, name, flags | Z_VF_CONSUME, va_arg(args, GString *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_OBJECT:
      z_policy_dict_register(dict,
                             Z_VT_OBJECT, name, flags | Z_VF_CONSUME, va_arg(args, ZPolicyObj **), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_ALIAS:
      z_policy_dict_register(dict,
                             Z_VT_ALIAS, name, flags, va_arg(args, gchar *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_OBSOLETE:
      z_policy_dict_register(dict,
                             Z_VT_ALIAS, name, flags | Z_VF_OBSOLETE, va_arg(args, gchar *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_METHOD:
      {
        gpointer user_data = va_arg(args, gpointer);
        gpointer method = va_arg(args, gpointer);
        z_policy_dict_register(dict,
                               Z_VT_METHOD, name, flags, method, user_data, NULL, NULL,
                               NULL);
        break;
      }
    case Z_VAR_TYPE_HASH:
      z_policy_dict_register(dict,
                             Z_VT_HASH, name, flags | Z_VF_CONSUME, va_arg(args, GHashTable *), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_DIMHASH:
      z_policy_dict_register(dict,
                             Z_VT_DIMHASH, name, flags | Z_VF_CONSUME, va_arg(args, gpointer), NULL,
                             NULL);
      break;
    case Z_VAR_TYPE_CUSTOM:
      {
        gpointer value = va_arg(args, gpointer);
        gpointer get_value = va_arg(args, gpointer);
        gpointer set_value = va_arg(args, gpointer);
        gpointer free_value = va_arg(args, gpointer);

        z_policy_dict_register(dict,
                               Z_VT_CUSTOM, name, flags,
                                 value, get_value, set_value, free_value,
                                 s, NULL,                // user_data, user_data_free
                                 NULL,                   // end of CUSTOM args
                               NULL);
        break;
      }
    default:
      g_assert(0);
      break;

    }
}

void
z_proxy_var_new(ZProxy *self, const gchar *name, guint flags, ...)
{
  va_list(args);

  va_start(args, flags);
  z_proxy_var_register_va(self, self->dict, name, flags, args);
  va_end(args);
}

/**
 * FIXME: we may want to add functions to manipulate self->endpoints and
 * update self->py_endpoints whenever self->endpoints changes. The
 * implementation here basically assumes that whenever self->endpoints
 * changes the Python layer queries proxy.client_stream or
 * proxy.server_stream. If this is not the case the Python layer may use a
 * stale stream.
 **/
static ZPolicyObj *
z_proxy_query_stream(ZProxy *self, gchar *name, gpointer  /* value */)
{
  ZPolicyObj *res;
  gint side;

  z_proxy_enter(self);
  if (strcmp(name, "client_stream") == 0)
    {
      side = EP_CLIENT;
    }
  else if (strcmp(name, "server_stream") == 0)
    {
      side = EP_SERVER;
    }
  else
    {
      g_assert_not_reached();
    }
  res = self->py_endpoints[side];
  if (!res)
    {
      /* no stream yet in cache */
      if (self->endpoints[side])
        {
          /* but there is one in C side */
          self->py_endpoints[side] = res = z_policy_stream_new(self->endpoints[side]);
        }
      else
        {
          res = z_policy_none;
        }
    }
  else if (((ZPolicyStream *) res)->stream != self->endpoints[side])
    {
      /* the cache is out of sync */
      z_stream_unref(((ZPolicyStream *)res)->stream);
      z_stream_ref(self->endpoints[side]);
      ((ZPolicyStream *)res)->stream = self->endpoints[side];
    }
  z_policy_var_ref(res);
  z_proxy_leave(self);
  return res;
}

/* methods for the ZProxy class */

/**
 * Default config method of ZProxy.
 *
 * @param self ZProxy instance
 *
 * This function is referenced as the default config method for the ZProxy
 * class. It calls the "config" method in the policy.
 *
 * @return FALSE upon failure, and TRUE otherwise.
 **/
gboolean
z_proxy_config_method(ZProxy *self)
{
  z_policy_dict_register(self->dict, Z_VT_INT8, "client_remote_tos", Z_VF_RW, &self->channel_props[EP_CLIENT].tos[EP_DIR_IN], NULL);
  z_policy_dict_register(self->dict, Z_VT_INT8, "client_local_tos", Z_VF_RW, &self->channel_props[EP_CLIENT].tos[EP_DIR_OUT], NULL);
  z_policy_dict_register(self->dict, Z_VT_INT8, "server_remote_tos", Z_VF_RW, &self->channel_props[EP_SERVER].tos[EP_DIR_IN], NULL);
  z_policy_dict_register(self->dict, Z_VT_INT8, "server_local_tos", Z_VF_RW, &self->channel_props[EP_SERVER].tos[EP_DIR_OUT], NULL);
  z_policy_dict_register(self->dict, Z_VT_INT, "server_socket_mark", Z_VF_RW | Z_VF_CFG_RW, &self->server_socket_mark, NULL);

  z_proxy_var_new(self, "language",
                  Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
                  self->language);
  z_proxy_var_new(self, "client_stream",
                  Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
                  NULL, z_proxy_query_stream, NULL, NULL);
  z_proxy_var_new(self, "server_stream",
                  Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
                  NULL, z_proxy_query_stream, NULL, NULL);

  z_proxy_ssl_register_vars(self);

  return z_proxy_policy_config(self);
}

/**
 * Default startup method of ZProxy.
 *
 * @param self ZProxy instance
 *
 * This function is referenced as the default startup method for the ZProxy
 * class. It calls the "startup" method in the policy.
 *
 * @return FALSE upon failure, and TRUE otherwise.
 **/
gboolean
z_proxy_startup_method(ZProxy *self)
{
  return z_proxy_policy_startup(self);
}

/**
 * Default main method of ZProxy.
 *
 * @param self ZProxy instance
 *
 * This function is referenced as the default main method for the ZProxy
 * class. Currently it does nothing and should be overriden in descendant
 * classes.
 **/
void __attribute__((const))
z_proxy_main_method(ZProxy * /* self */)
{
  ;
}

/**
 * Default shutdown method of ZProxy.
 *
 * @param self ZProxy instance
 *
 * This function is referenced as the default shutdown method for the ZProxy
 * class. It calls the "shutdown" method in the policy.
 *
 * @return FALSE upon failure, and TRUE otherwise.
 **/
void
z_proxy_shutdown_method(ZProxy *self)
{
  z_proxy_policy_shutdown(self);
}

/**
 * Default destroy method of ZProxy.
 *
 * @param self proxy instance
 *
 * This function is called from proxy implementation when the proxy is to
 * exit. Frees up associated resources, closes streams, etc. Note that the
 * ZProxy instance is not freed immediately as the reference from Python and
 * the caller still exists. z_proxy_destroy() ensures however that circular
 * references are resolved so the proxy will be freed as soon as those
 * references are dropped.
 **/
void
z_proxy_destroy_method(ZProxy *self)
{
  int i;
  ZPolicyObj *handler;
  ZPolicyThread *thread;
  ZPolicyDict *dict;
  GList *ifaces, *p;
  ZPolicyObj *encryption;

  z_proxy_enter(self);
  z_proxy_policy_destroy(self);

  /* this also removes the link to parent */

  z_proxy_set_parent(self, NULL);
  while (self->child_proxies)
    {
      z_proxy_del_child(self, (ZProxy *) self->child_proxies->data);
    }

  g_mutex_lock(&self->interfaces_lock);
  ifaces = self->interfaces;
  self->interfaces = NULL;
  g_mutex_unlock(&self->interfaces_lock);

  while (ifaces)
    {
      z_object_unref((ZObject *) ifaces->data);
      p = ifaces;
      ifaces = ifaces->next;
      g_list_free_1(p);
    }

  z_proxy_unregister(self);

  thread = self->thread;
  if (z_proxy_get_state(self) > ZPS_THREAD_STARTED)
    {
      for (i = EP_CLIENT; i <= EP_SERVER; i++)
        {
          z_policy_thread_acquire(thread);
          z_policy_var_unref(self->py_endpoints[i]);
          z_policy_thread_release(thread);

          if (self->endpoints[i])
            {
              z_stream_shutdown(self->endpoints[i], SHUT_RDWR, NULL);
              z_stream_close(self->endpoints[i], NULL);
              z_stream_unref(self->endpoints[i]);
              self->endpoints[i] = NULL;
            }
        }

      z_policy_thread_acquire(thread);
      self->thread = NULL;

      z_proxy_ssl_free_vars(self);

      dict = self->dict;
      self->dict = NULL;
      z_policy_dict_unwrap(dict, self->handler);
      z_policy_dict_destroy(dict);

      handler = self->handler;
      self->handler = NULL;
      z_policy_var_unref(handler);

      encryption = (ZPolicyObj *)self->encryption;
      self->encryption = NULL;
      z_policy_var_unref(encryption);

      z_policy_thread_release(thread);
    }
  else
    {
      self->thread = NULL;
    }
  z_policy_thread_destroy(thread);

  z_proxy_leave(self);
}

/**
 * Default run method of ZProxy.
 *
 * @param self ZProxy instance
 *
 * This function is referenced as the default run method for the ZProxy
 * class. It is started by the proxy specific thread and calls the
 * appropriate policy functions (config, startup), then continues by
 * calling z_proxy_main().
 **/
void
z_proxy_run(ZProxy *self)
{
  z_proxy_enter(self);
  if (z_proxy_config(self) &&
      z_proxy_startup(self) &&
      z_proxy_ssl_init_stream(self, EP_CLIENT))
    {
      z_proxy_propagate_channel_props(self);
      z_szig_value_add_thread_id(self);
      z_proxy_main(self);
    }
  z_proxy_shutdown(self);
  z_proxy_destroy(self);
  z_proxy_leave(self);
}

/**
 * Thread function of a proxy.
 *
 * @param s ZProxy instance as a general pointer
 *
 * This is the default thread function for proxies. The thread is started
 * in z_proxy_start().
 **/
static gpointer
z_proxy_thread_func(gpointer s)
{
  ZProxy *self = Z_CAST(s, ZProxy);

  self->proxy_thread = z_thread_self();
  z_proxy_set_state(self, ZPS_THREAD_STARTED);
  z_proxy_run(self);
  z_proxy_unref(self);
  return NULL;
}

/**
 * Create and start a proxy thread and start a proxy in it.
 *
 * @param self ZProxy instance
 * @param proxy_group proxy group to start the proxy in
 *
 * Starts the proxy by creating the new proxy thread. This function
 * is usually called by proxy constructors.
 **/
gboolean
z_proxy_threaded_start(ZProxy *self, ZProxyGroup *proxy_group)
{
  z_proxy_set_group(self, proxy_group);
  z_proxy_ref(self);
  if (!z_thread_new(self->session_id, z_proxy_thread_func, self))
    {
      /*LOG
	This message indicates that Zorp was unable to create a new thread for the new proxy instance.
	It is likely that Zorp reached a thread limit, or not enough resource is available.
       */
      z_proxy_log(self, CORE_ERROR, 2, "Error creating proxy thread;");
      z_proxy_destroy(self);
      z_proxy_unref(self);
      return FALSE;
    }
  return TRUE;
}


gboolean
z_proxy_nonblocking_start(ZProxy *self, ZProxyGroup *proxy_group)
{
  gboolean success;

  z_proxy_set_group(self, proxy_group);
  success = z_proxy_config(self) &&
            z_proxy_startup(self) &&
            z_proxy_ssl_init_stream_nonblocking(self, EP_CLIENT);
  return success;
}

void
z_proxy_nonblocking_stop(ZProxy *self)
{
  z_proxy_nonblocking_deinit(self);

  z_proxy_shutdown(self);
  z_proxy_destroy(self);
  z_proxy_group_stop_session(self->group, self);
}

/**
 * Wake up the main loop of a proxy.
 *
 * @param self ZProxy instance
 *
 * This function should try its best to wake-up the specified ZProxy
 * instance from sleeping, to check for example the stop-request flag.
 *
 * It currently checks whether the proxy is a non-blocking one, and wakes up
 * the associated poll loop.
 *
 * Proxies might override this function to provide additional wakeup methods.
 *
 * NOTE: this runs in a separate thread
 **/
void
z_proxy_wakeup_method(ZProxy *self)
{
  if ((self->flags & ZPF_NONBLOCKING) != 0)
    z_proxy_group_wakeup(self->group);
}

/**
 * Do common tasks that need to be done in each main loop iteration.
 *
 * @param s the proxy instance
 *
 * This function is to be called by proxies in their main loop. Whenever
 * this function returns FALSE the proxy should finish its processing and exit.
 *
 * It currently calls propagate_channel_props and checks the ZPF_STOP_REQUEST flag.
 *
 * @return TRUE if the proxy can continue, FALSE if it has to be stopped
 *
 */
gboolean
z_proxy_loop_iteration(ZProxy *s)
{
  z_proxy_propagate_channel_props(s);

  if (z_proxy_stop_requested(s))
    {
      /*LOG
        A stop request arrived to the proxy. It has to be stopped.
      */
      z_proxy_log(s, CORE_INFO, 2, "User initiated proxy termination request received;");
      return FALSE;
    }
  else
    {
      return TRUE;
    }
}

/**
 * Construct a new proxy instance.
 *
 * @param proxy_class proxy class to instantiate
 * @param params ZProxyParams containing ZProxy parameters
 *
 * This function is to be called from proxy constructors to initialize
 * common fields in the ZProxy struct.
 *
 * \note Unlike in previous versions, z_proxy_new is called with the Python
 * interpreter unlocked, thus it must grab the interpreter lock to create
 * thread specific Python state.
 *
 **/
ZProxy *
z_proxy_new(ZClass *proxy_class, ZProxyParams *params)
{
  ZProxy *self;
  ZProxyIface *iface;
  ZPolicyThread *policy_thread;

  z_enter();
  self = Z_NEW_COMPAT(proxy_class, ZProxy);

  if (params->client)
    {
      self->endpoints[EP_CLIENT] = params->client;
      z_stream_ref(params->client);
    }

  g_strlcpy(self->session_id, params->session_id, sizeof(self->session_id));

 /*
  * Adding mark to the server side socket makes passible to distinguish the
  * client and server side socket in IPTables since they cannot be
  * differenciate from each other by source/destination address/port pair if
  * they are forged. Mark 0x40000000 is used to mark packets which are
  * accepted by kZorp, so the next bit is used.
  */
  self->server_socket_mark = is_kzorp_enabled ? Z_PROXY_SERVER_SOCKET_MARK : 0;
  self->language = g_string_new("en");
  self->dict = z_policy_dict_new();

  g_mutex_init(&self->interfaces_lock);

  iface = (ZProxyIface *) z_proxy_basic_iface_new(Z_CLASS(ZProxyBasicIface), self);
  z_proxy_add_iface(self, iface);
  z_object_unref(&iface->super);

  z_python_lock();
  z_policy_dict_wrap(self->dict, params->handler);
  self->handler = params->handler;
  z_policy_var_ref(params->handler);
  policy_thread = z_policy_thread_self();
  self->thread = z_policy_thread_new(policy_thread ? z_policy_thread_get_policy(policy_thread) : current_policy);
  z_python_unlock();

  z_proxy_register(self);
  z_proxy_ssl_config_defaults(self);

  z_proxy_add_child(params->parent, self);
  z_return(self);
}

/**
 * Free a proxy instance.
 * @param self proxy instance
 *
 * Called when the proxy object is finally to be freed (when the Python layer
 * releases its reference). Calls the proxy specific free function and
 * frees self
 **/
void
z_proxy_free_method(ZObject *s)
{
  ZProxy *self = Z_CAST(s, ZProxy);

  z_enter();
  z_proxy_log(self, CORE_DEBUG, 7, "Freeing ZProxy instance;");
  z_proxy_group_unref(self->group);
  g_mutex_clear(&self->interfaces_lock);
  z_object_free_method(s);
  z_leave();
}

static ZProxyFuncs z_proxy_funcs =
{
  {
    Z_FUNCS_COUNT(ZProxy),
    z_proxy_free_method,
  }, /* super */
  z_proxy_config_method, /* config */
  z_proxy_startup_method, /* startup */
  z_proxy_main_method, /* main */
  z_proxy_shutdown_method, /* shutdown */
  z_proxy_destroy_method, /* destroy */
  NULL, /* nonblocking_init */
  NULL, /* nonblocking_deinit */
  z_proxy_wakeup_method, /* wakeup */
};

Z_CLASS_DEF(ZProxy, ZObject, z_proxy_funcs);

/* ZProxyIface */

/**
 * Constructor for ZProxyIface objects and derivates.
 *
 * @param class derived class description
 * @param proxy proxy instance to be associated with this interface
 *
 * A ZProxyIface class encapsulates a function interface which permits
 * inter-proxy communication.
 **/
ZProxyIface *
z_proxy_iface_new(ZClass *class_, ZProxy *proxy)
{
  ZProxyIface *self;

  self = Z_NEW_COMPAT(class_, ZProxyIface);
  self->owner = z_proxy_ref(proxy);
  return self;
}

/**
 * Destructor for ZProxyIface objects, frees associated references.
 *
 * @param s ZProxyIface instance passed as a ZObject pointer
 **/
void
z_proxy_iface_free_method(ZObject *s)
{
  ZProxyIface *self = Z_CAST(s, ZProxyIface);

  z_proxy_unref(self->owner);
  self->owner = NULL;
  z_object_free_method(s);
}

ZObjectFuncs z_proxy_iface_funcs =
{
  Z_FUNCS_COUNT(ZObject),
  z_proxy_iface_free_method,
};

Z_CLASS_DEF(ZProxyIface, ZObject, z_proxy_iface_funcs);

/* ZProxyBasicIface */

static gboolean
z_proxy_basic_iface_get_var_method(ZProxyBasicIface *self, const gchar *var_name, gchar **value)
{
  ZPolicyObj *value_obj, *value_str;
  ZProxy *owner = self->owner;
  gboolean success = FALSE;

  z_policy_lock(owner->thread);

  value_obj = z_policy_getattr(owner->handler, (gchar *) var_name);
  if (!value_obj)
    goto exit;

  value_str = z_policy_var_str(value_obj);
  g_assert(z_policy_str_check(value_str));
  *value = g_strdup(z_policy_str_as_string(value_str));
  z_policy_var_unref(value_obj);
  z_policy_var_unref(value_str);

  success = TRUE;
 exit:
  z_policy_unlock(owner->thread);

  return success;
}

static gboolean
z_proxy_basic_iface_set_var_method(ZProxyBasicIface * /* self */, const gchar * /* var_name */, gchar * /* value */)
{
  return FALSE;
}

void
z_proxy_report_policy_abort(ZProxy *self)
{
  gboolean called;
  z_policy_call(self->handler, "closedByAbort", NULL, &called, self->session_id);
}

void
z_proxy_report_invalid_policy(ZProxy *self)
{
  gboolean called;
  z_policy_call(self->handler, "invalidPolicyCall", NULL, &called, self->session_id);
}

/**
 * Constructor for ZProxyBasicIface class, derived from ZProxyIface.
 *
 * @param class class description
 * @param proxy associated proxy
 **/
ZProxyBasicIface *
z_proxy_basic_iface_new(ZClass *class_, ZProxy *proxy)
{
  ZProxyBasicIface *self;

  self = (ZProxyBasicIface *) z_proxy_iface_new(class_, proxy);
  return self;
}

ZProxyBasicIfaceFuncs z_proxy_basic_iface_funcs =
{
  {
    Z_FUNCS_COUNT(ZObject),
    NULL
  }, /* super */
  z_proxy_basic_iface_get_var_method, /* get_var */
  z_proxy_basic_iface_set_var_method, /* set_var */
};

Z_CLASS_DEF(ZProxyBasicIface, ZProxyIface, z_proxy_basic_iface_funcs);

ZProxyStackIfaceFuncs z_proxy_stack_iface_funcs =
{
  {
    Z_FUNCS_COUNT(ZObject),
    NULL
  }, /* super */
  NULL, /* get_verdict */
  NULL, /* get_content_hint */
  NULL /* set_content_hint */
};

Z_CLASS_DEF(ZProxyStackIface, ZProxyIface, z_proxy_stack_iface_funcs);

ZProxyHostIfaceFuncs z_proxy_host_iface_funcs =
{
  {
    Z_FUNCS_COUNT(ZObject),
    NULL
  }, /* super */
  NULL, /* check_name */
};

Z_CLASS_DEF(ZProxyHostIface, ZProxyIface, z_proxy_host_iface_funcs);
