/*
 * Copyright (C) 2016 Tobias Brunner
 *
 * Copyright (C) secunet Security Networks AG
 *
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * 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.
 */

/**
 * This class and singleton object initializes charon and provides helper
 * methods to create unit tests for IKEv2 exchanges.
 *
 * It also registers special implementations for the kernel_ipsec_t interface,
 * the sender and provides dummy configs and credentials.
 *
 * @defgroup exchange_test_helper exchange_test_helper
 * @{ @ingroup test_utils_c
 */

#ifndef EXCHANGE_TEST_HELPER_H_
#define EXCHANGE_TEST_HELPER_H_

#include <daemon.h>

#include "mock_sender.h"

typedef struct exchange_test_helper_t exchange_test_helper_t;
typedef struct exchange_test_sa_conf_t exchange_test_sa_conf_t;

struct exchange_test_helper_t {

	/**
	 * Sender instance used during tests
	 */
	mock_sender_t *sender;

	/**
	 * Set the initial byte of all nonces generated by future nonce
	 * generators (already instantiated nonce generators are not affected).
	 */
	u_char nonce_first_byte;

	/**
	 * Creates an established IKE_SA/CHILD_SA
	 *
	 * @param[out] init		IKE_SA of the initiator
	 * @param[out] resp		IKE_SA of the responder
	 * @param conf			configuration for SAs
	 */
	void (*establish_sa)(exchange_test_helper_t *this, ike_sa_t **init,
						 ike_sa_t **resp, exchange_test_sa_conf_t *conf);

	/**
	 * Similar to establish_sa() but does only create the SA and config
	 * objects, no exchanges are initiated/handled.  The returned child_cfg
	 * object is that created for the initiator to be used for a call to
	 * initiate(). The config objects for the responder are managed and
	 * provided by an internal config backend.
	 *
	 * Note that the responder SPIs are not yet set.
	 *
	 * @param[out] init		IKE_SA of the initiator
	 * @param[out] resp		IKE_SA of the responder
	 * @param conf			configuration for SAs
	 * @return				child_cfg for the initiator
	 */
	child_cfg_t *(*create_sa)(exchange_test_helper_t *this, ike_sa_t **init,
							  ike_sa_t **resp, exchange_test_sa_conf_t *conf);

	/**
	 * Pass a message to the given IKE_SA for processing, setting the IKE_SA on
	 * the bus while processing the message.
	 *
	 * @param ike_sa		the IKE_SA receiving the message
	 * @param message		the message, or NULL to pass the next message in the
	 *						send queue (adopted)
	 * @return				return value from ike_sa_t::process_message()
	 */
	status_t (*process_message)(exchange_test_helper_t *this, ike_sa_t *sa,
								message_t *message);

	/**
	 * Register a listener with the bus.
	 *
	 * Don't use bus_t::add_listener() directly for listeners on the stack
	 * as that could lead to invalid listeners registered when hooks are
	 * triggered during cleanup if a test case fails.  All of the listeners
	 * added this way are unregistered with the bus before cleaning up.
	 *
	 * @param listener		listener to add to the bus
	 */
	void (*add_listener)(exchange_test_helper_t *this, listener_t *listener);
};

struct exchange_test_sa_conf_t {

	/**
	 * Configuration for initiator and responder
	 */
	struct {
		/** IKE proposal */
		char *ike;
		/** ESP proposal */
		char *esp;
		/** Support for childless IKE_SAs */
		childless_t childless;
	} initiator, responder;
};

/**
 * Since we don't use the IKE_SA manager to checkout SAs use this to call a
 * method on the given IKE_SA in its context.
 */
#define call_ikesa(sa, method, ...) ({ \
	charon->bus->set_sa(charon->bus, sa); \
	sa->method(sa, ##__VA_ARGS__); \
	charon->bus->set_sa(charon->bus, NULL); \
})

/**
 * The one and only instance of the helper object.
 *
 * Set between exchange_test_helper_setup() and exchange_test_helper_teardown()
 * calls.
 */
extern exchange_test_helper_t *exchange_test_helper;

/**
 * Initialize charon and the helper object.
 *
 * @param plugins			plugins to load
 */
void exchange_test_helper_init(char *plugins);

/**
 * Deinitialize the helper object.
 */
void exchange_test_helper_deinit();

#endif /** EXCHANGE_TEST_HELPER_H_ @} */
