2
0
Эх сурвалжийг харах

- TLS configuration file parser (not yet integrated
with rest of tls module)

Jan Janak 19 жил өмнө
parent
commit
f1eadbe56e

+ 997 - 0
modules/tls/tls_config.c

@@ -0,0 +1,997 @@
+/*
+ * $Id$
+ *
+ * TLS configuration file parser
+ *
+ * Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2004,2005 Free Software Foundation, Inc.
+ * Copyright (C) 2005,2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 <stdio.h>
+#include <libgen.h>
+#include "../../dprint.h"
+#include "../../trim.h"
+#include "../../ut.h"
+#include "tls_config.h"
+#include "tls_domain.h"
+
+#define MAX_TOKEN_LEN 256
+
+
+/*
+ * Parser state
+ */
+static struct {
+	FILE* f;
+	char* file;
+	int line;
+	int col;
+} pstate;
+
+
+/*
+ * Structure representing lexical token
+ */
+typedef struct token {
+	char buf [MAX_TOKEN_LEN];
+	int type;  /* Token type */
+	str val;   /* Token value */
+
+	struct {   /* Position of first and last character of
+		    * token in file
+		    */
+		int line;
+		int col;
+	} start, end;
+} token_t;
+
+
+typedef int (*parser_func_f)(token_t* token);
+
+
+struct parser_tab {
+	str token;
+	union {
+		int ival;
+		str sval;
+		parser_func_f fval;
+	} u;
+};
+
+static struct parser_tab option_name[];
+static struct parser_tab token_method[];
+static struct parser_tab token_bool[];
+static struct parser_tab token_type[];
+static struct parser_tab token_default[];
+
+/*
+ * States of lexical scanner
+ */
+enum st {
+	ST_S,  /* Begin */
+	ST_A,  /* Alphanumeric */
+	ST_AE, /* Alphanumeric escaped */
+	ST_Q,  /* Quoted */
+	ST_QE, /* Quoted escaped */
+	ST_C,  /* Comment */
+	ST_CE, /* Comment escaped */
+	ST_E,  /* Escaped */
+};
+
+
+/* If the lexer finds a token then all characters
+ * untill EOL is scanned would belong to the token
+ */
+#define EXTENDED_ALPHA_TOKEN (1 << 0)
+
+
+/*
+ * Test for alphanumeric characters
+ */
+#define IS_ALPHA(c) \
+    ((c) >= 'a' && (c) <= 'z' || \
+     (c) >= 'A' && (c) <= 'Z' || \
+     (c) >= '0' && (c) <= '9' || \
+     (c) == '_')
+
+
+/*
+ * Test for delimiter characters
+ */
+#define IS_DELIM(c) \
+    ((c) == '=' || \
+     (c) == ':' || \
+     (c) == ';' || \
+     (c) == '.' || \
+     (c) == ',' || \
+     (c) == '?' || \
+     (c) == '[' || \
+     (c) == ']' || \
+     (c) == '/' || \
+     (c) == '@' || \
+     (c) == '!' || \
+     (c) == '$' || \
+     (c) == '%' || \
+     (c) == '&' || \
+     (c) == '*' || \
+     (c) == '(' || \
+     (c) == ')' || \
+     (c) == '-' || \
+     (c) == '+' || \
+     (c) == '|' || \
+     (c) == '\'')
+
+/* Whitespace characters */
+#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r') 
+#define IS_QUOTE(c)      ((c) == '\"')  /* Quote characters */
+#define IS_COMMENT(c)    ((c) == '#')   /* Characters that start comments */
+#define IS_ESCAPE(c)     ((c) == '\\')  /* Escape characters */
+#define IS_EOL(c)        ((c) == '\n')  /* End of line */
+
+
+/*
+ * Append character to the value of current
+ * token
+ */
+#define PUSH(c)                                    \
+    if (token->val.len >= MAX_TOKEN_LEN) {         \
+        ERR("%s:%d:%d: Token too long\n",          \
+	    pstate.file, pstate.line, pstate.col); \
+        return -1;                                 \
+    }                                              \
+    if (token->val.len == 0) {                     \
+         token->start.line = pstate.line;          \
+         token->start.col = pstate.col;            \
+    }                                              \
+    token->val.s[token->val.len++] = (c);
+
+
+/*
+ * Return current token from the lexical analyzer
+ */    
+#define RETURN(c)                  \
+    token->end.line = pstate.line; \
+    token->end.col = pstate.col;   \
+    token->type = (c);             \
+    print_token(token);            \
+    return 1;
+
+
+/*
+ * Get next character and update counters
+ */
+#define READ_CHAR         \
+     c = fgetc(pstate.f); \
+     if (IS_EOL(c)) {     \
+         pstate.line++;   \
+	 pstate.col = 0;  \
+     } else {             \
+	 pstate.col++;    \
+     }
+
+
+enum {
+	TOKEN_EOF = -1,
+	TOKEN_ALPHA = -2,
+	TOKEN_STRING = -3
+};
+
+
+static void print_token(struct token* token)
+{
+	DBG("token(%d, '%.*s', <%d,%d>-<%d,%d>)\n", 
+	    token->type, token->val.len, ZSW(token->val.s),
+	    token->start.line, token->start.col, 
+	    token->end.line, token->end.col);
+}
+
+
+int tls_lex(token_t* token, unsigned int flags)
+{
+	static int look_ahead = EOF;
+	int c;
+	enum st state;
+
+	state = ST_S;
+	
+	token->val.s = token->buf;
+	token->val.len = 0;
+
+	if (look_ahead != EOF) {
+		c = look_ahead;
+		look_ahead = EOF;
+	} else {
+		READ_CHAR;
+	}
+
+	while(c != EOF) {
+		switch(state) {
+		case ST_S:
+			if (flags & EXTENDED_ALPHA_TOKEN) {
+				if (IS_WHITESPACE(c)) {
+					     /* Do nothing */
+				} else if (IS_ALPHA(c) ||
+					   IS_ESCAPE(c) ||
+					   IS_DELIM(c)) {
+					PUSH(c);
+					state = ST_A;
+				} else if (IS_QUOTE(c)) {
+					state = ST_Q;
+				} else if (IS_COMMENT(c)) {
+					state = ST_C;
+				} else if (IS_EOL(c)) {
+					PUSH(c);
+					RETURN(c);
+				} else {
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
+					    pstate.file, pstate.line, pstate.col, c);
+					return -1;
+				}
+			} else {
+				if (IS_WHITESPACE(c)) {
+					     /* Do nothing */
+				} else if (IS_ALPHA(c)) {
+					PUSH(c);
+					state = ST_A;
+				} else if (IS_QUOTE(c)) {
+					state = ST_Q;
+				} else if (IS_COMMENT(c)) {
+					state = ST_C;
+				} else if (IS_ESCAPE(c)) {
+					state = ST_E;
+				} else if (IS_DELIM(c) || IS_EOL(c)) {
+					PUSH(c);
+					RETURN(c);
+				} else {
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
+					    pstate.file, pstate.line, pstate.col, c);
+					return -1;
+				}
+			}
+			break;
+
+		case ST_A:
+			if (flags & EXTENDED_ALPHA_TOKEN) {
+				if (IS_ALPHA(c) ||
+				    IS_DELIM(c) ||
+				    IS_QUOTE(c)) {
+					PUSH(c);
+				} else if (IS_ESCAPE(c)) {
+					state = ST_AE;
+				} else if (IS_COMMENT(c) || IS_EOL(c) || IS_WHITESPACE(c)) {
+					look_ahead = c;
+					RETURN(TOKEN_ALPHA);
+				} else {
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
+					    pstate.file, pstate.line, pstate.col, c);
+					return -1;
+				}
+			} else {
+				if (IS_ALPHA(c)) {
+					PUSH(c);
+				} else if (IS_ESCAPE(c)) {
+					state = ST_AE;
+				} else if (IS_WHITESPACE(c) ||
+					   IS_DELIM(c) ||
+					   IS_QUOTE(c) ||
+					   IS_COMMENT(c) ||
+					   IS_EOL(c)) {
+					look_ahead = c;
+					RETURN(TOKEN_ALPHA);
+				} else {
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
+					    pstate.file, pstate.line, pstate.col, c);
+					return -1;
+				}
+			}
+			break;
+
+		case ST_AE:
+			if (IS_COMMENT(c) ||
+			    IS_QUOTE(c) ||
+			    IS_ESCAPE(c)) {
+				PUSH(c);
+			} else if (c == 'r') {
+				PUSH('\r');
+			} else if (c == 'n') {
+				PUSH('\n');
+			} else if (c == 't') {
+				PUSH('\t');
+			} else if (c == ' ') {
+				PUSH(' ');
+			} else if (IS_EOL(c)) {
+				     /* Do nothing */
+			} else {
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
+				    pstate.file, pstate.line, pstate.col, c);
+				return -1;
+			}
+			state = ST_A;
+			break;
+
+		case ST_Q:
+			if (IS_QUOTE(c)) {
+				RETURN(TOKEN_STRING);
+			} else if (IS_ESCAPE(c)) {
+				state = ST_QE;
+				break;
+			} else {
+				PUSH(c);
+			}
+			break;
+
+		case ST_QE:
+			if (IS_ESCAPE(c) ||
+			    IS_QUOTE(c)) {
+				PUSH(c);
+			} else if (c == 'n') {
+				PUSH('\n');
+			} else if (c == 'r') {
+				PUSH('\r');
+			} else if (c == 't') {
+				PUSH('\t');
+			} else if (IS_EOL(c)) {
+				     /* Do nothing */
+			} else {
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
+				    pstate.file, pstate.line, pstate.col, c);
+				return -1;
+			}
+			state = ST_Q;
+			break;
+
+		case ST_C:
+			if (IS_ESCAPE(c)) {
+				state = ST_CE;
+			} else if (IS_EOL(c)) {
+				state = ST_S;
+				continue; /* Do not read a new char, return EOL */
+			} else {
+				     /* Do nothing */
+			}
+			break;
+
+		case ST_CE:
+			state = ST_C;
+			break;
+
+		case ST_E:
+			if (IS_COMMENT(c) ||
+			    IS_QUOTE(c) ||
+			    IS_ESCAPE(c)) {
+				PUSH(c);
+				RETURN(c);
+			} else if (c == 'r') {
+				PUSH('\r');
+				RETURN('\r');
+			} else if (c == 'n') {
+				PUSH('\n');
+				RETURN('\n');
+			} else if (c == 't') {
+				PUSH('\t');
+				RETURN('\t');
+			} else if (c == ' ') {
+				PUSH(' ');
+				RETURN(' ');
+			} else if (IS_EOL(c)) {
+				     /* Escped eol means no eol */
+				state = ST_S;
+			} else {
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
+				    pstate.file, pstate.line, pstate.col, c);
+				return -1;
+			}
+			break;
+		}
+
+		READ_CHAR;
+	};
+
+	switch(state) {
+	case ST_S: 
+	case ST_C:
+	case ST_CE:
+		return 0;
+
+	case ST_A:
+		RETURN(TOKEN_ALPHA);
+
+	case ST_Q:
+		ERR("%s:%d:%d: Premature end of file, missing closing quote in string constant\n", 
+		    pstate.file, pstate.line, pstate.col, c);
+		return -1;
+
+	case ST_QE:
+	case ST_E:
+	case ST_AE:
+		ERR("%s:%d:%d: Premature end of file, missing escaped character\n", 
+		    pstate.file, pstate.line, pstate.col);
+		return -1;
+	}
+}
+
+
+static struct parser_tab* lookup_token(struct parser_tab* table, str* token)
+{
+	struct parser_tab* ptr;
+
+	ptr = table;
+	while(ptr->token.s && ptr->token.len) {
+		if (token->len == ptr->token.len && 
+		    !strncasecmp(token->s, ptr->token.s, token->len)) {
+			return ptr;
+		}
+		ptr++;
+	}
+}
+
+
+static int parse_string_val(str* res, token_t* token)
+{
+	int ret;
+	token_t t;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
+		    pstate.file, token->start.line, token->start.col);
+		return -1;
+	}
+
+	if (t.type != '=') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n",
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+
+	*res = t.val;;
+	return 0;
+}
+
+
+/*
+ * Parse method option
+ */
+static int parse_method_opt(token_t* token)
+{
+	int ret;
+	token_t t;
+	struct parser_tab* r;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
+		    pstate.file, token->start.line, token->start.col);
+		return -1;
+	}
+
+	if (t.type != '=') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n",
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+
+	r = lookup_token(token_method, &t.val);
+	if (!r) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Parse boolean option value
+ */
+static int parse_bool_val(int* res, token_t* token)
+{
+	int ret;
+	token_t t;
+	struct parser_tab* r;
+	
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+
+	     /* No token or EOL means that the option did not have value, and
+	      * since we are parsing a boolean option we would assume true
+	      */
+	if (ret == 0 || t.type == '\n') {
+		*res = 1;
+		return 0;
+	}
+	
+	if (t.type != '=') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', boolean expected\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+
+	r = lookup_token(token_bool, &t.val);
+	if (!r) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', boolean expected\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+	
+	*res = r->u.ival;
+	return 0;
+
+}
+
+
+static int parse_verify_cert_opt(token_t* token)
+{
+	int ret;
+	return parse_bool_val(&ret, token);
+}
+
+
+static int parse_verify_depth_opt(token_t* token)
+{
+	unsigned int val;
+	int ret;
+	token_t t;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
+		    pstate.file, token->start.line, token->start.col);
+		return -1;
+	}
+
+	if (t.type != '=') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, '=' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	ret = tls_lex(&t, EXTENDED_ALPHA_TOKEN);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Option value missing\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	if (t.type != TOKEN_ALPHA) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid option value '%.*s', number expected\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+
+	if (str2int(&t.val, &val) < 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid number '%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+
+		return -1;
+	}
+	return 0;
+}
+
+
+static int parse_req_cert_opt(token_t* token)
+{
+	str val;
+	return parse_string_val(&val, token);
+}
+
+
+static int parse_pkey_opt(token_t* token)
+{
+	str val;
+	return parse_string_val(&val, token);
+}
+
+
+static int parse_ca_list_opt(token_t* token)
+{
+	str val;
+	return parse_string_val(&val, token);
+}
+
+
+static int parse_cert_opt(token_t* token)
+{
+	str val;
+	return parse_string_val(&val, token);
+}
+
+static int parse_cipher_list_opt(token_t* token)
+{
+	str val;
+	return parse_string_val(&val, token);
+}
+
+static int parse_ipv6(token_t* token)
+{
+	char buf[IP_ADDR_MAX_STR_SIZE];
+	int len, ret;
+	token_t t;
+	struct ip_addr ip;
+
+	ip.af = AF_INET6;
+	ip.len = 16;
+	len = 0;
+
+	while(1) {
+		ret = tls_lex(&t, 0);
+		if (ret <= 0) goto err;
+		if (t.type == ']') break;
+		if (t.type != TOKEN_ALPHA && t.type != ':') goto err;
+		if (len + t.val.len >= IP_ADDR_MAX_STR_SIZE) goto err;
+		memcpy(buf + len, t.val.s, t.val.len);
+		len += t.val.len;
+	}
+	buf[len] = '\0';
+
+	if (inet_pton(ip.af, buf, ip.u.addr) <= 0)  goto err;
+	return 0;
+ err:
+	LOG(L_ERR, "ERROR:%s:%d:%d: Invalid IPv6 address\n", 
+	    pstate.file, token->start.line, token->start.col);
+	return -1;
+}
+
+static int parse_ipv4(token_t* token)
+{
+	struct ip_addr ip;
+	int ret, i;
+	token_t  t;
+	unsigned int v;
+
+	ip.af = AF_INET;
+	ip.len = 4;
+
+	if (str2int(&token->val, &v) < 0) goto err;
+	if (v < 0 || v > 255) goto err;
+
+	ip.u.addr[0] = v;
+
+	for(i = 1; i < 4; i++) {
+		ret = tls_lex(&t, 0);
+		if (ret < 0) return -1;
+		if (ret == 0 || t.type != '.')  goto err;
+		
+		ret = tls_lex(&t, 0);
+		if (ret < 0) return -1;
+		if (ret == 0 || t.type != TOKEN_ALPHA) goto err;
+		if (str2int(&t.val, &v) < 0)  goto err;
+		if (v < 0 || v > 255) goto err;
+		ip.u.addr[i] = v;
+	}
+
+	return 0;
+ err:
+	LOG(L_ERR, "ERROR:%s:%d:%d: Invalid IPv4 address\n", 
+	    pstate.file, token->start.line, token->start.col);
+	return -1;
+}
+
+static int parse_hostport(token_t* token)
+{
+	unsigned int port;
+	int ret;
+	token_t t;
+	struct parser_tab* r;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Missing IP address\n", 
+		    pstate.file, token->start.line, token->start.col);
+		return -1;
+	}
+
+	if (t.type == '[') {
+		if (parse_ipv6(&t) < 0) return -1;
+	} else if (t.type == TOKEN_ALPHA) {
+		r = lookup_token(token_default, &t.val);
+		if (r) {
+			     /* Default domain */
+			return 0;
+		} else {
+			if (parse_ipv4(&t) < 0) return -1;
+		}
+	} else {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, IP address expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	     /* Parse port */
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
+		    pstate.file, pstate.line, pstate.col);
+		return -1;
+	}
+
+	if (t.type != ':') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Premature end of file, port number missing\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+
+	if (t.type != TOKEN_ALPHA || (str2int(&t.val, &port) < 0)) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid port number '%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.val.len, ZSW(t.val.s));
+		return -1;
+	}		
+	return 0;
+}
+
+static int parse_domain(token_t* token)
+{
+	token_t t;
+	int ret;
+	struct parser_tab* r;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: TLS domain type missing\n", 
+		    pstate.file, token->start.line, token->start.col);
+		return -1;
+	}
+
+	if (t.type != TOKEN_ALPHA || 
+	    ((r = lookup_token(token_type, &t.val)) == NULL)) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Invalid TLS domain type %d:'%.*s'\n", 
+		    pstate.file, t.start.line, t.start.col,
+		    t.type, t.val.len, ZSW(t.val.s));
+		return -1;
+	}
+	
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: TLS domain IP address missing\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+	if (t.type != ':') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ':' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	if (parse_hostport(&t) < 0) return -1;
+
+	ret = tls_lex(&t, 0);
+	if (ret < 0) return -1;
+	if (ret == 0) {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Closing ']' missing\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}
+	if (t.type != ']') {
+		LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error, ']' expected\n", 
+		    pstate.file, t.start.line, t.start.col);
+		return -1;
+	}	
+
+	return 0;
+}
+
+static int parse_config(void)
+{
+	int ret;
+	token_t token;
+	struct parser_tab* r;
+
+	while(1) {
+		     /* Get option name */
+		ret = tls_lex(&token, 0);
+		if (ret <= 0) return ret;
+
+		switch(token.type) {
+		case TOKEN_ALPHA:
+			     /* Lookup the option name */
+			r = lookup_token(option_name, &token.val);
+			if (!r) {
+				LOG(L_ERR, "ERROR:%s:%d: Unsupported option '%.*s'\n", 
+				    pstate.file, token.start.line, 
+				    token.val.len, ZSW(token.val.s));
+				return -1;
+			}
+			     /* Parse the option value */
+			if (r->u.fval(&token) < 0) return -1;
+			break;
+
+		case '[': 
+			if (parse_domain(&token) < 0) return -1;
+			break;
+
+			     /* Empty line */
+		case '\n': continue;
+
+		default:
+			LOG(L_ERR, "ERROR:%s:%d:%d: Syntax error\n", 
+			    pstate.file, token.start.line, token.start.col);
+			return -1;
+		}
+
+		     /* Skip EOL */
+		ret = tls_lex(&token, 0);
+		if (ret <= 0) return ret;
+		if (token.type != '\n') {
+			LOG(L_ERR, "ERROR:%s:%d:%d: End of line expected\n", 
+			    pstate.file, token.start.line, token.start.col);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+int tls_load_config(char* filename)
+{
+	str val;
+	struct token token;
+
+	pstate.f = fopen(filename, "r");
+	if (pstate.f == NULL) {
+		ERR("Unable to open TLS config file '%s'\n", filename);
+		return -1;
+	}
+	pstate.file = basename(filename);
+	pstate.line = 1;
+	pstate.col = 0;
+
+	if (parse_config() < 0) goto error;
+
+	fclose(pstate.f);
+	return 0;
+
+ error:
+	fclose(pstate.f);
+	return -1;
+}
+
+
+static struct parser_tab token_type[] = {
+	{STR_STATIC_INIT("server"), {.ival = TLS_DOMAIN_SRV}},
+	{STR_STATIC_INIT("srv"),    {.ival = TLS_DOMAIN_SRV}},
+	{STR_STATIC_INIT("s"),      {.ival = TLS_DOMAIN_SRV}},
+	{STR_STATIC_INIT("client"), {.ival = TLS_DOMAIN_CLI}},
+	{STR_STATIC_INIT("cli"),    {.ival = TLS_DOMAIN_CLI}},
+	{STR_STATIC_INIT("c"),      {.ival = TLS_DOMAIN_CLI}}, 
+	{STR_NULL}
+};
+
+
+static struct parser_tab token_bool[] = { 
+	{STR_STATIC_INIT("yes"),      {.ival = 1}},
+	{STR_STATIC_INIT("true"),     {.ival = 1}},
+	{STR_STATIC_INIT("enable"),   {.ival = 1}},
+	{STR_STATIC_INIT("enabled"),  {.ival = 1}},
+	{STR_STATIC_INIT("1"),        {.ival = 1}},
+	{STR_STATIC_INIT("on"),       {.ival = 1}},
+	{STR_STATIC_INIT("no"),       {.ival = 0}},
+	{STR_STATIC_INIT("false"),    {.ival = 0}},
+	{STR_STATIC_INIT("disable"),  {.ival = 0}},
+	{STR_STATIC_INIT("disabled"), {.ival = 0}},
+	{STR_STATIC_INIT("0"),        {.ival = 0}},
+	{STR_STATIC_INIT("off"),      {.ival = 0}},
+	{STR_NULL}
+};
+
+static struct parser_tab token_default[] = { 
+	{STR_STATIC_INIT("default")},
+	{STR_STATIC_INIT("def")},
+	{STR_STATIC_INIT("*")},
+	{STR_NULL}
+};
+
+static struct parser_tab token_method[] = { 
+	{STR_STATIC_INIT("SSLv2"),  {.ival = TLS_USE_SSLv2}},
+	{STR_STATIC_INIT("SSLv3"),  {.ival = TLS_USE_SSLv3}},
+	{STR_STATIC_INIT("SSLv23"), {.ival = TLS_USE_SSLv23}},
+	{STR_STATIC_INIT("TLSv1"),  {.ival = TLS_USE_TLSv1}},
+	{STR_NULL}
+};
+
+static struct parser_tab option_name[] = {
+	{STR_STATIC_INIT("method"),              {.fval = parse_method_opt}},
+	{STR_STATIC_INIT("tls_method"),          {.fval = parse_method_opt}},
+	{STR_STATIC_INIT("verify_certificate"),  {.fval = parse_verify_cert_opt}},
+	{STR_STATIC_INIT("verify_cert"),         {.fval = parse_verify_cert_opt}},
+	{STR_STATIC_INIT("verify_depth"),        {.fval = parse_verify_depth_opt}},
+	{STR_STATIC_INIT("require_certificate"), {.fval = parse_req_cert_opt}},
+	{STR_STATIC_INIT("require_cert"),        {.fval = parse_req_cert_opt}},
+	{STR_STATIC_INIT("private_key"),         {.fval = parse_pkey_opt}},
+	{STR_STATIC_INIT("pkey_file"),           {.fval = parse_pkey_opt}},
+	{STR_STATIC_INIT("ca_list"),             {.fval = parse_ca_list_opt}},
+	{STR_STATIC_INIT("calist_file"),         {.fval = parse_ca_list_opt}},
+	{STR_STATIC_INIT("certificate"),         {.fval = parse_cert_opt}},
+	{STR_STATIC_INIT("cert_file"),           {.fval = parse_cert_opt}},
+	{STR_STATIC_INIT("cipher_list"),         {.fval = parse_cipher_list_opt}},
+	{STR_NULL}
+};

+ 40 - 0
modules/tls/tls_config.h

@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * TLS configuration file parser
+ *
+ * Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2004,2005 Free Software Foundation, Inc.
+ * Copyright (C) 2005,2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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
+ *
+ */
+
+#ifndef _TLS_CONFIG_H
+#define _TLS_CONFIG_H
+
+#include "../../str.h"
+
+int load_tls_config(char* filename);
+
+#endif /* _TLS_CONFIG_H */

+ 9 - 0
modules/tls/tls_mod.c

@@ -50,8 +50,10 @@
 #include "tls_server.h"
 #include "tls_domain.h"
 #include "tls_select.h"
+#include "tls_config.h"
 #include "tls_mod.h"
 
+
 /*
  * FIXME:
  * - How do we ask for secret key password ? Mod_init is called after
@@ -85,6 +87,8 @@ int tls_conn_timeout = 600;
 int tls_log = 3;
 int tls_session_cache = 0;
 str tls_session_id = STR_STATIC_INIT("ser-tls-0.9.0");
+char* tls_config = 0;
+
 
 /*
  * Exported functions
@@ -112,6 +116,7 @@ static param_export_t params[] = {
 	{"tls_log",             PARAM_INT,                     &tls_log               },
 	{"session_cache",       PARAM_INT,                     &tls_session_cache     },
 	{"session_id",          PARAM_STR,                     &tls_session_id        },
+	{"config",              PARAM_STRING,                  &tls_config            },
 	{0, 0, 0}
 };
 
@@ -153,6 +158,10 @@ transport_t tls_transport = {
 
 static int mod_init(void)
 {
+	if (tls_config) {
+		if (tls_load_config(tls_config) < 0) return -1;
+	}
+
 	if (init_tls() < 0) return -1;
 	
 	tls = &tls_transport;

+ 1 - 0
modules/tls/tls_mod.h

@@ -41,5 +41,6 @@ extern int tls_conn_timeout;
 extern int tls_log;
 extern int tls_session_cache;
 extern str tls_session_id;
+extern char* tls_config;
 
 #endif /* _TLS_MOD_H */