Author: sayer
Date: 2008-12-19 20:12:57 +0100 (Fri, 19 Dec 2008)
New Revision: 1213
Modified:
trunk/apps/diameter_client/DiameterClient.cpp
trunk/apps/diameter_client/DiameterClient.h
trunk/apps/diameter_client/Makefile
trunk/apps/diameter_client/ServerConnection.cpp
trunk/apps/diameter_client/ServerConnection.h
trunk/apps/diameter_client/lib_dbase/Makefile
trunk/apps/diameter_client/lib_dbase/diameter_msg.c
trunk/apps/diameter_client/lib_dbase/diameter_msg.h
trunk/apps/diameter_client/lib_dbase/tcp_comm.c
trunk/apps/diameter_client/lib_dbase/tcp_comm.h
trunk/core/ampi/DiameterClientAPI.h
Log:
DIAMETER client with TLS, handling of request timeout/retries, multiple servers,
and bugfixes (closing failed sockets)
Modified: trunk/apps/diameter_client/DiameterClient.cpp
===================================================================
--- trunk/apps/diameter_client/DiameterClient.cpp 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/DiameterClient.cpp 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -1,9 +1,9 @@
/*
* $Id$
*
- * Copyright (C) 2007 iptego GmbH
+ * Copyright (C) 2007-2008 IPTEGO GmbH
*
- * This file is part of SEMS, a free SIP media server.
+ * This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,6 +56,11 @@
}
int DiameterClient::onLoad() {
+ if (tcp_init_tcp()) {
+ ERROR("initializing tcp transport layer.\n");
+ return -1;
+ }
+
DBG("DiameterClient loaded.\n");
return 0;
}
@@ -71,18 +76,31 @@
int app_id = args.get(6).asInt();
int vendor_id = args.get(7).asInt();
string product_name = args.get(8).asCStr();
+ int req_timeout = args.get(9).asInt();
+ string ca_file;
+ string cert_file;
+
+ if (args.size() > 10) {
+ ca_file = args.get(10).asCStr();
+ cert_file = args.get(11).asCStr();
+ }
+
ServerConnection* sc = new ServerConnection();
DBG("initializing new connection for application %s...\n",
app_name.c_str());
sc->init(server_ip, server_port,
+ ca_file, cert_file,
origin_host, origin_realm, origin_ip,
- app_id, vendor_id, product_name);
+ app_id, vendor_id, product_name,
+ req_timeout);
+
DBG("starting new connection...\n");
sc->start();
+
DBG("registering connection...\n");
conn_mut.lock();
- connections.insert(std::make_pair(app_name, sc));
+ connections.insert(make_pair(app_name, sc));
conn_mut.unlock();
ret.push(0);
@@ -130,7 +148,12 @@
AmArg& ret)
{
if(method == "newConnection"){
- args.assertArrayFmt("ssisssiis");
+ if (args.size()==10 /*sizeof("ssisssiisi")*/) {
+ args.assertArrayFmt("ssisssiisi");
+ } else {
+ // plus optional ssl/tls parameters ss
+ args.assertArrayFmt("ssisssiisiss");
+ }
newConnection(args, ret);
} else if(method == "sendRequest"){
args.assertArrayFmt("siias");
Modified: trunk/apps/diameter_client/DiameterClient.h
===================================================================
--- trunk/apps/diameter_client/DiameterClient.h 2008-12-19 18:48:00 UTC (rev
1212)
+++ trunk/apps/diameter_client/DiameterClient.h 2008-12-19 19:12:57 UTC (rev
1213)
@@ -1,9 +1,9 @@
/*
* $Id$
*
- * Copyright (C) 2007 iptego GmbH
+ * Copyright (C) 2007-2008 IPTEGO GmbH
*
- * This file is part of SEMS, a free SIP media server.
+ * This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
#ifndef _DIAMETER_CLIENT_H
#define _DIAMETER_CLIENT_H
Modified: trunk/apps/diameter_client/Makefile
===================================================================
--- trunk/apps/diameter_client/Makefile 2008-12-19 18:48:00 UTC (rev 1212)
+++ trunk/apps/diameter_client/Makefile 2008-12-19 19:12:57 UTC (rev 1213)
@@ -3,9 +3,19 @@
DIAMETER_BASE_LIBDIR = lib_dbase/
DIAMETER_BASE_LIBNAME = lib_dbase.a
+# for TLS support:
+#
+#WITH_OPENSSL = 1
+
module_ldflags =
module_cflags = -I $(DIAMETER_BASE_LIBDIR)
+
+ifdef WITH_OPENSSL
+module_ldflags += -lssl -lcrypto
+module_cflags += -DWITH_OPENSSL
+endif
+
module_extra_objs = $(DIAMETER_BASE_LIBDIR)$(DIAMETER_BASE_LIBNAME)
extra_clean = baseclean
Modified: trunk/apps/diameter_client/ServerConnection.cpp
===================================================================
--- trunk/apps/diameter_client/ServerConnection.cpp 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/ServerConnection.cpp 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -1,9 +1,9 @@
/*
* $Id$
*
- * Copyright (C) 2007 iptego GmbH
+ * Copyright (C) 2007-2008 IPTEGO GmbH
*
- * This file is part of SEMS, a free SIP media server.
+ * This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,15 +42,20 @@
#define CONN_WAIT_MAX 4 // 10 (*50ms = 0.5s)
#define CONN_WAIT_USECS 50000 // 50 ms
-#define MAX_RETRANSMIT_RCV_RETRY 4
+#define CHECK_TIMEOUT_USECS 500000 // 0.5ms
+#define CHECK_TIMEOUT_INTERVAL (CHECK_TIMEOUT_USECS / CONN_WAIT_USECS)
// #define EXTRA_DEBUG
#define CONNECT_CEA_REPLY_TIMEOUT 2 // seconds
#define RETRY_CONNECTION_INTERVAL 2 // seconds
+// after syserror, wait 30 secs for retry
+#define RETRY_CONNECTION_SYSERROR 30 // seconds
+
DiameterServerConnection::DiameterServerConnection()
- : in_use(false), sockfd(-1) {
+ : in_use(false), dia_conn(0)
+{
memset(&rb, 0, sizeof(rd_buf_t));
h2h = random();
e2e = (time(NULL) & 0xFFF << 20) | (random() % 0xFFFFF);
@@ -58,10 +63,11 @@
void DiameterServerConnection::terminate() {
- if (sockfd>0)
- close_tcp_connection(sockfd);
-
- sockfd = -1;
+ if (dia_conn) {
+ tcp_close_connection(dia_conn);
+ tcp_destroy_connection(dia_conn);
+ dia_conn = NULL;
+ }
}
void DiameterServerConnection::setIDs(AAAMessage* msg) {
@@ -92,13 +98,14 @@
gettimeofday(&now, NULL);
req_map_mut.lock();
- req_map[exe] = std::make_pair(re->sess_link, now);
+ req_map[exe] = make_pair(re->sess_link, now);
req_map_mut.unlock();
}
ServerConnection::ServerConnection()
: server_port(-1), open(false),
- AmEventQueue(this)
+ AmEventQueue(this), request_timeout(3000),
+ timeout_check_cntr(0)
{
}
@@ -108,15 +115,20 @@
}
int ServerConnection::init(const string& _server_name,
- int _server_port,
- const string& _origin_host,
- const string& _origin_realm,
- const string& _origin_ip,
- AAAApplicationId _app_id,
- unsigned int _vendorID,
- const string& _product_name) {
+ int _server_port,
+ const string& _ca_file,
+ const string& _cert_file,
+ const string& _origin_host,
+ const string& _origin_realm,
+ const string& _origin_ip,
+ AAAApplicationId _app_id,
+ unsigned int _vendorID,
+ const string& _product_name,
+ int _request_timeout) {
server_name = _server_name;
server_port = _server_port;
+ ca_file = _ca_file;
+ cert_file = _cert_file;
origin_host = _origin_host;
origin_realm = _origin_realm;
origin_ip = _origin_ip;
@@ -124,6 +136,7 @@
app_id = htonl(_app_id);
// todo: separate vendor for client/app
vendorID = htonl(_vendorID);
+ request_timeout = _request_timeout;
memset(origin_ip_address, 0, sizeof(origin_ip_address));
origin_ip_address[0] = 0;
@@ -147,15 +160,21 @@
}
void ServerConnection::openConnection() {
+
DBG("init TCP connection\n");
- int res = init_mytcp(server_name.c_str(), server_port);
- if (res < 0) {
+ if (conn.dia_conn) {
+ ERROR("CRITICAL: trying to open new connection, while current one still"
+ " opened.\n");
+ abort();
+ }
+ conn.dia_conn = tcp_create_connection(server_name.c_str(), server_port,
+ ca_file.c_str(), cert_file.c_str());
+ if (!conn.dia_conn) {
ERROR("establishing connection to %s\n",
server_name.c_str());
setRetryConnectLater();
return;
}
- conn.sockfd = res;
// send CER
AAAMessage* cer;
@@ -195,7 +214,7 @@
(char*)&vendorID, sizeof(vendorID)) ||
(AAAAddAVPToMessage(cer, vs_appid, 0) != AAA_ERR_SUCCESS)
) {
- ERROR( M_NAME":makeConnections(): creating AVP failed."
+ ERROR( M_NAME":openConnection(): creating AVP failed."
" (no more free memory!)\n");
conn.terminate();
setRetryConnectLater();
@@ -209,12 +228,12 @@
conn.setIDs(cer);
if(AAABuildMsgBuffer(cer) != AAA_ERR_SUCCESS) {
- ERROR( " makeConnections(): message buffer not created\n");
+ ERROR( " openConnection(): message buffer not created\n");
AAAFreeMessage(&cer);
return;
}
- int ret = tcp_send(conn.sockfd, cer->buf.s, cer->buf.len);
+ int ret = tcp_send(conn.dia_conn, cer->buf.s, cer->buf.len);
if (ret) {
ERROR( "openConnection(): could not send message\n");
conn.terminate();
@@ -225,16 +244,30 @@
AAAFreeMessage(&cer);
- AAAMessage* cea = NULL;
- res = tcp_recv_reply(conn.sockfd, &conn.rb, &cea,
- CONNECT_CEA_REPLY_TIMEOUT, 0);
- if (res) {
- ERROR( " makeConnections(): did not receive CEA reply.\n");
+ int res = tcp_recv_msg(conn.dia_conn, &conn.rb,
+ CONNECT_CEA_REPLY_TIMEOUT, 0);
+
+ if (res <= 0) {
+ if (!res) {
+ ERROR( " openConnection(): did not receive response (CEA).\n");
+ } else {
+ ERROR( " openConnection(): error receiving response (CEA).\n");
+ }
conn.terminate();
setRetryConnectLater();
- AAAFreeMessage(&cer);
return;
}
+
+ /* obtain the structure corresponding to the message */
+ AAAMessage* cea = AAATranslateMessage(conn.rb.buf, conn.rb.buf_len, 0);
+ if(!cea) {
+ ERROR( " openConnection(): could not decipher response (CEA).\n");
+ conn.terminate();
+ setRetryConnectLater();
+ return;
+ }
+
+ // assume its CEA....
#ifdef EXTRA_DEBUG
if (cea != NULL)
@@ -364,7 +397,7 @@
return AAA_ERROR_MESSAGE;
}
- int ret = tcp_send(conn.sockfd, req->buf.s, req->buf.len);
+ int ret = tcp_send(conn.dia_conn, req->buf.s, req->buf.len);
if (ret) {
ERROR( " sendRequest(): could not send message\n");
AAAFreeMessage(&req);
@@ -405,10 +438,10 @@
}
DBG("sending Device-Watchdog-Answer...\n");
- int ret = tcp_send(conn.sockfd, reply->buf.s, reply->buf.len);
+ int ret = tcp_send(conn.dia_conn, reply->buf.s, reply->buf.len);
if (ret) {
ERROR( " sendRequest(): could not send message\n");
- open = false;
+ closeConnection();
AAAFreeMessage(&reply);
return AAA_ERROR_COMM;
}
@@ -432,12 +465,12 @@
int ServerConnection::handleReply(AAAMessage* rep) {
unsigned int rep_id = rep->endtoendId;
- DBG("received reply - id %d\n", rep_id);
-
+ int reply_code = AAAMessageGetReplyCode(rep);
+ DBG("received reply - id %d, reply code %d\n", rep_id, reply_code);
+
string sess_link = "";
req_map_mut.lock();
- map<unsigned int, pair<string, struct timeval> >::iterator it =
- req_map.find(rep_id);
+ DReqMap::iterator it = req_map.find(rep_id);
if (it != req_map.end()) {
sess_link = it->second.first;
req_map.erase(it);
@@ -452,12 +485,22 @@
AAAMessageAVPs2AmArg(rep));
if (!AmSessionContainer::instance()->postEvent(sess_link, r_ev)) {
DBG("unhandled reply\n");
- }
+ }
+ } else {
+ DBG("no session-link for DIAMETER reply.\n");
}
+ if ((reply_code == AAA_OUT_OF_SPACE)
+ || reply_code >= AAA_PERMANENT_FAILURE_START) {
+ WARN("critical or permanent failure Diameter error reply"
+ " (code %d) received. Shutdown connection.\n",
+ reply_code);
+ shutdownConnection();
+ }
+
return 0;
}
-
+
AmArg ServerConnection::AAAMessageAVPs2AmArg(AAAMessage* rep) {
AmArg res;
for(AAA_AVP* avp=rep->avpList.head;avp;avp=avp->next) {
@@ -472,6 +515,16 @@
return res;
}
+int ServerConnection::AAAMessageGetReplyCode(AAAMessage* rep) {
+ for(AAA_AVP* avp=rep->avpList.head;avp;avp=avp->next) {
+ if (avp->code == AVP_Result_Code) {
+ int res = ntohl(*(uint32_t*)avp->data.s);
+ return res;
+ }
+ }
+ return -1;
+}
+
void ServerConnection::setRetryConnectLater() {
gettimeofday(&connect_ts, NULL);
connect_ts.tv_sec += RETRY_CONNECTION_INTERVAL;
@@ -482,29 +535,36 @@
}
void ServerConnection::receive() {
- AAAMessage *response = NULL;
- int res = tcp_recv_reply(conn.sockfd, &conn.rb, &response,
- 0, CONN_WAIT_USECS);
- if (res) {
- ERROR( " receive(): tcp_recv_reply() failed.\n");
- open = false;
+ int res = tcp_recv_msg(conn.dia_conn, &conn.rb,
+ 0, CONN_WAIT_USECS);
+
+ if (res < 0) {
+ ERROR( M_NAME "receive(): tcp_recv_reply() failed.\n");
+ closeConnection();
+ return;
}
- // nothing received
- if (response == NULL)
+ if (!res) // nothing received
return;
+ /* obtain the structure corresponding to the message */
+ AAAMessage* msg = AAATranslateMessage(conn.rb.buf, conn.rb.buf_len, 0);
+ if(!msg) {
+ ERROR( M_NAME "receive(): message structure not obtained from
message.\n");
+ closeConnection();
+ return;
+ }
#ifdef EXTRA_DEBUG
- AAAPrintMessage(response);
+ AAAPrintMessage(msg);
#endif
- if (is_req(response))
- handleRequest(response);
+ if (is_req(msg))
+ handleRequest(msg);
else
- handleReply(response);
+ handleReply(msg);
- AAAFreeMessage(&response);
+ AAAFreeMessage(&msg);
}
void ServerConnection::run() {
@@ -522,9 +582,78 @@
}
} else {
receive();
+ checkTimeouts();
}
processEvents();
}
}
+void ServerConnection::checkTimeouts() {
+ if ((++timeout_check_cntr)%CHECK_TIMEOUT_INTERVAL)
+ return;
+
+ req_map_mut.lock();
+
+#ifdef EXTRA_DEBUG
+ DBG("checking request timeout of %zd pending requests....\n",
+ req_map.size());
+#endif
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ for (DReqMap::iterator it =
+ req_map.begin();it != req_map.end();) {
+
+ struct timeval diff;
+ timersub(&now,&it->second.second,&diff);
+ // millisec
+ if (diff.tv_sec * 1000 + diff.tv_usec / 1000 > request_timeout) {
+ WARN("timeout for DIAMETER request '%u'\n", it->first);
+ string& sess_link = it->second.first;
+
+ DBG("notify session '%s' of diameter request timeout\n",
+ sess_link.c_str());
+ DiameterTimeoutEvent* r_ev =
+ new DiameterTimeoutEvent(it->first);
+ if (!AmSessionContainer::instance()->postEvent(sess_link, r_ev)) {
+ DBG("unhandled timout event.\n");
+ }
+ DReqMap::iterator d_it = it;
+ it++;
+ req_map.erase(d_it);
+ } else {
+ it++;
+ }
+ }
+
+ req_map_mut.unlock();
+}
+
+void ServerConnection::shutdownConnection() {
+ gettimeofday(&connect_ts, NULL);
+ connect_ts.tv_sec += RETRY_CONNECTION_SYSERROR;
+ closeConnection();
+
+ req_map_mut.lock();
+ DBG("shutdown: posting timeout to %zd pending requests....\n",
+ req_map.size());
+ for (DReqMap::iterator it = req_map.begin();
+ it != req_map.end();it++) {
+ string& sess_link = it->second.first;
+ DiameterTimeoutEvent* r_ev =
+ new DiameterTimeoutEvent(it->first);
+ if (!AmSessionContainer::instance()->postEvent(sess_link, r_ev)) {
+ DBG("unhandled timout event.\n");
+ }
+ }
+ req_map.clear();
+ req_map_mut.unlock();
+}
+
+void ServerConnection::closeConnection() {
+ if (open)
+ conn.terminate();
+ open = false;
+}
Modified: trunk/apps/diameter_client/ServerConnection.h
===================================================================
--- trunk/apps/diameter_client/ServerConnection.h 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/ServerConnection.h 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -1,9 +1,9 @@
/*
* $Id$
*
- * Copyright (C) 2007 iptego GmbH
+ * Copyright (C) 2007-2008 IPTEGO GmbH
*
- * This file is part of SEMS, a free SIP media server.
+ * This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
#ifndef _DIAMETER_SERVER_CONNECTION_H
#define _DIAMETER_SERVER_CONNECTION_H
@@ -75,7 +74,7 @@
struct DiameterServerConnection {
bool in_use;
- int sockfd;
+ dia_tcp_conn* dia_conn; // transport connection
rd_buf_t rb;
string origin_host;
@@ -97,14 +96,22 @@
{
struct timeval connect_ts;
bool open;
+ int timeout_check_cntr;
string server_name;
int server_port;
+
+ // openssl
+ string ca_file;
+ string cert_file;
+
string origin_host;
string origin_realm;
string origin_ip;
AAAApplicationId app_id;
+ int request_timeout; // millisec
+
char origin_ip_address[2+4];// AF and address
// the client
@@ -113,11 +120,12 @@
DiameterServerConnection conn;
- map<unsigned int, pair<string, struct timeval> >
- req_map;
+ typedef map<unsigned int, pair<string, struct timeval> > DReqMap;
+ DReqMap req_map;
AmMutex req_map_mut;
void openConnection();
+ void closeConnection();
int addOrigin(AAAMessage* msg);
@@ -125,6 +133,7 @@
int handleRequest(AAAMessage* req);
AAAMessage* ReqEvent2AAAMessage(DiameterRequestEvent* re);
AmArg AAAMessageAVPs2AmArg(AAAMessage* rep);
+ int AAAMessageGetReplyCode(AAAMessage* rep);
static int addStringAVP(AAAMessage* msg, AAA_AVPCode avp_code, string& val,
bool attail = false);
static int addDataAVP(AAAMessage* msg, AAA_AVPCode avp_code, char* val,
unsigned int len);
@@ -136,17 +145,23 @@
void process(AmEvent*);
void receive();
void setRetryConnectLater();
+ void checkTimeouts();
+ void shutdownConnection();
+
public:
ServerConnection();
~ServerConnection();
int init(const string& _server_name,
int _server_port,
+ const string& _ca_file,
+ const string& _cert_file,
const string& _origin_host,
const string& _origin_realm,
const string& _origin_ip,
AAAApplicationId _app_id,
unsigned int _vendorID,
- const string& _product_name);
+ const string& _product_name,
+ int _request_timeout); // millisec
bool is_open() { return open; }
void run();
Modified: trunk/apps/diameter_client/lib_dbase/Makefile
===================================================================
--- trunk/apps/diameter_client/lib_dbase/Makefile 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/lib_dbase/Makefile 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -12,8 +12,14 @@
objs = $(srcs:.c=.o)
depends = $(srcs:.c=.d)
-cflags ?= $(CFLAGS) -I $(MCOREPATH) -Wall $(module_cflags)
+cflags ?= $(C_FLAGS) -I $(MCOREPATH) -Wall $(module_cflags)
+WITH_OPENSSL = 1
+
+ifdef WITH_OPENSSL
+cflags+= -D WITH_OPENSSL
+endif
+
AR = ar
RANLIB = ranlib
Modified: trunk/apps/diameter_client/lib_dbase/diameter_msg.c
===================================================================
--- trunk/apps/diameter_client/lib_dbase/diameter_msg.c 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/lib_dbase/diameter_msg.c 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -232,7 +232,8 @@
/* check the params */
if( !source || !sourceLen || sourceLen<AAA_MSG_HDR_SIZE) {
- ERROR("ERROR:AAATranslateMessage: invalid buffered received!\n");
+ ERROR("ERROR:AAATranslateMessage: could not decipher "
+ "received message - wrong size (%d)!\n", sourceLen);
goto error;
}
Modified: trunk/apps/diameter_client/lib_dbase/diameter_msg.h
===================================================================
--- trunk/apps/diameter_client/lib_dbase/diameter_msg.h 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/lib_dbase/diameter_msg.h 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -200,6 +200,7 @@
AAA_AUTHENTICATION_REJECTED = 4001,
AAA_OUT_OF_SPACE = 4002,
AAA_ELECTION_LOST = 4003,
+ AAA_PERMANENT_FAILURE_START = 5000,
AAA_AVP_UNSUPPORTED = 5001,
AAA_UNKNOWN_SESSION_ID = 5002,
AAA_AUTHORIZATION_REJECTED = 5003,
Modified: trunk/apps/diameter_client/lib_dbase/tcp_comm.c
===================================================================
--- trunk/apps/diameter_client/lib_dbase/tcp_comm.c 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/lib_dbase/tcp_comm.c 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -58,27 +58,112 @@
#define MAX_TRIES 10
+#define WANT_RW_TIMEOUT_USEC 100000 // 100 ms
+
+void reset_read_buffer(rd_buf_t *rb);
+int do_read(dia_tcp_conn* conn_st, rd_buf_t *p);
+
+#ifdef WITH_OPENSSL
+/* for printing error msg */
+BIO* bio_err = 0;
+
+long tcp_ssl_dbg_cb(BIO *bio, int oper, const char *argp,
+ int argi, long argl, long retvalue) {
+
+ if (oper & BIO_CB_RETURN)
+ return retvalue;
+
+ switch (oper) {
+ case BIO_CB_WRITE: {
+ char buf[256];
+ snprintf(buf, 256, "%s: %s", argp, bio->method->name);
+ INFO("%s", buf);
+ } break;
+
+ case BIO_CB_PUTS: {
+ char buf[2];
+ buf[0] = *argp;
+ buf[1] = '\0';
+ INFO("%s", buf);
+ } break;
+ default: break;
+ }
+
+ return retvalue;
+}
+
+static int password_cb(char *buf,int num,
+ int rwflag,void *userdata) {
+ ERROR("password protected key file.\n"); /* todo? */
+ return 0;
+}
+
+/* Check that the common name matches the
+ host name*/
+int check_cert(SSL * ssl, char* host) {
+ X509 *peer;
+ char peer_CN[256];
+
+ if(SSL_get_verify_result(ssl)!=X509_V_OK) {
+ ERROR("Certificate doesn't verify");
+ return -1;
+ }
+
+ /*Check the cert chain. The chain length
+ is automatically checked by OpenSSL when
+ we set the verify depth in the ctx */
+
+ /*Check the common name*/
+ peer=SSL_get_peer_certificate(ssl);
+ X509_NAME_get_text_by_NID
+ (X509_get_subject_name(peer),
+ NID_commonName, peer_CN, 256);
+ if(strcasecmp(peer_CN,host)) {
+ ERROR("Common name doesn't match host name");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
+
+int tcp_init_tcp() {
+#ifdef WITH_OPENSSL
+ SSL_library_init();
+ SSL_load_error_strings();
+ bio_err = BIO_new(BIO_s_null());
+ BIO_set_callback(bio_err, tcp_ssl_dbg_cb);
+#endif
+ return 0;
+}
+
/* it initializes the TCP connection */
-int init_mytcp(const char* host, int port)
+dia_tcp_conn* tcp_create_connection(const char* host, int port,
+ const char* CA_file, const char*
client_cert_file)
{
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
+#ifdef WITH_OPENSSL
+ SSL_METHOD* meth;
+#endif
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
- ERROR(M_NAME":init_mytcp(): error creating the socket\n");
- return -1;
+ ERROR(M_NAME":init_diatcp(): error creating the socket\n");
+ return 0;
}
server = gethostbyname(host);
if (server == NULL)
{
close(sockfd);
- ERROR( M_NAME":init_mytcp(): error finding the host\n");
- return -1;
+ ERROR( M_NAME":init_diatcp(): error finding the host '%s'\n",
+ host);
+ return 0;
}
memset((char *) &serv_addr, 0, sizeof(serv_addr));
@@ -91,16 +176,79 @@
sizeof(serv_addr)) < 0)
{
close(sockfd);
- ERROR( M_NAME":init_mytcp(): error connecting to the "
- "DIAMETER peer\n");
- return -1;
+ ERROR( M_NAME":init_diatcp(): error connecting to the "
+ "DIAMETER peer '%s'\n", host);
+ return 0;
}
- return sockfd;
-}
+ dia_tcp_conn* conn_st = pkg_malloc(sizeof(dia_tcp_conn));
+ memset(conn_st, 0, sizeof(dia_tcp_conn));
+ conn_st->sockfd = sockfd;
+#ifdef WITH_OPENSSL
+ if (!strlen(CA_file)) {
+ DBG("no CA certificate - not using TLS.\n");
+ return conn_st;
+ }
+ meth=TLSv1_client_method();
+ conn_st->ctx = SSL_CTX_new(meth);
+
+ if (!strlen(client_cert_file)) {
+ DBG("no client certificate - not authenticating client.\n");
+ } else {
+ if (!SSL_CTX_use_certificate_chain_file(conn_st->ctx, client_cert_file)) {
+ ERROR("using certificate from file '%s'\n",
+ client_cert_file);
+ SSL_CTX_free(conn_st->ctx);
+ pkg_free(conn_st);
+ return 0;
+ }
+
+ SSL_CTX_set_default_passwd_cb(conn_st->ctx, password_cb);
+
+ if(!(SSL_CTX_use_PrivateKey_file(conn_st->ctx,
+ client_cert_file,SSL_FILETYPE_PEM))) {
+ ERROR("Loading private key file '%s'\n",
+ client_cert_file);
+ SSL_CTX_free(conn_st->ctx);
+ pkg_free(conn_st);
+ return 0;
+ }
+ }
+
+ /* Load the CAs we trust*/
+ if(!(SSL_CTX_load_verify_locations(conn_st->ctx,
+ CA_file,0))) {
+ ERROR("Loading CA file '%s'\n",
+ CA_file);
+ SSL_CTX_free(conn_st->ctx);
+ pkg_free(conn_st);
+ return 0;
+ }
+
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+ SSL_CTX_set_verify_depth(ctx,1);
+#endif
+
+ conn_st->ssl=SSL_new(conn_st->ctx);
+ conn_st->sbio=BIO_new_socket(sockfd,BIO_NOCLOSE);
+ SSL_set_bio(conn_st->ssl,conn_st->sbio,conn_st->sbio);
+ if(SSL_connect(conn_st->ssl)<=0) {
+ ERROR("in SSL connect\n");
+ SSL_free(conn_st->ssl);
+ SSL_CTX_free(conn_st->ctx);
+ pkg_free(conn_st);
+ return 0;
+ }
+
+#endif
+/* check_cert(ssl,host); */
+
+ return conn_st;
+}
+
void reset_read_buffer(rd_buf_t *rb)
{
rb->ret_code = 0;
@@ -116,8 +264,59 @@
rb->buf = 0;
}
+int tryreceive(dia_tcp_conn* conn_st, unsigned char* ptr, int nwanted) {
+#ifdef WITH_OPENSSL
+ int res;
+ fd_set rw_fd_set;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = WANT_RW_TIMEOUT_USEC;
+
+ if (conn_st->ssl) {
+ while (1) {
+ res = SSL_read(conn_st->ssl, ptr, nwanted);
+ switch(SSL_get_error(conn_st->ssl,res)){
+ case SSL_ERROR_NONE: {
+ return res;
+ }
+
+ case SSL_ERROR_ZERO_RETURN: /* shutdown */
+ DBG("SSL shutdown connection (in SSL_read)\n");
+ return 0;
+
+ case SSL_ERROR_WANT_READ: {
+ FD_ZERO(&rw_fd_set);
+ FD_SET(conn_st->sockfd, &rw_fd_set);
+ res = select (conn_st->sockfd+1, &rw_fd_set, NULL, NULL, &tv);
+ if ( res < 0) {
+ ERROR( M_NAME":SSL_WANT_READ select failed\n");
+ return -1;
+ }
+ } break;
+
+ case SSL_ERROR_WANT_WRITE: {
+ FD_ZERO(&rw_fd_set);
+ FD_SET(conn_st->sockfd, &rw_fd_set);
+ res = select (conn_st->sockfd+1, NULL, &rw_fd_set, NULL, &tv);
+ if ( res < 0) {
+ ERROR( M_NAME":SSL_WANT_WRITE select failed\n");
+ return -1;
+ }
+ } break;
+ default: return 0;
+ }
+ }
+ } else {
+#endif
+ return recv(conn_st->sockfd, ptr, nwanted, MSG_DONTWAIT);
+#ifdef WITH_OPENSSL
+ }
+#endif
+}
+
/* read from a socket, an AAA message buffer */
-int do_read( int socket, rd_buf_t *p)
+int do_read(dia_tcp_conn* conn_st, rd_buf_t *p)
{
unsigned char *ptr;
unsigned int wanted_len, len;
@@ -134,7 +333,7 @@
ptr = p->buf + p->buf_len;
}
- while( (n=recv( socket, ptr, wanted_len, MSG_DONTWAIT ))>0 )
+ while( (n=tryreceive(conn_st, ptr, wanted_len))>0 )
{
// DBG("DEBUG:do_read (sock=%d) -> n=%d (expected=%d)\n",
// p->sock,n,wanted_len);
@@ -154,7 +353,7 @@
if (len<AAA_MSG_HDR_SIZE || len>MAX_AAA_MSG_SIZE)
{
ERROR("ERROR:do_read (sock=%d): invalid message "
- "length read %u (%x)\n", socket, len, p->first_4bytes);
+ "length read %u (%x)\n", conn_st->sockfd, len,
p->first_4bytes);
goto error;
}
//DBG("message length = %d(%x)\n",len,len);
@@ -185,22 +384,39 @@
if (n==0)
{
- INFO("INFO:do_read (sock=%d): FIN received\n", socket);
+ INFO("INFO:do_read (sock=%d): FIN received\n", conn_st->sockfd);
return CONN_CLOSED;
}
if ( n==-1 && errno!=EINTR && errno!=EAGAIN )
{
ERROR("ERROR:do_read (sock=%d): n=%d , errno=%d (%s)\n",
- socket, n, errno, strerror(errno));
+ conn_st->sockfd, n, errno, strerror(errno));
goto error;
}
error:
return CONN_ERROR;
}
+int tcp_send(dia_tcp_conn* conn_st, char* buf, int len) {
+ int n;
+ int sockfd;
+ fd_set rw_fd_set;
+ struct timeval tv;
-int tcp_send(int sockfd, char* buf, int len) {
- int n;
+ tv.tv_sec = 0;
+ tv.tv_usec = WANT_RW_TIMEOUT_USEC;
+
+
+ if (!conn_st) {
+ ERROR("called without conn_st\n");
+ return CONN_ERROR;
+ }
+
+ sockfd = conn_st->sockfd;
+
+#ifdef WITH_OPENSSL
+ if (!conn_st->ssl) {
+#endif
/* try to write the message to the Diameter client */
while( (n=write(sockfd, buf, len))==-1 ) {
if (errno==EINTR)
@@ -213,29 +429,77 @@
ERROR( M_NAME": write gave no error but wrote less than asked\n");
return AAA_ERROR;
}
+#ifdef WITH_OPENSSL
+ } else {
+ while (1) {
+ n = SSL_write(conn_st->ssl, buf, len);
+ switch(SSL_get_error(conn_st->ssl, n)) {
+ case SSL_ERROR_NONE: {
+ if (len != n) {
+ ERROR( M_NAME": write gave no error but wrote less than asked\n");
+ return AAA_ERROR;
+ }
+ return 0;
+ };
+ case SSL_ERROR_ZERO_RETURN: /* shutdown */
+ DBG("SSL shutdown connection (in SSL_write)\n");
+ return 0;
+
+ case SSL_ERROR_WANT_READ: {
+ FD_ZERO(&rw_fd_set);
+ FD_SET(conn_st->sockfd, &rw_fd_set);
+ n=select(conn_st->sockfd+1, &rw_fd_set, NULL, NULL, &tv);
+ if (n < 0) {
+ ERROR( M_NAME":SSL_WANT_READ select failed\n");
+ return -1;
+ }
+ } break; /* try again */
+
+ case SSL_ERROR_WANT_WRITE: {
+ FD_ZERO(&rw_fd_set);
+ FD_SET(conn_st->sockfd, &rw_fd_set);
+ n=select(conn_st->sockfd+1, NULL, &rw_fd_set, NULL, &tv);
+ if (n < 0) {
+ ERROR( M_NAME":SSL_WANT_WRITE select failed\n");
+ return -1;
+ }
+ } break; /* try again */
+ default: {
+ ERROR("SSL write error.\n");
+ return AAA_ERROR;
+ }
+ }
+ }
+ }
+#endif
+
return 0;
}
-int tcp_recv_reply(int sockfd, rd_buf_t* rb, AAAMessage** msg,
- time_t wait_sec, suseconds_t wait_usec) {
+int tcp_recv_msg(dia_tcp_conn* conn_st, rd_buf_t* rb,
+ time_t wait_sec, suseconds_t wait_usec) {
int res;
- fd_set active_fd_set, read_fd_set;
+ fd_set rd_fd_set;
struct timeval tv;
+ int sockfd;
- if (msg == NULL)
- return AAA_ERROR;
+ if (!conn_st) {
+ ERROR("called without conn_st\n");
+ return CONN_ERROR;
+ }
+
+ sockfd = conn_st->sockfd;
/* wait for the answer a limited amount of time */
tv.tv_sec = wait_sec;
tv.tv_usec = wait_usec;
/* Initialize the set of active sockets. */
- FD_ZERO (&active_fd_set);
- FD_SET (sockfd, &active_fd_set);
+ FD_ZERO (&rd_fd_set);
+ FD_SET (sockfd, &rd_fd_set);
- read_fd_set = active_fd_set;
- res = select (sockfd+1, &read_fd_set, NULL, NULL, &tv);
+ res = select (sockfd+1, &rd_fd_set, NULL, NULL, &tv);
if ( res < 0) {
ERROR( M_NAME":tcp_reply_recv(): select function failed\n");
return AAA_ERROR;
@@ -246,7 +510,7 @@
/* Data arriving on a already-connected socket. */
reset_read_buffer(rb);
- switch( do_read(sockfd, rb) )
+ switch( do_read(conn_st, rb) )
{
case CONN_ERROR:
ERROR( M_NAME":tcp_reply_recv(): error when trying to read from
socket\n");
@@ -255,163 +519,31 @@
ERROR( M_NAME":tcp_reply_recv(): connection closed by diameter peer\n");
return AAA_CONN_CLOSED;
}
-
- /* obtain the structure corresponding to the message */
- *msg = AAATranslateMessage(rb->buf, rb->buf_len, 0);
- if(! (*msg)) {
- ERROR( M_NAME":tcp_reply_recv(): message structure not obtained\n");
- return AAA_ERROR;
+ return 1; //received something
+}
+
+void tcp_close_connection(dia_tcp_conn* conn_st)
+{
+ if (!conn_st) {
+ ERROR("called without conn_st\n");
+ return;
}
- return 0;
+ shutdown(conn_st->sockfd, SHUT_RDWR);
+ DBG("closing DIAMETER socket %d\n", conn_st->sockfd);
+ close(conn_st->sockfd);
}
-/* send a message over an already opened TCP connection */
-int tcp_send_recv(int sockfd, char* buf, int len, rd_buf_t* rb,
- unsigned int waited_id)
-{
- int n, number_of_tries;
- fd_set active_fd_set, read_fd_set;
- struct timeval tv;
- unsigned long int result_code;
- AAAMessage *msg;
- AAA_AVP *avp;
- char serviceType;
- unsigned int m_id;
+void tcp_destroy_connection(dia_tcp_conn* conn_st) {
+ if (!conn_st) {
+ ERROR("called without conn_st\n");
+ return;
+ }
- /* try to write the message to the Diameter client */
- while( (n=write(sockfd, buf, len))==-1 )
- {
- if (errno==EINTR)
- continue;
- ERROR( M_NAME": write returned error: %s\n", strerror(errno));
- return AAA_ERROR;
- }
+ if (conn_st->ssl)
+ SSL_free(conn_st->ssl);
+ if (conn_st->ctx)
+ SSL_CTX_free(conn_st->ctx);
- if (n!=len)
- {
- ERROR( M_NAME": write gave no error but wrote less than asked\n");
- return AAA_ERROR;
- }
-
- /* wait for the answer a limited amount of time */
- tv.tv_sec = MAX_WAIT_SEC;
- tv.tv_usec = MAX_WAIT_USEC;
-
- /* Initialize the set of active sockets. */
- FD_ZERO (&active_fd_set);
- FD_SET (sockfd, &active_fd_set);
- number_of_tries = 0;
-
- while(number_of_tries<MAX_TRIES)
- {
- read_fd_set = active_fd_set;
- if (select (sockfd+1, &read_fd_set, NULL, NULL, &tv) < 0)
- {
- ERROR( M_NAME":tcp_send_msg(): select function failed\n");
- return AAA_ERROR;
- }
- /*
- if (!FD_ISSET (sockfd, &read_fd_set))
- {
- ERROR( M_NAME":tcp_send_rcv(): no response message received\n");
- // return AAA_ERROR;
- }
- */
- /* Data arriving on a already-connected socket. */
- reset_read_buffer(rb);
- switch( do_read(sockfd, rb) )
- {
- case CONN_ERROR:
- ERROR( M_NAME": error when trying to read from socket\n");
- return AAA_CONN_CLOSED;
- case CONN_CLOSED:
- ERROR( M_NAME": connection closed by diameter client!\n");
- return AAA_CONN_CLOSED;
- }
-
- /* obtain the structure corresponding to the message */
- msg = AAATranslateMessage(rb->buf, rb->buf_len, 0);
- if(!msg)
- {
- ERROR( M_NAME": message structure not obtained\n");
- return AAA_ERROR;
- }
- avp = AAAFindMatchingAVP(msg, NULL, AVP_SIP_MSGID,
- 0 /* vendorID */, AAA_FORWARD_SEARCH);
- if(!avp)
- {
- ERROR( M_NAME": AVP_SIP_MSGID not found\n");
- return AAA_ERROR;
- }
- m_id = *((unsigned int*)(avp->data.s));
- DBG("######## m_id=%d\n", m_id);
- if(m_id!=waited_id)
- {
- number_of_tries ++;
- DBG(M_NAME": old message received\n");
- continue;
- }
- goto next;
- }
-
- ERROR( M_NAME": too many old messages received\n");
- return AAA_TIMEOUT;
- next:
- /* Finally die correct answer */
- avp = AAAFindMatchingAVP(msg, NULL, AVP_Service_Type,
- 0 /* vendorID */, AAA_FORWARD_SEARCH);
- if(!avp)
- {
- ERROR( M_NAME": AVP_Service_Type not found\n");
- return AAA_ERROR;
- }
- serviceType = avp->data.s[0];
-
- result_code = ntohl(*((unsigned long int*)(msg->res_code->data.s)));
- switch(result_code)
- {
- case AAA_SUCCESS: /* 2001 */
- rb->ret_code = AAA_AUTHORIZED;
- break;
- case AAA_AUTHENTICATION_REJECTED: /* 4001 */
- if(serviceType!=SIP_AUTH_SERVICE)
- {
- rb->ret_code = AAA_NOT_AUTHORIZED;
- break;
- }
- avp = AAAFindMatchingAVP(msg, NULL, AVP_Challenge,
- 0 /* vendorID */, AAA_FORWARD_SEARCH);
- if(!avp)
- {
- ERROR( M_NAME": AVP_Response not found\n");
- rb->ret_code = AAA_SRVERR;
- break;
- }
- rb->chall_len=avp->data.len;
- rb->chall = (unsigned char*)pkg_malloc(avp->data.len*sizeof(char));
- if(rb->chall == NULL)
- {
- ERROR( M_NAME": no more free memory\n");
- rb->ret_code = AAA_SRVERR;
- break;
- }
- memcpy(rb->chall, avp->data.s, avp->data.len);
- rb->ret_code = AAA_CHALENGE;
- break;
- case AAA_AUTHORIZATION_REJECTED: /* 5003 */
- rb->ret_code = AAA_NOT_AUTHORIZED;
- break;
- default: /* error */
- rb->ret_code = AAA_SRVERR;
- }
-
- return rb->ret_code;
+ pkg_free(conn_st);
}
-
-void close_tcp_connection(int sfd)
-{
- shutdown(sfd, 2);
-}
-
-
Modified: trunk/apps/diameter_client/lib_dbase/tcp_comm.h
===================================================================
--- trunk/apps/diameter_client/lib_dbase/tcp_comm.h 2008-12-19 18:48:00 UTC
(rev 1212)
+++ trunk/apps/diameter_client/lib_dbase/tcp_comm.h 2008-12-19 19:12:57 UTC
(rev 1213)
@@ -4,6 +4,7 @@
* Digest Authentication - Diameter support
*
* Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 iptego GmbH
*
* This file is part of ser, a free SIP server.
*
@@ -40,6 +41,10 @@
#include "sys/time.h"
+#ifdef WITH_OPENSSL
+#include <openssl/ssl.h>
+#endif
+
#define MAX_WAIT_SEC 2
#define MAX_WAIT_USEC 0
@@ -49,25 +54,46 @@
#define CONN_ERROR -1
#define CONN_CLOSED -2
+#ifdef __cplusplus
+extern "C" {
+#endif
-void reset_read_buffer(rd_buf_t *rb);
-int do_read( int socket, rd_buf_t *p);
+#ifdef WITH_OPENSSL
+ extern BIO* bio_err;
+#endif
+ struct dia_tcp_conn_t {
+ int sockfd;
+#ifdef WITH_OPENSSL
+ SSL_CTX* ctx;
+ SSL* ssl;
+ BIO* sbio;
+#endif
+ };
-/* it initializes the TCP connection */
-int init_mytcp(const char* host, int port);
+ typedef struct dia_tcp_conn_t dia_tcp_conn;
-/* send a message over an already opened TCP connection */
-int tcp_send(int sockfd, char* buf, int len);
-/* receive reply
- */
-int tcp_recv_reply(int sockfd, rd_buf_t* rb, AAAMessage** msg,
- time_t wait_sec, suseconds_t wait_usec);
+ /* initializes the lib/module */
+ int tcp_init_tcp();
-/* send a message over an already opened TCP connection */
-int tcp_send_recv(int sockfd, char* buf, int len, rd_buf_t* resp,
- unsigned int id);
+ /* initializes the TCP connection */
+ dia_tcp_conn* tcp_create_connection(const char* host, int port,
+ const char* CA_file, const char*
client_cert_file);
-void close_tcp_connection(int sfd);
+ /* send a message over an already opened TCP connection */
+ int tcp_send(dia_tcp_conn* conn_st, char* buf, int len);
+ /* receive reply
+ */
+ int tcp_recv_msg(dia_tcp_conn* conn_st, rd_buf_t* rb,
+ time_t wait_sec, suseconds_t wait_usec);
+
+ void tcp_close_connection(dia_tcp_conn* conn_st);
+
+ void tcp_destroy_connection(dia_tcp_conn* conn_st);
+
+#ifdef __cplusplus
+}
#endif
+
+#endif
Modified: trunk/core/ampi/DiameterClientAPI.h
===================================================================
--- trunk/core/ampi/DiameterClientAPI.h 2008-12-19 18:48:00 UTC (rev 1212)
+++ trunk/core/ampi/DiameterClientAPI.h 2008-12-19 19:12:57 UTC (rev 1213)
@@ -20,6 +20,7 @@
// unsigned int app_id
// unsigned int vendor_id
// string product_name
+// unsigned int timeout // millisec
// sendRequest
@@ -55,4 +56,15 @@
{ }
};
+struct DiameterTimeoutEvent
+ : public AmEvent
+{
+ unsigned int req_id;
+
+ DiameterTimeoutEvent(unsigned int req_id)
+ : AmEvent(1), req_id(req_id)
+ { }
+};
+
+
#endif
_______________________________________________
Semsdev mailing list
[email protected]
http://lists.iptel.org/mailman/listinfo/semsdev