/*
     This file is part of GNUnet.
     Copyright (C) 2010-2014, 2016 GNUnet e.V.

     GNUnet is free software: you can redistribute it and/or modify it
     under the terms of the GNU Affero General Public License as published
     by the Free Software Foundation, either version 3 of the License,
     or (at your option) any later version.

     GNUnet 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
     Affero General Public License for more details.

     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */

/**
 * @file transport/transport_api_blacklist.c
 * @brief library to access the blacklisting functions of the transport service
 * @author Christian Grothoff
 */
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_arm_service.h"
#include "gnunet_hello_lib.h"
#include "gnunet_protocols.h"
#include "gnunet_transport_service.h"
#include "transport.h"

/**
 * Handle for blacklisting requests.
 */
struct GNUNET_TRANSPORT_Blacklist
{
  /**
   * Connection to transport service.
   */
  struct GNUNET_MQ_Handle *mq;

  /**
   * Configuration to use.
   */
  const struct GNUNET_CONFIGURATION_Handle *cfg;

  /**
   * Function to call for determining if a peer is allowed
   * to communicate with us.
   */
  GNUNET_TRANSPORT_BlacklistCallback cb;

  /**
   * Closure for @e cb.
   */
  void *cb_cls;
};


/**
 * Establish blacklist connection to transport service.
 *
 * @param br overall handle
 */
static void
reconnect (struct GNUNET_TRANSPORT_Blacklist *br);


/**
 * Handle blacklist queries.
 *
 * @param cls our overall handle
 * @param bm query
 */
static void
handle_query (void *cls,
              const struct BlacklistMessage *bm)
{
  struct GNUNET_TRANSPORT_Blacklist *br = cls;
  struct GNUNET_MQ_Envelope *env;
  struct BlacklistMessage *res;

  GNUNET_break (0 == ntohl (bm->is_allowed));
  env = GNUNET_MQ_msg (res,
                       GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_REPLY);
  res->is_allowed = htonl (br->cb (br->cb_cls,
                                   &bm->peer));
  res->peer = bm->peer;
  GNUNET_MQ_send (br->mq,
                  env);
}


/**
 * Generic error handler, called with the appropriate error code and
 * the same closure specified at the creation of the message queue.
 * Not every message queue implementation supports an error handler.
 *
 * @param cls closure with the `struct GNUNET_TRANSPORT_Blacklist *`
 * @param error error code
 */
static void
mq_error_handler (void *cls,
                  enum GNUNET_MQ_Error error)
{
  struct GNUNET_TRANSPORT_Blacklist *br = cls;

  reconnect (br);
}


/**
 * Establish blacklist connection to transport service.
 *
 * @param br overall handle
 */
static void
reconnect (struct GNUNET_TRANSPORT_Blacklist *br)
{
  struct GNUNET_MQ_MessageHandler handlers[] = {
    GNUNET_MQ_hd_fixed_size (query,
                             GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY,
                             struct BlacklistMessage,
                             br),
    GNUNET_MQ_handler_end ()
  };
  struct GNUNET_MQ_Envelope *env;
  struct GNUNET_MessageHeader *req;

  if (NULL != br->mq)
    GNUNET_MQ_destroy (br->mq);
  br->mq = GNUNET_CLIENT_connect (br->cfg,
                                  "transport",
                                  handlers,
                                  &mq_error_handler,
                                  br);
  if (NULL == br->mq)
    return;
  env = GNUNET_MQ_msg (req,
                       GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_INIT);
  GNUNET_MQ_send (br->mq,
                  env);
}


/**
 * Install a blacklist callback.  The service will be queried for all
 * existing connections as well as any fresh connections to check if
 * they are permitted.  If the blacklisting callback is unregistered,
 * all hosts that were denied in the past will automatically be
 * whitelisted again.  Cancelling the blacklist handle is also the
 * only way to re-enable connections from peers that were previously
 * blacklisted.
 *
 * @param cfg configuration to use
 * @param cb callback to invoke to check if connections are allowed
 * @param cb_cls closure for @a cb
 * @return NULL on error, otherwise handle for cancellation
 */
struct GNUNET_TRANSPORT_Blacklist *
GNUNET_TRANSPORT_blacklist (const struct GNUNET_CONFIGURATION_Handle *cfg,
                            GNUNET_TRANSPORT_BlacklistCallback cb,
                            void *cb_cls)
{
  struct GNUNET_TRANSPORT_Blacklist *br;

  br = GNUNET_new (struct GNUNET_TRANSPORT_Blacklist);
  br->cfg = cfg;
  br->cb = cb;
  br->cb_cls = cb_cls;
  reconnect (br);
  if (NULL == br->mq)
  {
    GNUNET_free (br);
    return NULL;
  }
  return br;
}


/**
 * Abort the blacklist.  Note that this function is the only way for
 * removing a peer from the blacklist.
 *
 * @param br handle of the request that is to be cancelled
 */
void
GNUNET_TRANSPORT_blacklist_cancel (struct GNUNET_TRANSPORT_Blacklist *br)
{
  GNUNET_MQ_destroy (br->mq);
  GNUNET_free (br);
}


/* end of transport_api_blacklist.c */
