/*
 * $Id: tls_domain.c 1827 2007-03-12 15:22:53Z bogdan_iancu $
 *
 * Copyright (C) 2001-2003 FhG Fokus
 * Copyright (C) 2004,2005 Free Software Foundation, Inc.
 * Copyright (C) 2006 enum.at
 *
 * This file is part of openser, a free SIP server.
 *
 * openser 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
 *
 * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tls_server.h"
#include "tls_domain.h"
#include <stdlib.h>

struct tls_domain *tls_server_domains = NULL;
struct tls_domain *tls_client_domains = NULL;
struct tls_domain *tls_default_server_domain = NULL;
struct tls_domain *tls_default_client_domain = NULL;

/*
 * find server domain with given ip and port 
 * return default domain if virtual domain not found
 */
struct tls_domain *
tls_find_server_domain(struct ip_addr *ip, unsigned short port)
{
	struct tls_domain *p = tls_server_domains;
	while (p) {
		if ((p->port == port) && ip_addr_cmp(&p->addr, ip)) {
			DBG("tls_find_server_domain: virtual TLS server domain found\n");
			return p;
		}
		p = p->next;
	}
	DBG("tls_find_server_domain: virtual TLS server domain not found, "
		"Using default TLS server domain settings\n");
	return tls_default_server_domain;
}

/*
 * find client domain with given ip and port,
 * return default domain if virtual domain not found
 */
struct tls_domain *
tls_find_client_domain(struct ip_addr *ip, unsigned short port)
{
	struct tls_domain *p = tls_client_domains;
	while (p) {
		if ((p->name.len == 0) && (p->port == port) && ip_addr_cmp(&p->addr, ip)) {
			DBG("tls_find_client_domain: virtual TLS client domain found\n");
			return p;
		}
		p = p->next;
	}
	DBG("tls_find_client_domain: virtual TLS client domain not found, "
		"Using default TLS client domain settings\n");
	return tls_default_client_domain;
}

/*
 * find client domain with given name,
 * return 0 if name based virtual domain not found
 */
struct tls_domain *
tls_find_client_domain_name(str name)
{
	struct tls_domain *p = tls_client_domains;
	while (p) {
		if ((p->name.len == name.len) && !strncasecmp(p->name.s, name.s, name.len)) {
			DBG("tls_find_client_domain_name: virtual TLS client domain found\n");
			return p;
		}
		p = p->next;
	}
	DBG("tls_find_client_domain_name: virtual TLS client domain not found\n");
	return 0;
}


/*
 * create a new server domain (identified by a socket)
 */
int
tls_new_server_domain(struct ip_addr *ip, unsigned short port)
{
	struct tls_domain *d;

	d = tls_new_domain(TLS_DOMAIN_SRV);
	if (d == NULL) {
		LOG(L_ERR, "tls_new_server_domain: Memory allocation failure\n");
		return -1;
	}

	/* fill socket data */
	memcpy(&d->addr, ip, sizeof(struct ip_addr));
	d->port = port;

	/* add this new domain to the linked list */
	d->next = tls_server_domains;
	tls_server_domains = d;
	return 0;
}

/*
 * create a new client domain (identified by a socket)
 */
int
tls_new_client_domain(struct ip_addr *ip, unsigned short port)
{
	struct tls_domain *d;

	d = tls_new_domain(TLS_DOMAIN_CLI);
	if (d == NULL) {
		LOG(L_ERR, "tls_new_client_domain: Memory allocation failure\n");
		return -1;
	}

	/* fill socket data */
	memcpy(&d->addr, ip, sizeof(struct ip_addr));
	d->port = port;

	/* add this new domain to the linked list */
	d->next = tls_client_domains;
	tls_client_domains = d;
	return 0;
}

/*
 * create a new client domain (identified by a string)
 */
int
tls_new_client_domain_name(char *s, int len)
{
	struct tls_domain *d;

	d = tls_new_domain(TLS_DOMAIN_CLI | TLS_DOMAIN_NAME);
	if (d == NULL) {
		LOG(L_ERR, "tls_new_client_domain: Memory allocation failure\n");
		return -1;
	}

	/* initialize name data */
	d->name.s = pkg_malloc(len);
	if (d->name.s == NULL) {
		LOG(L_ERR, "tls_new_client_domain: Memory allocation failure for domain name\n");
		pkg_free(d);
		return -1;
	}
	memcpy(d->name.s, s, len);
	d->name.len = len;

	/* add this new domain to the linked list */
	d->next = tls_client_domains;
	tls_client_domains = d;
	return 0;
}

/*
 * allocate memory and set default values for
 * TLS domain structure
 */
struct tls_domain *tls_new_domain(int type)
{
	struct tls_domain *d;

	d = pkg_malloc(sizeof(struct tls_domain));
	if (d == NULL) {
		LOG(L_ERR,"ERROR:tls:pre_init_tls_domain: memory allocation failed\n");
		return 0;
	}
	memset( d, 0, sizeof(struct tls_domain));

	d->type = type;

	if (type & TLS_DOMAIN_SRV) {
		d->verify_cert         = tls_verify_client_cert;
		d->require_client_cert = tls_require_client_cert;
	} else {
		d->verify_cert         = tls_verify_server_cert;
		d->require_client_cert = 0;
	}
	d->method = TLS_METHOD_UNSPEC;

	return d;
}

/*
 * clean up 
 */
void
tls_free_domains(void)
{
	struct tls_domain *p;
	while (tls_server_domains) {
		p = tls_server_domains;
		tls_server_domains = tls_server_domains->next;
		pkg_free(p);
	}
	while (tls_client_domains) {
		p = tls_client_domains;
		tls_client_domains = tls_client_domains->next;
		/* ToDo: If socket based client domains will be implemented, the name may
		   be empty (must be set to NULL manually). Thus no need to free it */
		if (p->name.s) {
			pkg_free(p->name.s);
		}
		pkg_free(p);
	}
	pkg_free(tls_default_client_domain);
	pkg_free(tls_default_server_domain);
}

