2013-07-20 21:01:44 +02:00
|
|
|
#include "ac.h"
|
|
|
|
#include "ac_soap.h"
|
|
|
|
#include "capwap_socket.h"
|
|
|
|
|
|
|
|
/* */
|
|
|
|
#define SOAP_PROTOCOL_CONNECT_TIMEOUT 10000
|
|
|
|
|
|
|
|
/* */
|
|
|
|
#define HTTP_RESPONSE_STATUS_CODE 0
|
|
|
|
#define HTTP_RESPONSE_HEADER 1
|
|
|
|
#define HTTP_RESPONSE_BODY 2
|
|
|
|
#define HTTP_RESPONSE_ERROR 3
|
|
|
|
|
2013-07-30 22:05:32 +02:00
|
|
|
/* */
|
|
|
|
static const char l_encodeblock[] =
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
static const char l_decodeblock[] =
|
|
|
|
"\x3f\x00\x00\x00\x40\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x00"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
|
|
|
|
"\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24"
|
|
|
|
"\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34";
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
/* */
|
|
|
|
xmlNodePtr ac_xml_get_children(xmlNodePtr parent) {
|
|
|
|
xmlNodePtr children;
|
|
|
|
|
|
|
|
ASSERT(parent != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
children = parent->xmlChildrenNode;
|
|
|
|
while ((children != NULL) && (children->type != XML_ELEMENT_NODE)) {
|
|
|
|
children = children->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
xmlNodePtr ac_xml_get_next(xmlNodePtr element) {
|
|
|
|
xmlNodePtr node;
|
|
|
|
|
|
|
|
ASSERT(element != NULL);
|
|
|
|
|
|
|
|
node = element->next;
|
|
|
|
while ((node != NULL) && (node->type != XML_ELEMENT_NODE)) {
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlNodePtr ac_xml_search_child(xmlNodePtr parent, char* prefix, char* name) {
|
|
|
|
xmlNodePtr children;
|
|
|
|
|
|
|
|
ASSERT(parent != NULL);
|
|
|
|
ASSERT(name != NULL);
|
|
|
|
|
|
|
|
children = ac_xml_get_children(parent);
|
|
|
|
while (children != NULL) {
|
|
|
|
if (!xmlStrcmp(children->name, BAD_CAST name) && (!prefix || !xmlStrcmp(children->ns->prefix, BAD_CAST prefix))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
children = ac_xml_get_next(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2013-07-23 22:10:35 +02:00
|
|
|
static int ac_soapclient_parsing_url(struct ac_http_soap_server* server, const char* url) {
|
2013-07-20 21:01:44 +02:00
|
|
|
int length;
|
|
|
|
int protocol;
|
|
|
|
int host;
|
|
|
|
int port;
|
|
|
|
int hostlength;
|
|
|
|
int pathlength;
|
|
|
|
|
|
|
|
ASSERT(server != NULL);
|
|
|
|
ASSERT(url != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
length = strlen(url);
|
|
|
|
if (length < 8) {
|
|
|
|
/* Invalid URL */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search protocol */
|
|
|
|
protocol = 0;
|
|
|
|
while (url[protocol] && url[protocol] != ':') {
|
|
|
|
protocol++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!protocol || (protocol + 3 >= length)) {
|
|
|
|
/* Invalid URL */
|
|
|
|
return 0;
|
|
|
|
} else if ((url[protocol] != ':') || (url[protocol + 1] != '/') || (url[protocol + 2] != '/')) {
|
|
|
|
/* Invalid URL */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parsing protocol */
|
|
|
|
if (!strncasecmp(url, "http", protocol)) {
|
|
|
|
server->protocol = SOAP_HTTP_PROTOCOL;
|
|
|
|
} else if (!strncasecmp(url, "https", protocol)) {
|
2013-08-06 22:15:19 +02:00
|
|
|
server->protocol = SOAP_HTTPS_PROTOCOL;
|
2013-07-20 21:01:44 +02:00
|
|
|
} else {
|
|
|
|
/* Unknown protocol */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol += 3;
|
|
|
|
|
|
|
|
/* Search hostname */
|
|
|
|
host = protocol;
|
|
|
|
while (url[host] && (url[host] != ':') && (url[host] != '/')) {
|
|
|
|
host++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host == protocol) {
|
|
|
|
/* Invalid hostname */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search port */
|
|
|
|
port = host;
|
|
|
|
if (url[port] == ':') {
|
|
|
|
while (url[port] && (url[port] != '/')) {
|
|
|
|
port++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve hostname */
|
|
|
|
hostlength = port - protocol;
|
|
|
|
server->host = capwap_alloc(hostlength + 1);
|
|
|
|
strncpy(server->host, &url[protocol], hostlength);
|
|
|
|
server->host[hostlength] = 0;
|
|
|
|
|
|
|
|
/* Parsing hostname */
|
|
|
|
if (!capwap_address_from_string(server->host, &server->address)) {
|
|
|
|
/* Invalid hostname */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port == host) {
|
|
|
|
/* Get default port */
|
|
|
|
if (server->protocol == SOAP_HTTP_PROTOCOL) {
|
|
|
|
CAPWAP_SET_NETWORK_PORT(&server->address, SOAP_HTTP_PORT);
|
|
|
|
} else if (server->protocol == SOAP_HTTPS_PROTOCOL) {
|
|
|
|
CAPWAP_SET_NETWORK_PORT(&server->address, SOAP_HTTPS_PORT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve path */
|
|
|
|
pathlength = length - port;
|
|
|
|
if (!pathlength) {
|
|
|
|
pathlength = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
server->path = capwap_alloc(pathlength + 1);
|
|
|
|
if (length == port) {
|
|
|
|
strcpy(server->path, "/");
|
|
|
|
} else {
|
|
|
|
strncpy(server->path, &url[port], pathlength);
|
|
|
|
server->path[pathlength] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-08-06 22:15:19 +02:00
|
|
|
/* */
|
|
|
|
static int ac_soapclient_connect(struct ac_http_soap_request* httprequest) {
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (httprequest->server->protocol == SOAP_HTTP_PROTOCOL) {
|
|
|
|
result = capwap_socket_connect(httprequest->sock, &httprequest->server->address, SOAP_PROTOCOL_CONNECT_TIMEOUT);
|
|
|
|
} else if (httprequest->server->protocol == SOAP_HTTPS_PROTOCOL) {
|
|
|
|
result = capwap_socket_connect(httprequest->sock, &httprequest->server->address, SOAP_PROTOCOL_CONNECT_TIMEOUT);
|
|
|
|
if (result) {
|
|
|
|
/* Establish SSL/TLS connection */
|
|
|
|
httprequest->sslsock = capwap_socket_ssl_connect(httprequest->sock, httprequest->server->sslcontext, SOAP_PROTOCOL_CONNECT_TIMEOUT);
|
|
|
|
if (!httprequest->sslsock) {
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
/* */
|
|
|
|
static int ac_soapclient_send_http(struct ac_http_soap_request* httprequest, char* soapaction, char* body, int length) {
|
|
|
|
time_t ts;
|
|
|
|
struct tm stm;
|
|
|
|
char datetime[32];
|
|
|
|
int headerlength;
|
|
|
|
int result;
|
|
|
|
char* buffer;
|
|
|
|
|
|
|
|
/* Retrieve datetime */
|
|
|
|
ts = time(NULL);
|
|
|
|
localtime_r(&ts, &stm);
|
|
|
|
strftime(datetime, 32, "%a, %d %b %Y %T %z", &stm);
|
|
|
|
|
|
|
|
/* Calculate header length */
|
2013-07-25 22:17:55 +02:00
|
|
|
headerlength = 150 + length + strlen(httprequest->server->path) + strlen(httprequest->server->host) + strlen(datetime) + strlen((soapaction ? soapaction : ""));
|
2013-07-20 21:01:44 +02:00
|
|
|
buffer = capwap_alloc(headerlength);
|
|
|
|
|
|
|
|
/* HTTP headers */
|
|
|
|
result = snprintf(buffer, headerlength,
|
|
|
|
"POST %s HTTP/1.1\r\n"
|
|
|
|
"Host: %s\r\n"
|
|
|
|
"Date: %s\r\n"
|
|
|
|
"Content-Length: %d\r\n"
|
|
|
|
"Content-Type: text/xml\r\n"
|
|
|
|
"Connection: Close\r\n"
|
|
|
|
"SoapAction: %s\r\n"
|
2013-07-25 22:17:55 +02:00
|
|
|
"Expect: 100-continue\r\n"
|
2013-07-20 21:01:44 +02:00
|
|
|
"\r\n"
|
|
|
|
"%s",
|
|
|
|
httprequest->server->path,
|
|
|
|
httprequest->server->host,
|
|
|
|
datetime,
|
|
|
|
length,
|
|
|
|
(soapaction ? soapaction : ""),
|
|
|
|
body
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Send headers */
|
|
|
|
if (result < 0) {
|
|
|
|
result = 0;
|
|
|
|
} else {
|
2013-08-06 22:15:19 +02:00
|
|
|
int sendlength = -1;
|
|
|
|
|
|
|
|
/* Send packet */
|
|
|
|
if (httprequest->server->protocol == SOAP_HTTP_PROTOCOL) {
|
|
|
|
sendlength = capwap_socket_send(httprequest->sock, buffer, result, httprequest->requesttimeout);
|
|
|
|
} else if (httprequest->server->protocol == SOAP_HTTPS_PROTOCOL) {
|
|
|
|
sendlength = capwap_socket_crypto_send(httprequest->sslsock, buffer, result, httprequest->requesttimeout);
|
2013-07-20 21:01:44 +02:00
|
|
|
}
|
2013-08-06 22:15:19 +02:00
|
|
|
|
|
|
|
/* Check result */
|
|
|
|
result = ((sendlength == result) ? 1 : 0);
|
2013-07-20 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
capwap_free(buffer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2013-07-25 22:17:55 +02:00
|
|
|
static int ac_soapclient_http_readline(struct ac_http_soap_request* httprequest, char* buffer, int length) {
|
2013-07-20 21:01:44 +02:00
|
|
|
int result = -1;
|
2013-07-25 22:17:55 +02:00
|
|
|
int bufferpos = 0;
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
for (;;) {
|
|
|
|
/* Receive packet into temporaly buffer */
|
2013-08-06 22:15:19 +02:00
|
|
|
if (httprequest->server->protocol == SOAP_HTTP_PROTOCOL) {
|
|
|
|
if (capwap_socket_recv(httprequest->sock, &buffer[bufferpos], 1, httprequest->responsetimeout) != 1) {
|
|
|
|
break; /* Connection error */
|
|
|
|
}
|
|
|
|
} else if (httprequest->server->protocol == SOAP_HTTPS_PROTOCOL) {
|
|
|
|
if (capwap_socket_crypto_recv(httprequest->sslsock, &buffer[bufferpos], 1, httprequest->responsetimeout) != 1) {
|
|
|
|
break; /* Connection error */
|
|
|
|
}
|
2013-07-25 22:17:55 +02:00
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
/* Update buffer size */
|
|
|
|
bufferpos += 1;
|
|
|
|
if (bufferpos >= length) {
|
|
|
|
break; /* Buffer overflow */
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
/* Search line */
|
|
|
|
if ((bufferpos > 1) && (buffer[bufferpos - 2] == '\r') && (buffer[bufferpos - 1] == '\n')) {
|
|
|
|
result = bufferpos - 2;
|
|
|
|
buffer[result] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
return result;
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
/* */
|
|
|
|
static int ac_soapclient_xml_io_read(void* ctx, char* buffer, int len) {
|
|
|
|
int result = -1;
|
|
|
|
char respbuffer[8192];
|
|
|
|
int respbufferlength = 0;
|
|
|
|
struct ac_http_soap_request* httprequest = (struct ac_http_soap_request*)ctx;
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
while ((httprequest->httpstate == HTTP_RESPONSE_STATUS_CODE) || (httprequest->httpstate == HTTP_RESPONSE_HEADER)) {
|
|
|
|
/* Receive packet into temporaly buffer */
|
|
|
|
respbufferlength = ac_soapclient_http_readline(httprequest, respbuffer, sizeof(respbuffer));
|
|
|
|
if (respbufferlength == -1) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
} else if (httprequest->httpstate == HTTP_RESPONSE_STATUS_CODE) {
|
|
|
|
int temp;
|
|
|
|
int descpos;
|
|
|
|
|
|
|
|
/* Parse response code */
|
|
|
|
temp = sscanf(respbuffer, "HTTP/1.1 %d %n", &httprequest->responsecode, &descpos);
|
|
|
|
if (temp != 1) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
/* Parsing headers */
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_HEADER;
|
|
|
|
} else if (httprequest->httpstate == HTTP_RESPONSE_HEADER) {
|
|
|
|
char* value;
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
if (!respbufferlength) {
|
|
|
|
if (httprequest->responsecode == HTTP_RESULT_CONTINUE) {
|
|
|
|
if (!httprequest->contentlength) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_STATUS_CODE;
|
|
|
|
} else {
|
2013-07-20 21:01:44 +02:00
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
}
|
2013-07-25 22:17:55 +02:00
|
|
|
} else if (httprequest->contentxml && (httprequest->contentlength > 0)) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_BODY; /* Retrieve body */
|
|
|
|
} else {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Separate key from value */
|
|
|
|
value = strchr(respbuffer, ':');
|
|
|
|
if (!value) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
} else {
|
2013-07-20 21:01:44 +02:00
|
|
|
*value = 0;
|
|
|
|
value++;
|
|
|
|
while (*value == ' ') {
|
|
|
|
value++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
if (!strcmp(respbuffer, "Content-Length")) {
|
|
|
|
httprequest->contentlength = atoi(value);
|
|
|
|
if (!httprequest->contentlength) {
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(respbuffer, "Content-Type")) {
|
|
|
|
char* param;
|
|
|
|
|
|
|
|
/* Remove param from value */
|
|
|
|
param = strchr(value, ';');
|
|
|
|
if (param) {
|
|
|
|
*param = 0;
|
|
|
|
}
|
|
|
|
|
2013-07-25 22:17:55 +02:00
|
|
|
if (!strcmp(value, "text/xml")) {
|
|
|
|
httprequest->contentxml = 1;
|
|
|
|
} else {
|
2013-07-20 21:01:44 +02:00
|
|
|
httprequest->httpstate = HTTP_RESPONSE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (httprequest->httpstate == HTTP_RESPONSE_BODY) {
|
|
|
|
if (!httprequest->contentlength) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive body directly into XML buffer */
|
2013-08-06 22:15:19 +02:00
|
|
|
if (httprequest->server->protocol == SOAP_HTTP_PROTOCOL) {
|
|
|
|
result = capwap_socket_recv(httprequest->sock, buffer, len, httprequest->responsetimeout);
|
|
|
|
} else if (httprequest->server->protocol == SOAP_HTTPS_PROTOCOL) {
|
|
|
|
result = capwap_socket_crypto_recv(httprequest->sslsock, buffer, len, httprequest->responsetimeout);
|
|
|
|
}
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
if (result > 0) {
|
|
|
|
httprequest->contentlength -= result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
static int ac_soapclient_xml_io_close(void *ctx) {
|
|
|
|
struct ac_http_soap_request* httprequest = (struct ac_http_soap_request*)ctx;
|
|
|
|
|
2013-07-23 22:10:35 +02:00
|
|
|
if ((httprequest->httpstate != HTTP_RESPONSE_BODY) || httprequest->contentlength) {
|
2013-07-20 21:01:44 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
static void ac_soapclient_parse_error(void* ctxt, const char* msg, ...) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_soapclient_init(void) {
|
|
|
|
xmlInitParser();
|
|
|
|
xmlSetGenericErrorFunc(NULL, ac_soapclient_parse_error);
|
2013-07-23 22:10:35 +02:00
|
|
|
xmlThrDefSetGenericErrorFunc(NULL, ac_soapclient_parse_error);
|
2013-07-20 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_soapclient_free(void) {
|
|
|
|
xmlCleanupParser();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2013-07-23 22:10:35 +02:00
|
|
|
struct ac_http_soap_server* ac_soapclient_create_server(const char* url) {
|
2013-07-20 21:01:44 +02:00
|
|
|
struct ac_http_soap_server* server;
|
|
|
|
|
|
|
|
ASSERT(url != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
server = (struct ac_http_soap_server*)capwap_alloc(sizeof(struct ac_http_soap_server));
|
|
|
|
memset(server, 0, sizeof(struct ac_http_soap_server));
|
|
|
|
|
|
|
|
/* */
|
|
|
|
if (!ac_soapclient_parsing_url(server, url)) {
|
|
|
|
ac_soapclient_free_server(server);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_soapclient_free_server(struct ac_http_soap_server* server) {
|
|
|
|
ASSERT(server != NULL);
|
|
|
|
|
|
|
|
if (server->host) {
|
|
|
|
capwap_free(server->host);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (server->path) {
|
|
|
|
capwap_free(server->path);
|
|
|
|
}
|
|
|
|
|
2013-08-06 22:15:19 +02:00
|
|
|
if (server->sslcontext) {
|
|
|
|
capwap_socket_crypto_freecontext(server->sslcontext);
|
|
|
|
}
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
capwap_free(server);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
struct ac_soap_request* ac_soapclient_create_request(char* method, char* urinamespace) {
|
2013-07-23 22:10:35 +02:00
|
|
|
char* tagMethod;
|
2013-07-20 21:01:44 +02:00
|
|
|
struct ac_soap_request* request;
|
|
|
|
|
|
|
|
ASSERT(method != NULL);
|
2013-07-23 22:10:35 +02:00
|
|
|
ASSERT(urinamespace != NULL);
|
2013-07-20 21:01:44 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
request = (struct ac_soap_request*)capwap_alloc(sizeof(struct ac_soap_request));
|
|
|
|
memset(request, 0, sizeof(struct ac_soap_request));
|
|
|
|
|
|
|
|
/* Build XML SOAP Request */
|
|
|
|
request->xmlDocument = xmlNewDoc(BAD_CAST "1.0");
|
|
|
|
request->xmlRoot = xmlNewNode(NULL, BAD_CAST "SOAP-ENV:Envelope");
|
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "xmlns:xsd", BAD_CAST "http://www.w3.org/2001/XMLSchema");
|
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "xmlns:xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance");
|
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "xmlns:SOAP-ENC", BAD_CAST "http://schemas.xmlsoap.org/soap/encoding/");
|
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "SOAP-ENV:encodingStyle", BAD_CAST "http://schemas.xmlsoap.org/soap/encoding/");
|
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "xmlns:SOAP-ENV", BAD_CAST "http://schemas.xmlsoap.org/soap/envelope/");
|
2013-07-25 17:05:56 +02:00
|
|
|
xmlNewProp(request->xmlRoot, BAD_CAST "xmlns:tns", BAD_CAST urinamespace);
|
2013-07-20 21:01:44 +02:00
|
|
|
xmlDocSetRootElement(request->xmlDocument, request->xmlRoot);
|
|
|
|
|
|
|
|
xmlNewChild(request->xmlRoot, NULL, BAD_CAST "SOAP-ENV:Header", NULL);
|
|
|
|
request->xmlBody = xmlNewChild(request->xmlRoot, NULL, BAD_CAST "SOAP-ENV:Body", NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
request->method = capwap_duplicate_string(method);
|
|
|
|
|
2013-07-23 22:10:35 +02:00
|
|
|
/* Create request */
|
|
|
|
tagMethod = capwap_alloc(strlen(method) + 5);
|
|
|
|
sprintf(tagMethod, "tns:%s", method);
|
|
|
|
request->xmlRequest = xmlNewChild(request->xmlBody, NULL, BAD_CAST tagMethod, NULL);
|
|
|
|
capwap_free(tagMethod);
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_soapclient_free_request(struct ac_soap_request* request) {
|
|
|
|
ASSERT(request != NULL);
|
|
|
|
|
|
|
|
if (request->xmlDocument) {
|
|
|
|
xmlFreeDoc(request->xmlDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request->method) {
|
|
|
|
capwap_free(request->method);
|
|
|
|
}
|
|
|
|
|
|
|
|
capwap_free(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
int ac_soapclient_add_param(struct ac_soap_request* request, char* type, char* name, char* value) {
|
|
|
|
xmlNodePtr xmlParam;
|
|
|
|
|
|
|
|
ASSERT(request != NULL);
|
|
|
|
ASSERT(name != NULL);
|
|
|
|
ASSERT(value != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
xmlParam = xmlNewTextChild(request->xmlRequest, NULL, BAD_CAST name, BAD_CAST value);
|
|
|
|
if (!xmlParam) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type) {
|
|
|
|
if (!xmlNewProp(xmlParam, BAD_CAST "xsi:type", BAD_CAST type)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
char* ac_soapclient_get_request(struct ac_soap_request* request) {
|
|
|
|
char* result;
|
|
|
|
size_t length;
|
|
|
|
xmlBufferPtr buffer;
|
|
|
|
|
|
|
|
ASSERT(request != NULL);
|
|
|
|
ASSERT(request->xmlDocument != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
buffer = xmlBufferCreate();
|
|
|
|
length = xmlNodeDump(buffer, request->xmlDocument, request->xmlRoot, 1, 0);
|
|
|
|
|
|
|
|
/* Clone XML document string */
|
|
|
|
result = capwap_alloc(length + 1);
|
|
|
|
memcpy(result, (char*)xmlBufferContent(buffer), length);
|
|
|
|
result[length] = 0;
|
|
|
|
|
|
|
|
/* */
|
|
|
|
xmlBufferFree(buffer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
struct ac_http_soap_request* ac_soapclient_prepare_request(struct ac_soap_request* request, struct ac_http_soap_server* server) {
|
|
|
|
struct ac_http_soap_request* httprequest;
|
|
|
|
|
|
|
|
ASSERT(request != NULL);
|
|
|
|
ASSERT(request->xmlDocument != NULL);
|
|
|
|
ASSERT(request->xmlRoot != NULL);
|
|
|
|
ASSERT(server != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
httprequest = (struct ac_http_soap_request*)capwap_alloc(sizeof(struct ac_http_soap_request));
|
2013-08-18 19:07:19 +02:00
|
|
|
memset(httprequest, 0, sizeof(struct ac_http_soap_request));
|
2013-07-20 21:01:44 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
httprequest->request = request;
|
|
|
|
httprequest->server = server;
|
2013-07-25 17:05:56 +02:00
|
|
|
httprequest->requesttimeout = SOAP_PROTOCOL_REQUEST_TIMEOUT;
|
|
|
|
httprequest->responsetimeout = SOAP_PROTOCOL_RESPONSE_TIMEOUT;
|
2013-07-20 21:01:44 +02:00
|
|
|
|
|
|
|
/* Create socket */
|
|
|
|
httprequest->sock = socket(httprequest->server->address.ss_family, SOCK_STREAM, 0);
|
|
|
|
if (httprequest->sock < 0) {
|
|
|
|
ac_soapclient_close_request(httprequest, 0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return httprequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
int ac_soapclient_send_request(struct ac_http_soap_request* httprequest, char* soapaction) {
|
|
|
|
char* buffer;
|
|
|
|
size_t length;
|
|
|
|
xmlBufferPtr xmlBuffer;
|
|
|
|
|
|
|
|
ASSERT(httprequest != NULL);
|
|
|
|
|
|
|
|
/* Retrieve XML SOAP Request */
|
|
|
|
xmlBuffer = xmlBufferCreate();
|
|
|
|
length = xmlNodeDump(xmlBuffer, httprequest->request->xmlDocument, httprequest->request->xmlRoot, 1, 0);
|
|
|
|
if (!length) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = (char*)xmlBufferContent(xmlBuffer);
|
|
|
|
|
|
|
|
/* Connect to remote host */
|
2013-08-06 22:15:19 +02:00
|
|
|
if (!ac_soapclient_connect(httprequest)) {
|
2013-07-20 21:01:44 +02:00
|
|
|
xmlBufferFree(xmlBuffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send HTTP Header */
|
|
|
|
if (!ac_soapclient_send_http(httprequest, soapaction, buffer, (int)length)) {
|
|
|
|
xmlBufferFree(xmlBuffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sent SOAP Request */
|
|
|
|
xmlBufferFree(xmlBuffer);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-07-25 17:05:56 +02:00
|
|
|
/* */
|
|
|
|
void ac_soapclient_shutdown_request(struct ac_http_soap_request* httprequest) {
|
|
|
|
ASSERT(httprequest != NULL);
|
|
|
|
|
2013-08-06 22:15:19 +02:00
|
|
|
if (httprequest->sslsock) {
|
|
|
|
capwap_socket_ssl_shutdown(httprequest->sslsock, SOAP_PROTOCOL_CLOSE_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2013-07-25 17:05:56 +02:00
|
|
|
if (httprequest->sock >= 0) {
|
2013-08-06 22:15:19 +02:00
|
|
|
capwap_socket_shutdown(httprequest->sock);
|
2013-07-25 17:05:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
/* */
|
|
|
|
void ac_soapclient_close_request(struct ac_http_soap_request* httprequest, int closerequest) {
|
|
|
|
ASSERT(httprequest != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
if (closerequest && httprequest->request) {
|
|
|
|
ac_soapclient_free_request(httprequest->request);
|
|
|
|
}
|
|
|
|
|
2013-08-06 22:15:19 +02:00
|
|
|
/* */
|
|
|
|
if (httprequest->sslsock) {
|
|
|
|
capwap_socket_ssl_shutdown(httprequest->sslsock, SOAP_PROTOCOL_CLOSE_TIMEOUT);
|
|
|
|
capwap_socket_ssl_close(httprequest->sslsock);
|
|
|
|
capwap_free(httprequest->sslsock);
|
|
|
|
}
|
|
|
|
|
2013-07-20 21:01:44 +02:00
|
|
|
/* Close socket */
|
|
|
|
if (httprequest->sock >= 0) {
|
2013-08-06 22:15:19 +02:00
|
|
|
capwap_socket_close(httprequest->sock);
|
2013-07-20 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
capwap_free(httprequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
struct ac_soap_response* ac_soapclient_recv_response(struct ac_http_soap_request* httprequest) {
|
|
|
|
struct ac_soap_response* response;
|
|
|
|
|
|
|
|
ASSERT(httprequest != NULL);
|
|
|
|
ASSERT(httprequest->sock >= 0);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
response = (struct ac_soap_response*)capwap_alloc(sizeof(struct ac_soap_response));
|
|
|
|
memset(response, 0, sizeof(struct ac_soap_response));
|
|
|
|
|
|
|
|
/* Receive HTTP response into XML callback */
|
|
|
|
httprequest->httpstate = HTTP_RESPONSE_STATUS_CODE;
|
|
|
|
response->xmlDocument = xmlReadIO(ac_soapclient_xml_io_read, ac_soapclient_xml_io_close, (void*)httprequest, "", NULL, 0);
|
|
|
|
if (!response->xmlDocument) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parsing response */
|
2013-07-25 22:17:55 +02:00
|
|
|
response->responsecode = httprequest->responsecode;
|
2013-07-20 21:01:44 +02:00
|
|
|
response->xmlRoot = xmlDocGetRootElement(response->xmlDocument);
|
|
|
|
if (!response->xmlRoot) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve Body */
|
|
|
|
response->xmlBody = ac_xml_search_child(response->xmlRoot, "SOAP-ENV", "Body");
|
|
|
|
if (!response->xmlBody) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve response */
|
2013-08-07 21:48:49 +02:00
|
|
|
if (response->responsecode == HTTP_RESULT_OK) {
|
|
|
|
char* tagMethod = capwap_alloc(strlen(httprequest->request->method) + 9);
|
|
|
|
sprintf(tagMethod, "%sResponse", httprequest->request->method);
|
|
|
|
response->xmlResponse = ac_xml_search_child(response->xmlBody, NULL, tagMethod);
|
|
|
|
capwap_free(tagMethod);
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-08-07 21:48:49 +02:00
|
|
|
if (!response->xmlResponse) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve optional return response */
|
|
|
|
response->xmlResponseReturn = ac_xml_search_child(response->xmlResponse, NULL, "return");
|
|
|
|
} else {
|
|
|
|
/* Retrieve Fault */
|
|
|
|
response->xmlFault = ac_xml_search_child(response->xmlBody, "SOAP-ENV", "Fault");
|
|
|
|
if (!response->xmlFault) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
2013-08-07 21:48:49 +02:00
|
|
|
/* Retrieve FaultCode */
|
|
|
|
response->xmlFaultCode = ac_xml_search_child(response->xmlFault, NULL, "faultcode");
|
|
|
|
if (!response->xmlFaultCode) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve FaultString */
|
|
|
|
response->xmlFaultString = ac_xml_search_child(response->xmlFault, NULL, "faultstring");
|
|
|
|
if (!response->xmlFaultString) {
|
|
|
|
ac_soapclient_free_response(response);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2013-07-20 21:01:44 +02:00
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_soapclient_free_response(struct ac_soap_response* response) {
|
|
|
|
ASSERT(response != NULL);
|
|
|
|
|
|
|
|
if (response->xmlDocument) {
|
|
|
|
xmlFreeDoc(response->xmlDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
capwap_free(response);
|
|
|
|
}
|
2013-07-30 22:05:32 +02:00
|
|
|
|
|
|
|
/* */
|
2013-08-18 22:19:56 +02:00
|
|
|
int ac_base64_binary_encode(const char* plain, int length, char* encoded) {
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
ASSERT(plain != NULL);
|
|
|
|
ASSERT(encoded != NULL);
|
2013-07-30 22:05:32 +02:00
|
|
|
|
2013-08-18 22:19:56 +02:00
|
|
|
while (length > 0) {
|
|
|
|
int len = ((length > 1) ? ((length > 2) ? 3 : 2) : 1);
|
2013-07-30 22:05:32 +02:00
|
|
|
|
|
|
|
/* Encode block */
|
2013-08-18 22:19:56 +02:00
|
|
|
encoded[0] = l_encodeblock[plain[0] >> 2];
|
|
|
|
encoded[1] = l_encodeblock[((plain[0] & 0x03) << 4) | ((plain[1] & 0xf0) >> 4)];
|
|
|
|
encoded[2] = (len > 1 ? l_encodeblock[((plain[1] & 0x0f) << 2) | ((plain[2] & 0xc0) >> 6)] : '=');
|
|
|
|
encoded[3] = (len > 2 ? l_encodeblock[plain[2] & 0x3f] : '=');
|
2013-07-30 22:05:32 +02:00
|
|
|
|
|
|
|
/* Next block */
|
2013-08-18 22:19:56 +02:00
|
|
|
plain += len;
|
|
|
|
length -= len;
|
|
|
|
encoded += 4;
|
|
|
|
result += 4;
|
2013-07-30 22:05:32 +02:00
|
|
|
}
|
|
|
|
|
2013-08-18 22:19:56 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_base64_string_encode(const char* plain, char* encoded) {
|
|
|
|
int result;
|
|
|
|
|
|
|
|
ASSERT(plain != NULL);
|
|
|
|
ASSERT(encoded != NULL);
|
|
|
|
|
|
|
|
/* Encode base64 */
|
|
|
|
result = ac_base64_binary_encode(plain, strlen(plain), encoded);
|
|
|
|
|
2013-07-30 22:05:32 +02:00
|
|
|
/* Terminate string */
|
2013-08-18 22:19:56 +02:00
|
|
|
encoded[result] = 0;
|
2013-07-30 22:05:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2013-08-18 22:19:56 +02:00
|
|
|
int ac_base64_binary_decode(const char* encoded, int length, char* plain) {
|
2013-07-30 22:05:32 +02:00
|
|
|
int i;
|
|
|
|
char bufdec[3];
|
2013-08-18 22:19:56 +02:00
|
|
|
int result = 0;
|
2013-07-30 22:05:32 +02:00
|
|
|
|
2013-08-18 22:19:56 +02:00
|
|
|
ASSERT(encoded != NULL);
|
|
|
|
ASSERT(plain != NULL);
|
2013-07-30 22:05:32 +02:00
|
|
|
|
2013-08-18 22:19:56 +02:00
|
|
|
while (length > 0) {
|
2013-07-30 22:05:32 +02:00
|
|
|
int len = 0;
|
|
|
|
char bufenc[4] = { 0, 0, 0, 0 };
|
|
|
|
|
2013-08-18 22:19:56 +02:00
|
|
|
for (i = 0; i < 4 && (length > 0); i++) {
|
2013-07-30 22:05:32 +02:00
|
|
|
char element = 0;
|
2013-08-18 22:19:56 +02:00
|
|
|
while ((length > 0) && !element) {
|
|
|
|
element = *encoded++;
|
2013-07-30 22:05:32 +02:00
|
|
|
element = (((element < 43) || (element > 122)) ? 0 : l_decodeblock[element - 43]);
|
2013-08-18 22:19:56 +02:00
|
|
|
length--;
|
2013-07-30 22:05:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (element) {
|
|
|
|
len++;
|
|
|
|
bufenc[i] = element - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len) {
|
|
|
|
bufdec[0] = (bufenc[0] << 2 | bufenc[1] >> 4);
|
|
|
|
bufdec[1] = (bufenc[1] << 4 | bufenc[2] >> 2);
|
|
|
|
bufdec[2] = (((bufenc[2] << 6) & 0xc0) | bufenc[3]);
|
|
|
|
|
|
|
|
for (i = 0; i < len - 1; i++) {
|
2013-08-18 22:19:56 +02:00
|
|
|
*plain++ = bufdec[i];
|
|
|
|
result++;
|
2013-07-30 22:05:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Terminate string */
|
2013-08-18 22:19:56 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void ac_base64_string_decode(const char* encoded, char* plain) {
|
|
|
|
int result;
|
|
|
|
|
|
|
|
ASSERT(encoded != NULL);
|
|
|
|
ASSERT(plain != NULL);
|
|
|
|
|
|
|
|
/* Decode base64 */
|
|
|
|
result = ac_base64_binary_decode(encoded, strlen(encoded), plain);
|
|
|
|
|
|
|
|
/* Terminate string */
|
|
|
|
plain[result] = 0;
|
2013-07-30 22:05:32 +02:00
|
|
|
}
|