created tnc_ifmap2_soap_msg class
This commit is contained in:
parent
b885c3cde6
commit
cd13c9a90f
|
@ -20,7 +20,8 @@ libstrongswan_tnc_ifmap2_la_LIBADD = \
|
|||
libstrongswan_tnc_ifmap2_la_SOURCES = \
|
||||
tnc_ifmap2_plugin.h tnc_ifmap2_plugin.c \
|
||||
tnc_ifmap2_listener.h tnc_ifmap2_listener.c \
|
||||
tnc_ifmap2_soap.h tnc_ifmap2_soap.c
|
||||
tnc_ifmap2_soap.h tnc_ifmap2_soap.c \
|
||||
tnc_ifmap2_soap_msg.h tnc_ifmap2_soap_msg.c
|
||||
|
||||
libstrongswan_tnc_ifmap2_la_LDFLAGS = -module -avoid-version
|
||||
|
||||
|
|
|
@ -13,28 +13,20 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for asprintf() */
|
||||
|
||||
#include "tnc_ifmap2_soap.h"
|
||||
#include "tnc_ifmap2_soap_msg.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/lexparser.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
#include <daemon.h>
|
||||
|
||||
#include <tls_socket.h>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define SOAP_NS "http://www.w3.org/2003/05/soap-envelope"
|
||||
#define IFMAP_NS "http://www.trustedcomputinggroup.org/2010/IFMAP/2"
|
||||
#define IFMAP_META_NS "http://www.trustedcomputinggroup.org/2010/IFMAP-METADATA/2"
|
||||
#define IFMAP_LOGFILE "strongswan_ifmap.log"
|
||||
|
@ -90,206 +82,11 @@ struct private_tnc_ifmap2_soap_t {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Send HTTP POST request and receive HTTP response
|
||||
*/
|
||||
static bool http_send_receive(private_tnc_ifmap2_soap_t *this, chunk_t out,
|
||||
chunk_t *in)
|
||||
{
|
||||
char header[] =
|
||||
"POST /ifmap HTTP/1.1\r\n"
|
||||
"Content-Type: application/soap+xml;charset=utf-8\r\n"
|
||||
"Content-Length: ";
|
||||
char *request, response[2048];
|
||||
chunk_t line, http, parameter;
|
||||
int len, code, content_len = 0;
|
||||
|
||||
/* Write HTTP POST request */
|
||||
len = asprintf(&request, "%s%d\r\n\r\n%.*s", header, out.len,
|
||||
out.len, out.ptr);
|
||||
if (len == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->tls->write(this->tls, request, len);
|
||||
free(request);
|
||||
|
||||
/* Read HTTP response */
|
||||
len = this->tls->read(this->tls, response, sizeof(response), TRUE);
|
||||
if (len == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*in = chunk_create(response, len);
|
||||
|
||||
/* Process HTTP protocol version */
|
||||
if (!fetchline(in, &line) || !extract_token(&http, ' ', &line) ||
|
||||
!match("HTTP/1.1", &http) || sscanf(line.ptr, "%d", &code) != 1)
|
||||
{
|
||||
DBG1(DBG_TNC, "malformed http response header");
|
||||
return FALSE;
|
||||
}
|
||||
if (code != 200)
|
||||
{
|
||||
DBG1(DBG_TNC, "http response returns error code %d", code);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Process HTTP header line by line until the HTTP body is reached */
|
||||
while (fetchline(in, &line))
|
||||
{
|
||||
if (line.len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (extract_token(¶meter, ':', &line) &&
|
||||
match("Content-Length", ¶meter) &&
|
||||
sscanf(line.ptr, "%d", &len) == 1)
|
||||
{
|
||||
content_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Found Content-Length parameter and check size of HTTP body */
|
||||
if (content_len)
|
||||
{
|
||||
if (content_len > in->len)
|
||||
{
|
||||
DBG1(DBG_TNC, "http body is smaller than content length");
|
||||
return FALSE;
|
||||
}
|
||||
in->len = content_len;
|
||||
}
|
||||
*in = chunk_clone(*in);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a child node with a given name
|
||||
*/
|
||||
static xmlNodePtr find_child(xmlNodePtr parent, const xmlChar* name)
|
||||
{
|
||||
xmlNodePtr child;
|
||||
|
||||
child = parent->xmlChildrenNode;
|
||||
while (child)
|
||||
{
|
||||
if (xmlStrcmp(child->name, name) == 0)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
DBG1(DBG_TNC, "child node \"%s\" not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request and receive result via SOAP
|
||||
*/
|
||||
static bool soap_send_receive(private_tnc_ifmap2_soap_t *this,
|
||||
char *request_name, xmlNodePtr request,
|
||||
char *result_name, xmlNodePtr *result,
|
||||
xmlDocPtr *result_doc)
|
||||
{
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr env, body, cur;
|
||||
xmlNsPtr ns;
|
||||
xmlChar *xml;
|
||||
int len;
|
||||
chunk_t in, out;
|
||||
|
||||
*result_doc = NULL;
|
||||
DBG2(DBG_TNC, "sending ifmap %s", request_name);
|
||||
|
||||
/* Generate XML Document containing SOAP Envelope */
|
||||
doc = xmlNewDoc("1.0");
|
||||
env =xmlNewNode(NULL, "Envelope");
|
||||
ns = xmlNewNs(env, SOAP_NS, "env");
|
||||
xmlSetNs(env, ns);
|
||||
xmlDocSetRootElement(doc, env);
|
||||
|
||||
/* Add SOAP Body containing IF-MAP request */
|
||||
body = xmlNewNode(ns, "Body");
|
||||
xmlAddChild(body, request);
|
||||
xmlAddChild(env, body);
|
||||
|
||||
/* Convert XML Document into a character string */
|
||||
xmlDocDumpFormatMemory(doc, &xml, &len, 1);
|
||||
xmlFreeDoc(doc);
|
||||
DBG3(DBG_TNC, "%.*s", len, xml);
|
||||
out = chunk_create(xml, len);
|
||||
|
||||
/* Send SOAP-XML request via HTTP */
|
||||
if (!http_send_receive(this, out, &in))
|
||||
{
|
||||
xmlFree(xml);
|
||||
return FALSE;
|
||||
}
|
||||
xmlFree(xml);
|
||||
|
||||
DBG3(DBG_TNC, "%B", &in);
|
||||
doc = xmlParseMemory(in.ptr, in.len);
|
||||
free(in.ptr);
|
||||
|
||||
if (!doc)
|
||||
{
|
||||
DBG1(DBG_TNC, "failed to parse XML message");
|
||||
return FALSE;
|
||||
}
|
||||
*result_doc = doc;
|
||||
|
||||
/* check out XML document */
|
||||
cur = xmlDocGetRootElement(doc);
|
||||
if (!cur)
|
||||
{
|
||||
DBG1(DBG_TNC, "empty XML message");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get XML Document type is a SOAP Envelope */
|
||||
if (xmlStrcmp(cur->name, "Envelope"))
|
||||
{
|
||||
DBG1(DBG_TNC, "XML message does not contain a SOAP Envelope");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get SOAP Body */
|
||||
cur = find_child(cur, "Body");
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get IF-MAP response */
|
||||
cur = find_child(cur, "response");
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get IF-MAP result */
|
||||
cur = find_child(cur, result_name);
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
*result = cur;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tnc_ifmap2_soap_t, newSession, bool,
|
||||
private_tnc_ifmap2_soap_t *this)
|
||||
{
|
||||
tnc_ifmap2_soap_msg_t *soap_msg;
|
||||
xmlNodePtr request, result;
|
||||
xmlDocPtr result_doc;
|
||||
xmlNsPtr ns;
|
||||
|
||||
/*build newSession request */
|
||||
|
@ -297,20 +94,18 @@ METHOD(tnc_ifmap2_soap_t, newSession, bool,
|
|||
ns = xmlNewNs(request, IFMAP_NS, "ifmap");
|
||||
xmlSetNs(request, ns);
|
||||
|
||||
if (!soap_send_receive(this, "newSession", request, "newSessionResult",
|
||||
&result, &result_doc))
|
||||
soap_msg = tnc_ifmap2_soap_msg_create(this->tls);
|
||||
if (!soap_msg->post(soap_msg, "newSession", request,
|
||||
"newSessionResult", &result))
|
||||
{
|
||||
if (result_doc)
|
||||
{
|
||||
xmlFreeDoc(result_doc);
|
||||
}
|
||||
soap_msg->destroy(soap_msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get session-id and ifmap-publisher-id properties */
|
||||
this->session_id = xmlGetProp(result, "session-id");
|
||||
this->ifmap_publisher_id = xmlGetProp(result, "ifmap-publisher-id");
|
||||
xmlFreeDoc(result_doc);
|
||||
soap_msg->destroy(soap_msg);
|
||||
|
||||
DBG1(DBG_TNC, "session-id: %s, ifmap-publisher-id: %s",
|
||||
this->session_id, this->ifmap_publisher_id);
|
||||
|
@ -327,8 +122,8 @@ METHOD(tnc_ifmap2_soap_t, newSession, bool,
|
|||
METHOD(tnc_ifmap2_soap_t, purgePublisher, bool,
|
||||
private_tnc_ifmap2_soap_t *this)
|
||||
{
|
||||
tnc_ifmap2_soap_msg_t *soap_msg;
|
||||
xmlNodePtr request;
|
||||
xmlDocPtr result_doc;
|
||||
xmlNsPtr ns;
|
||||
bool success;
|
||||
|
||||
|
@ -339,12 +134,11 @@ METHOD(tnc_ifmap2_soap_t, purgePublisher, bool,
|
|||
xmlNewProp(request, "session-id", this->session_id);
|
||||
xmlNewProp(request, "ifmap-publisher-id", this->ifmap_publisher_id);
|
||||
|
||||
success = soap_send_receive(this, "purgePublisher", request,
|
||||
"purgePublisherReceived", NULL, &result_doc);
|
||||
if (result_doc)
|
||||
{
|
||||
xmlFreeDoc(result_doc);
|
||||
}
|
||||
soap_msg = tnc_ifmap2_soap_msg_create(this->tls);
|
||||
success = soap_msg->post(soap_msg, "purgePublisher", request,
|
||||
"purgePublisherReceived", NULL);
|
||||
soap_msg->destroy(soap_msg);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Andreas Steffen
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for asprintf() */
|
||||
|
||||
#include "tnc_ifmap2_soap_msg.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/lexparser.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define SOAP_NS "http://www.w3.org/2003/05/soap-envelope"
|
||||
|
||||
typedef struct private_tnc_ifmap2_soap_msg_t private_tnc_ifmap2_soap_msg_t;
|
||||
|
||||
/**
|
||||
* Private data of an tnc_ifmap2_soap_msg_t object.
|
||||
*/
|
||||
struct private_tnc_ifmap2_soap_msg_t {
|
||||
|
||||
/**
|
||||
* Public tnc_ifmap2_soap_msg_t interface.
|
||||
*/
|
||||
tnc_ifmap2_soap_msg_t public;
|
||||
|
||||
/**
|
||||
* TLS Socket
|
||||
*/
|
||||
tls_socket_t *tls;
|
||||
|
||||
/**
|
||||
* XML Document
|
||||
*/
|
||||
xmlDocPtr doc;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Send HTTP POST request and receive HTTP response
|
||||
*/
|
||||
static bool http_send_receive(private_tnc_ifmap2_soap_msg_t *this,
|
||||
chunk_t out, chunk_t *in)
|
||||
{
|
||||
char header[] =
|
||||
"POST /ifmap HTTP/1.1\r\n"
|
||||
"Content-Type: application/soap+xml;charset=utf-8\r\n"
|
||||
"Content-Length: ";
|
||||
char *request, response[2048];
|
||||
chunk_t line, http, parameter;
|
||||
int len, code, content_len = 0;
|
||||
|
||||
/* Write HTTP POST request */
|
||||
len = asprintf(&request, "%s%d\r\n\r\n%.*s", header, out.len,
|
||||
out.len, out.ptr);
|
||||
if (len == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->tls->write(this->tls, request, len);
|
||||
free(request);
|
||||
|
||||
/* Read HTTP response */
|
||||
len = this->tls->read(this->tls, response, sizeof(response), TRUE);
|
||||
if (len == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*in = chunk_create(response, len);
|
||||
|
||||
/* Process HTTP protocol version */
|
||||
if (!fetchline(in, &line) || !extract_token(&http, ' ', &line) ||
|
||||
!match("HTTP/1.1", &http) || sscanf(line.ptr, "%d", &code) != 1)
|
||||
{
|
||||
DBG1(DBG_TNC, "malformed http response header");
|
||||
return FALSE;
|
||||
}
|
||||
if (code != 200)
|
||||
{
|
||||
DBG1(DBG_TNC, "http response returns error code %d", code);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Process HTTP header line by line until the HTTP body is reached */
|
||||
while (fetchline(in, &line))
|
||||
{
|
||||
if (line.len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (extract_token(¶meter, ':', &line) &&
|
||||
match("Content-Length", ¶meter) &&
|
||||
sscanf(line.ptr, "%d", &len) == 1)
|
||||
{
|
||||
content_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Found Content-Length parameter and check size of HTTP body */
|
||||
if (content_len)
|
||||
{
|
||||
if (content_len > in->len)
|
||||
{
|
||||
DBG1(DBG_TNC, "http body is smaller than content length");
|
||||
return FALSE;
|
||||
}
|
||||
in->len = content_len;
|
||||
}
|
||||
*in = chunk_clone(*in);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a child node with a given name
|
||||
*/
|
||||
static xmlNodePtr find_child(xmlNodePtr parent, const xmlChar* name)
|
||||
{
|
||||
xmlNodePtr child;
|
||||
|
||||
child = parent->xmlChildrenNode;
|
||||
while (child)
|
||||
{
|
||||
if (xmlStrcmp(child->name, name) == 0)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
DBG1(DBG_TNC, "child node \"%s\" not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(tnc_ifmap2_soap_msg_t, post, bool,
|
||||
private_tnc_ifmap2_soap_msg_t *this, char *request_name, xmlNodePtr request,
|
||||
char *result_name, xmlNodePtr *result)
|
||||
{
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr env, body, cur;
|
||||
xmlNsPtr ns;
|
||||
xmlChar *xml;
|
||||
int len;
|
||||
chunk_t in, out;
|
||||
|
||||
DBG2(DBG_TNC, "sending ifmap %s", request_name);
|
||||
|
||||
/* Generate XML Document containing SOAP Envelope */
|
||||
doc = xmlNewDoc("1.0");
|
||||
env =xmlNewNode(NULL, "Envelope");
|
||||
ns = xmlNewNs(env, SOAP_NS, "env");
|
||||
xmlSetNs(env, ns);
|
||||
xmlDocSetRootElement(doc, env);
|
||||
|
||||
/* Add SOAP Body containing IF-MAP request */
|
||||
body = xmlNewNode(ns, "Body");
|
||||
xmlAddChild(body, request);
|
||||
xmlAddChild(env, body);
|
||||
|
||||
/* Convert XML Document into a character string */
|
||||
xmlDocDumpFormatMemory(doc, &xml, &len, 1);
|
||||
xmlFreeDoc(doc);
|
||||
DBG3(DBG_TNC, "%.*s", len, xml);
|
||||
out = chunk_create(xml, len);
|
||||
|
||||
/* Send SOAP-XML request via HTTP */
|
||||
if (!http_send_receive(this, out, &in))
|
||||
{
|
||||
xmlFree(xml);
|
||||
return FALSE;
|
||||
}
|
||||
xmlFree(xml);
|
||||
|
||||
DBG3(DBG_TNC, "%B", &in);
|
||||
this->doc = xmlParseMemory(in.ptr, in.len);
|
||||
free(in.ptr);
|
||||
|
||||
if (!this->doc)
|
||||
{
|
||||
DBG1(DBG_TNC, "failed to parse XML message");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check out XML document */
|
||||
cur = xmlDocGetRootElement(this->doc);
|
||||
if (!cur)
|
||||
{
|
||||
DBG1(DBG_TNC, "empty XML message");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get XML Document type is a SOAP Envelope */
|
||||
if (xmlStrcmp(cur->name, "Envelope"))
|
||||
{
|
||||
DBG1(DBG_TNC, "XML message does not contain a SOAP Envelope");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get SOAP Body */
|
||||
cur = find_child(cur, "Body");
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get IF-MAP response */
|
||||
cur = find_child(cur, "response");
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* get IF-MAP result */
|
||||
cur = find_child(cur, result_name);
|
||||
if (!cur)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
*result = cur;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tnc_ifmap2_soap_msg_t, destroy, void,
|
||||
private_tnc_ifmap2_soap_msg_t *this)
|
||||
{
|
||||
if (this->doc)
|
||||
{
|
||||
xmlFreeDoc(this->doc);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tnc_ifmap2_soap_msg_t *tnc_ifmap2_soap_msg_create(tls_socket_t *tls)
|
||||
{
|
||||
private_tnc_ifmap2_soap_msg_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.post = _post,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.tls = tls,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Andreas Steffen
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tnc_ifmap2_soap_msg tnc_ifmap2_soap_msg
|
||||
* @{ @ingroup tnc_ifmap2
|
||||
*/
|
||||
|
||||
#ifndef TNC_IFMAP2_SOAP_MSG_H_
|
||||
#define TNC_IFMAP2_SOAP_MSG_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <tls_socket.h>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
|
||||
typedef struct tnc_ifmap2_soap_msg_t tnc_ifmap2_soap_msg_t;
|
||||
|
||||
/**
|
||||
* Interface for sending and receiving SOAP-XML messages
|
||||
*/
|
||||
struct tnc_ifmap2_soap_msg_t {
|
||||
|
||||
/**
|
||||
* Post an IF-MAP request in a SOAP-XML message and return a result
|
||||
*
|
||||
* @param request_name name of the IF-MAP request
|
||||
* @param request XML-encoded IF-MAP request
|
||||
* @param result_name name of the IF-MAP result
|
||||
* @param result XML-encoded IF-MAP result
|
||||
*/
|
||||
bool (*post)(tnc_ifmap2_soap_msg_t *this,
|
||||
char *request_name, xmlNodePtr request,
|
||||
char *result_name, xmlNodePtr* result);
|
||||
|
||||
/**
|
||||
* Destroy a tnc_ifmap2_soap_msg_t object.
|
||||
*/
|
||||
void (*destroy)(tnc_ifmap2_soap_msg_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tnc_ifmap2_soap_msg instance.
|
||||
*
|
||||
* @param tls TLS socket protecting the SOAP message
|
||||
*/
|
||||
tnc_ifmap2_soap_msg_t *tnc_ifmap2_soap_msg_create(tls_socket_t *tls);
|
||||
|
||||
#endif /** TNC_IFMAP2_SOAP_MSG_H_ @}*/
|
Loading…
Reference in New Issue