947 lines
30 KiB
C++
947 lines
30 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) Intel Corporation, 2003 - 2009.
|
|
//
|
|
// File: httpDigest.cpp
|
|
//
|
|
// Contents: Sample code for a gSOAP plugin to implement HTTP Digest
|
|
// authentication.
|
|
//
|
|
// Limitations:
|
|
// - MIME, DIME and HTTP chunks (SOAP_IO_CHUNK) are not supported.
|
|
// - This implementationn will internally buffer the entire outgoing
|
|
// message before sending
|
|
// - This implementation will fail if challenge isn't received within
|
|
// SOAP_BUFLEN bytes read.
|
|
// - This implementation will fail if challenge or response are larger
|
|
// than the constants we used.
|
|
// - This implementation calculates the digest response for each call
|
|
// and doesn't save information.
|
|
// - This implementation assumes that the algorithm is MD5 and that
|
|
// qop="auth".
|
|
//
|
|
// Usage: Add the httpDigest.h and httpDigest.cpp files to your project
|
|
//
|
|
// In your source, just after calling soap_init(), register this
|
|
// plugin with soap_register_plugin( soap, http_digest ).
|
|
// Use soap.userid and soap.passwd for the username and password.
|
|
// As in gSOAP, username and password have to be provided for each call.
|
|
//
|
|
// e.g.
|
|
// struct soap soap;
|
|
// soap_init( &soap );
|
|
// soap_register_plugin( &soap, http_digest );
|
|
// soap.userid = "admin";
|
|
// soap.passwd = "admin";
|
|
// ...
|
|
// soap_done(&soap);
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "httpDigest.h"
|
|
#include "digcalc.h"
|
|
#include <string.h>
|
|
|
|
#if 0 // ifdef out to avoid compiler warning
|
|
// Debugging macros and variable:
|
|
static int nest_level = 0;
|
|
#define entry(routine) fprintf(stderr, "Enter - %d - %s\n", ++nest_level, #routine);fflush(stderr)
|
|
#define leave(routine) fprintf(stderr, "Exit - %d - %s line %d\n", nest_level--, #routine, __LINE__);fflush(stderr)
|
|
#define logstep(whatsgoingon) fprintf(stderr, "http_digest_frecv: - %d - line %d - %s\n", nest_level, __LINE__, whatsgoingon); fflush(stderr);
|
|
|
|
static void soap_dump(struct soap *soap); // for debugging
|
|
static void data_dump(struct http_digest_data *data); // for debugging
|
|
#endif
|
|
static bool valid_socket(int fd); // for debugging
|
|
|
|
static bool debug = false;
|
|
|
|
#define HDR_SIZE 1024
|
|
|
|
const char HTTP_DIGEST_ID[12] = "HTTP-Digest"; // plugin identification
|
|
static const unsigned int SYN_DELAY = 400000;
|
|
|
|
struct http_digest_data
|
|
{
|
|
struct soap *soap; // back pointer
|
|
|
|
// send buffer parameters
|
|
char *sendBuffer; // send buffer
|
|
size_t sendMsgSize; // total length of the message
|
|
size_t sendBufferLength; // length of data in buffer
|
|
|
|
// receive buffer parameters
|
|
char *rcvBuffer; // receive buffer
|
|
size_t rcvSize; // length of buffer
|
|
size_t rcvIdx; // current index
|
|
size_t rcvRead; // amount of data read
|
|
bool rcvErr; // error encountered when reading
|
|
|
|
// open parameters
|
|
char *endpoint;
|
|
char *host;
|
|
int port;
|
|
|
|
// function pointers
|
|
int (*fopen)(struct soap*, const char*, const char*, int); // open function
|
|
int (*fsend)(struct soap*, const char*, size_t); // send function
|
|
size_t (*frecv)(struct soap*, char*, size_t); // receive function
|
|
int (*fposthdr)(struct soap*, const char*, const char*); // post header function
|
|
int (*fclose)(struct soap*); // disconnect function
|
|
|
|
char *username;
|
|
char *password;
|
|
char requestUrl[128];
|
|
char method[5];
|
|
|
|
int connect_count; // for tracking if we saw CONNECT command
|
|
};
|
|
|
|
static int http_digest_init(struct soap *soap, struct http_digest_data *data);
|
|
static int http_digest_copy(struct soap *soap, struct soap_plugin *dst,
|
|
struct soap_plugin *src);
|
|
static void http_digest_delete(struct soap *soap, struct soap_plugin *p);
|
|
|
|
static int http_digest_fopen(struct soap *soap, const char *endpoint,
|
|
const char *host, int port);
|
|
static int http_digest_fsend(struct soap *soap, const char *buf, size_t size);
|
|
static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size);
|
|
static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value);
|
|
static int http_digest_disconnect(struct soap *soap);
|
|
|
|
static void reset_data(struct http_digest_data *data);
|
|
|
|
/*
|
|
* The entry point for HTTP digest plugin
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* p - pointer to the soap plugin
|
|
* arg - reserved. Should be NULL
|
|
*
|
|
* Returns SOAP_OK for suceess. Other values for error
|
|
*/
|
|
int http_digest(struct soap *soap, struct soap_plugin *p, void *arg)
|
|
{
|
|
debug = (getenv("DEBUGME") != NULL);
|
|
p->id = HTTP_DIGEST_ID;
|
|
p->data = (void*)malloc(sizeof(struct http_digest_data));
|
|
p->fcopy = http_digest_copy;
|
|
p->fdelete = http_digest_delete;
|
|
if (p->data) {
|
|
memset(p->data, 0, sizeof(struct http_digest_data));
|
|
if (http_digest_init(soap, (struct http_digest_data*)p->data)) {
|
|
free(p->data);
|
|
return SOAP_EOM;
|
|
}
|
|
return SOAP_OK;
|
|
}
|
|
return SOAP_EOM;
|
|
}
|
|
|
|
/*
|
|
* Initializes the http digest data structure.
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* data - pointer to the http digest data structure
|
|
*
|
|
* Returns SOAP_OK for suceess. Other values for error
|
|
*/
|
|
static int http_digest_init(struct soap *soap, struct http_digest_data *data)
|
|
{
|
|
data->soap = soap;
|
|
data->fopen = soap->fopen;
|
|
soap->fopen = http_digest_fopen;
|
|
data->fsend = soap->fsend;
|
|
soap->fsend = http_digest_fsend;
|
|
data->frecv = soap->frecv;
|
|
soap->frecv = http_digest_frecv;
|
|
data->fposthdr = soap->fposthdr;
|
|
soap->fposthdr = http_digest_fposthdr;
|
|
data->fclose = soap->fclose;
|
|
soap->fclose = http_digest_disconnect;
|
|
|
|
data->rcvBuffer = NULL;
|
|
data->rcvSize = 0;
|
|
data->rcvIdx = 0;
|
|
data->rcvRead = 0;
|
|
data->rcvErr = false;
|
|
|
|
data->connect_count = 0;
|
|
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/*
|
|
* Creates the HTTP digest response
|
|
* Arguments:
|
|
* userName - the user name
|
|
* password - the password
|
|
* method - the HTTP method ("GET", "POST")
|
|
* realm - the realm for the authentication
|
|
* uri - the URI from the HTTP request
|
|
* nonce - the nonce from the challenge
|
|
* cnonce - client generated nonce
|
|
* digestResponse - The result authorization string
|
|
* length - size of buffer for response
|
|
*
|
|
* Returns 0 for suceess. -1 for error
|
|
*/
|
|
static int CalculateResponse(char *userName, char *password, char *method,
|
|
char *realm, char *uri, char *nonce, char *cnonce,
|
|
char *digestResponse, size_t *length)
|
|
{
|
|
size_t currOffset = 0, segmentLength;
|
|
static const char *INITIAL_HDR = "Authorization: Digest username=\"";
|
|
static const char *REALM_HDR = "\", realm=\"";
|
|
static const char *ALGO_HDR = "\", qop=\"auth\", algorithm=\"MD5\", uri=\"";
|
|
static const char *NONCE_HDR = "\", nonce=\"";
|
|
static const char *NC_HDR = "\", nc=00000002, cnonce=\"";
|
|
static const char *RSP_HDR = "\", response=\"";
|
|
|
|
HASHHEX HA1;
|
|
HASHHEX HA2 = "";
|
|
HASHHEX response;
|
|
|
|
//"Authorization: Digest username="
|
|
segmentLength = strlen(INITIAL_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, INITIAL_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username
|
|
segmentLength = strlen(userName);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, userName, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="
|
|
segmentLength = strlen(REALM_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, REALM_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="realm
|
|
segmentLength = strlen(realm);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, realm, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="
|
|
segmentLength = strlen(ALGO_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, ALGO_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service
|
|
segmentLength = strlen(uri);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, uri, segmentLength);
|
|
currOffset+= segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="
|
|
segmentLength = strlen(NONCE_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, NONCE_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...
|
|
segmentLength = strlen(nonce);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, nonce, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
|
|
//cnonce="
|
|
segmentLength = strlen(NC_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, NC_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
|
|
//cnonce="ab341...
|
|
segmentLength = strlen(cnonce);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, cnonce, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
|
|
//cnonce="ab341...", response="
|
|
segmentLength = strlen(RSP_HDR);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, RSP_HDR, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//calc response
|
|
DigestCalcHA1("MD5", userName, realm, password, nonce, cnonce, HA1);
|
|
DigestCalcResponse(HA1, nonce, "00000002", cnonce, "auth", method, uri,
|
|
HA2, response);
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
|
|
//cnonce="ab341...", response="8bbf2...
|
|
segmentLength = strlen(response);
|
|
if (*length < (currOffset + segmentLength)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, response, segmentLength);
|
|
currOffset += segmentLength;
|
|
|
|
//"Authorization: Digest username="username", realm="myRealm", qop="auth",
|
|
//algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
|
|
//cnonce="ab341...", response="8bbf2..."
|
|
if (*length < (currOffset + 2)) {
|
|
return -1;
|
|
}
|
|
memcpy(digestResponse + currOffset, "\"", 1);
|
|
currOffset += 1;
|
|
|
|
//add null termination
|
|
*(digestResponse+currOffset) = 0;
|
|
*length = currOffset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* generate a 32 byte random hexadecimal string such as "4f6ba982..."
|
|
* Arguments:
|
|
* outbuff - buffer to fill
|
|
*/
|
|
static void GenerateCNonce(char *outbuff)
|
|
{
|
|
srand((unsigned int)time(NULL));
|
|
for(int i = 0; i < 32; i++) {
|
|
int num = (int)(((double) rand()/ ((double)RAND_MAX+1)) * 16);
|
|
switch(num) {
|
|
case 0: case 1: case 2: case 3: case 4: case 5:
|
|
case 6: case 7: case 8: case 9:
|
|
outbuff[i] = '0' + num;
|
|
break;
|
|
case 10: case 11: case 12: case 13: case 14: case 15:
|
|
outbuff[i] = 'a' + (num-10);
|
|
break;
|
|
default:
|
|
outbuff[i] = 'f';
|
|
}
|
|
}
|
|
outbuff[32] = 0;
|
|
}
|
|
|
|
/*
|
|
* Creates the HTTP digest response
|
|
* Arguments:
|
|
* data - the HTTP digest structure
|
|
* authHeader - the HTTP digest challenge
|
|
* responseLength - size of response buffer
|
|
* digestResponse - buffer for HTTP digest response
|
|
*
|
|
* Returns 0 for suceess. -1 for error
|
|
*/
|
|
static int CreateDigestResponse(struct http_digest_data *data, char *authHeader,
|
|
size_t *responseLength, char *digestResponse)
|
|
{
|
|
char cnonce[33];
|
|
char *realmPtrStart, *realmPtrEnd, *noncePtrStart, *noncePtrEnd;
|
|
size_t segmentLength;
|
|
char realm[HDR_SIZE], nonce[HDR_SIZE];;
|
|
|
|
if (digestResponse == NULL || authHeader == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
GenerateCNonce(cnonce);
|
|
|
|
//grep realm from challange
|
|
realmPtrStart = strstr(authHeader, "realm=");
|
|
if (realmPtrStart == NULL) {
|
|
return -1;
|
|
}
|
|
//point to start of realm
|
|
realmPtrStart += 7;
|
|
realmPtrEnd = strstr(realmPtrStart, "\"");
|
|
segmentLength = realmPtrEnd - realmPtrStart;
|
|
memcpy(realm, realmPtrStart, segmentLength);
|
|
//add NULL termination
|
|
realm[segmentLength] = 0;
|
|
|
|
//grep nonce from challange
|
|
noncePtrStart = strstr(authHeader, "nonce=");
|
|
if (noncePtrStart == NULL) {
|
|
return -1;
|
|
}
|
|
//point to start of nonce
|
|
noncePtrStart += 7;
|
|
noncePtrEnd = strstr(noncePtrStart, "\"");
|
|
segmentLength = noncePtrEnd - noncePtrStart;
|
|
memcpy(nonce, noncePtrStart, segmentLength);
|
|
//add NULL termination
|
|
nonce[segmentLength]=0;
|
|
// in case there is a proxy the full URL is given
|
|
// hence take only the last part http://ip/XXXService --> /XXXService
|
|
char* uri = strrchr(data->requestUrl, '/');
|
|
return CalculateResponse(data->username, data->password, data->method, realm,
|
|
uri, nonce, cnonce,digestResponse, responseLength);
|
|
}
|
|
|
|
|
|
/*
|
|
* Copies the contents of the plugin
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* dst - the destination plugin
|
|
* src - the original plugin
|
|
*
|
|
* Returns SOAP_OK for suceess. Error value for error
|
|
*/
|
|
static int http_digest_copy(struct soap *soap, struct soap_plugin *dst,
|
|
struct soap_plugin *src)
|
|
{
|
|
*dst = *src;
|
|
dst->data = (void*)malloc(sizeof(struct http_digest_data));
|
|
if (!dst->data) {
|
|
return SOAP_EOM;
|
|
}
|
|
memcpy(dst->data, src->data, sizeof(struct http_digest_data));
|
|
|
|
((struct http_digest_data*)dst->data)->sendBuffer = NULL;
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/*
|
|
* Deletes the contents of the plugin
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* p - the plugin
|
|
*/
|
|
static void http_digest_delete(struct soap *soap, struct soap_plugin *p)
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data*)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
|
|
if (data) {
|
|
if (data->sendBuffer) {
|
|
free(data->sendBuffer);
|
|
}
|
|
free(data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open function. Will be called when connection opened. Used for saving parameters.
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* endpoint - the URL
|
|
* host - machine to connect to
|
|
* port - port on the host
|
|
*
|
|
* Returns SOAP_OK for suceess. Error value for error
|
|
*/
|
|
static int http_digest_fopen(struct soap *soap, const char *endpoint,
|
|
const char *host, int port)
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
data->endpoint = (char*)endpoint;
|
|
data->host = (char*)host;
|
|
data->port = port;
|
|
soap->frecv = http_digest_frecv;
|
|
if (debug) {
|
|
fprintf(stderr, "BEFORE data->fopen\n");
|
|
valid_socket(soap->socket);
|
|
}
|
|
int ret = data->fopen(soap, endpoint, host, port);
|
|
if (debug) {
|
|
fprintf(stderr, "AFTER data->fopen\n");
|
|
valid_socket(soap->socket);
|
|
fflush(stderr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Post header function. Used to identify parameters of the HTTP message
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* key - the header key
|
|
* value - the header value
|
|
*
|
|
* Returns SOAP_OK for suceess. Error value for error
|
|
*/
|
|
static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value)
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
char *s1, *s2;
|
|
|
|
if (key && !value) {
|
|
if (strncmp(key, "CONNECT", 7) == 0) {
|
|
data->connect_count++;
|
|
int ret;
|
|
ret = data->fposthdr(soap, key, value);
|
|
if (debug) {
|
|
fprintf(stderr, "fposthdr: to soap fposthdr: key = %s, value = %s\n", key, value);
|
|
fflush(stderr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
data->sendMsgSize = 0;
|
|
data->sendBufferLength = 0;
|
|
|
|
if (data->sendBuffer) {
|
|
free(data->sendBuffer);
|
|
data->sendBuffer = NULL;
|
|
}
|
|
|
|
data->password = soap->passwd;
|
|
data->username = soap->userid;
|
|
soap->passwd = soap->userid = NULL;
|
|
soap->frecv = http_digest_frecv;
|
|
|
|
// read the method and URI from the key
|
|
if (strncmp(key, "GET", 3) == 0)
|
|
memcpy(data->method, "GET", 4);
|
|
else if (strncmp(key, "POST", 4) == 0)
|
|
memcpy(data->method, "POST", 5);
|
|
else {
|
|
return soap->error = SOAP_EOM;
|
|
}
|
|
|
|
s1 = strstr(key, " ");
|
|
if (!s1) {
|
|
return soap->error = SOAP_EOM;
|
|
}
|
|
s1++;
|
|
|
|
s2 = strstr(s1, " ");
|
|
if (!s2) {
|
|
return soap->error = SOAP_EOM;
|
|
}
|
|
|
|
if (sizeof(data->requestUrl) <= (size_t)(s2 - s1)) {
|
|
return soap->error = SOAP_EOM;
|
|
}
|
|
memcpy(data->requestUrl, s1, s2-s1);
|
|
data->requestUrl[s2-s1] = '\0';
|
|
|
|
} else if (value) {
|
|
// determine the maximum length of this message so that we can
|
|
// correctly determine when we have completed the send
|
|
if (strcmp(key, "Content-Length") == 0) {
|
|
data->sendMsgSize += strtoul(value, NULL, 10);
|
|
}
|
|
}
|
|
|
|
// calculate the header size
|
|
data->sendMsgSize += 2;
|
|
if (key) {
|
|
data->sendMsgSize += strlen(key);
|
|
if (value) {
|
|
data->sendMsgSize += (strlen(value) + 2);
|
|
}
|
|
}
|
|
int ret = data->fposthdr(soap, key, value);
|
|
if (debug) {
|
|
fprintf(stderr, "fposthdr: to soap fposthdr 2: key = %s, value = %s\n", key, value);
|
|
fflush(stderr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Send function. Used to buffer the sent data and save for resends
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* buf - buffer to be sent
|
|
* size - size of data to send
|
|
*
|
|
* Returns SOAP_OK for suceess. Error value for error
|
|
*/
|
|
static int http_digest_fsend(struct soap *soap, const char *buf, size_t size)
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
size_t newBufferLen = data->sendBufferLength + size;
|
|
|
|
if (!data->sendBuffer || (newBufferLen > data->sendMsgSize)) {
|
|
if (newBufferLen > data->sendMsgSize) {
|
|
data->sendMsgSize = newBufferLen;
|
|
}
|
|
data->sendBuffer = (char *)realloc(data->sendBuffer, data->sendMsgSize);
|
|
if (!data->sendBuffer) {
|
|
return SOAP_EOM;
|
|
}
|
|
}
|
|
memcpy(data->sendBuffer + data->sendBufferLength, buf, size);
|
|
data->sendBufferLength = newBufferLen;
|
|
|
|
// if we haven't got the entire length of the message yet, then
|
|
// we return to gsoap and let it continue
|
|
if (data->sendBufferLength < data->sendMsgSize) {
|
|
return SOAP_OK;
|
|
}
|
|
|
|
// we've now got the entire message, now we can send the buffer
|
|
int ret = data->fsend(soap, data->sendBuffer, data->sendBufferLength);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Reads the next character. May need to read from the network
|
|
* Arguments:
|
|
* data - pointer to the http digest structure
|
|
*
|
|
* Returns the next character or EOF for failure.
|
|
*/
|
|
static char http_digest_getchar(struct http_digest_data *data)
|
|
{
|
|
size_t res;
|
|
if (data->rcvIdx < data->rcvRead)
|
|
return data->rcvBuffer[data->rcvIdx++];
|
|
if (debug) {
|
|
fprintf(stderr,"getchar:\n");
|
|
valid_socket(data->soap->socket);
|
|
}
|
|
res = data->frecv(data->soap, (data->rcvBuffer + data->rcvRead),
|
|
(data->rcvSize - data->rcvRead));
|
|
if (res <= 0)
|
|
return EOF;
|
|
data->rcvRead += res;
|
|
return data->rcvBuffer[data->rcvIdx++];
|
|
}
|
|
|
|
/*
|
|
* Reads the next HTTP header line.
|
|
* Arguments:
|
|
* data - pointer to the http digest structure
|
|
* line - buffer to store the line read
|
|
* len - length of the line buffer
|
|
*
|
|
* Returns SOAP_OK for suceess. Error value for error.
|
|
*/
|
|
static int http_digest_getline(struct http_digest_data *data, char *line, size_t len)
|
|
{
|
|
unsigned int i = len;
|
|
int c = 0;
|
|
char *s = line;
|
|
|
|
for (;;) {
|
|
while (--i > 0) {
|
|
c = http_digest_getchar(data);
|
|
if (c == '\r')
|
|
break;
|
|
if (c == EOF)
|
|
return SOAP_EOF;
|
|
*s++ = c;
|
|
}
|
|
c = http_digest_getchar(data);
|
|
if (c == '\n') {
|
|
*s = '\0';
|
|
if (i+1 == len) // empty line: end of HTTP header
|
|
break;
|
|
c = http_digest_getchar(data);
|
|
data->rcvIdx--; // return to previous character
|
|
if (c != ' ' && c != '\t') // HTTP line continuation?
|
|
break;
|
|
} else if (c == EOF)
|
|
return SOAP_EOF;
|
|
}
|
|
if (debug) {
|
|
fprintf(stderr,"getline: got %s\n", line);
|
|
fflush(stderr);
|
|
}
|
|
return SOAP_OK;
|
|
}
|
|
|
|
/*
|
|
* receive function. Used to look for digest authentication challenge.
|
|
* If the challenge is found will calculate the response and resend.
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
* buf - buffer read data into
|
|
* size - size of buf
|
|
*
|
|
* Returns number of characters read. 0 for error.
|
|
*/
|
|
|
|
static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size)
|
|
{
|
|
// lookup plugin
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
|
|
// declare variables
|
|
char header[HDR_SIZE], authResponse[HDR_SIZE];
|
|
static const char *CHALLANGE = "WWW-Authenticate: Digest";
|
|
char *s;
|
|
unsigned long httpStatus;
|
|
size_t len;
|
|
bool found = false;
|
|
|
|
if (data->connect_count == 1) {
|
|
int ret = data->frecv(soap, buf, size);
|
|
if (debug) {
|
|
fprintf(stderr,"frecv: got back >>%.*s<<\n", ret, buf);
|
|
fflush(stderr);
|
|
}
|
|
/*
|
|
* When reading the response to the CONNECT, we have to
|
|
* transparently pass the entire header back to gSOAP.
|
|
* So check for the end of the header by looking for two
|
|
* successive ends-of-line. If we've seen them, increment
|
|
* data->connect_count so that we don't hit this part of the
|
|
* code again. Otherwise, we'll come back into this part
|
|
* again to pass back the next chunk read from the proxy.
|
|
* (Seen in real life with Apache on Windows as proxy.)
|
|
*/
|
|
if (strncmp(buf + ret - 4, "\r\n\r\n", 4) == 0)
|
|
data->connect_count++;
|
|
return ret;
|
|
}
|
|
|
|
// init private buffer
|
|
data->rcvBuffer = new char[size];
|
|
data->rcvSize = size;
|
|
data->rcvIdx = 0;
|
|
data->rcvRead = 0;
|
|
data->rcvErr = false;
|
|
|
|
// first call to http_digest_getline will call data->frecv to read data
|
|
// and store it in private buffer
|
|
|
|
// read header lines and parse them to get httpStatus
|
|
do {
|
|
if (http_digest_getline(data, header, HDR_SIZE))
|
|
goto _out;
|
|
if ((s = strchr(header, ' ')))
|
|
httpStatus = soap_strtoul(s, NULL, 10);
|
|
else
|
|
httpStatus = 0;
|
|
|
|
if ((httpStatus != 100) && (httpStatus != 401))
|
|
goto _out;
|
|
|
|
for (;;) {
|
|
if (http_digest_getline(data, header, SOAP_HDRLEN))
|
|
goto _out;
|
|
|
|
if (!*header)
|
|
break;
|
|
|
|
if ((httpStatus == 401) && strncmp(header, CHALLANGE, strlen(CHALLANGE)) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
} while (httpStatus == 100);
|
|
|
|
// if we got here httpStatus==401
|
|
if (!found)
|
|
goto _out;
|
|
|
|
// create digest response
|
|
// header is HTTP digest challenge
|
|
len = HDR_SIZE;
|
|
|
|
if (CreateDigestResponse(data, header, &len, authResponse))
|
|
goto _out;
|
|
|
|
s = strstr(data->sendBuffer, "\r\n");
|
|
if (!s)
|
|
goto _out;
|
|
|
|
s += 2; // point to the start of second line
|
|
|
|
// delay sending SYN to allow AMT to close connection gracefully
|
|
usleep(SYN_DELAY);
|
|
|
|
// reset soap connectivity
|
|
if (data->connect_count || httpStatus == 401)
|
|
soap->fclose(soap);
|
|
|
|
// use real receiver for reopening connection
|
|
// use real sender for reopening connection
|
|
soap->frecv = data->frecv;
|
|
soap->fsend = data->fsend;
|
|
soap->fposthdr = data->fposthdr;
|
|
|
|
// resend CONNECT; this reopens the socket and SSL
|
|
soap->socket = data->fopen(soap, data->endpoint, data->host, data->port);
|
|
if (soap->error || !soap_valid_socket(soap->socket)) {
|
|
data->rcvErr = true;
|
|
goto _out;
|
|
}
|
|
|
|
if (data->fsend(soap, data->sendBuffer, s-data->sendBuffer) ||
|
|
data->fsend(soap, authResponse, len) ||
|
|
data->fsend(soap, "\r\n", 2) ||
|
|
data->fsend(soap, s, data->sendBufferLength - (s-data->sendBuffer)))
|
|
{
|
|
data->rcvErr = true;
|
|
goto _out;
|
|
}
|
|
|
|
// after send - send FIN if needed
|
|
#ifdef WITH_OPENSSL
|
|
if (!soap->ssl && soap_valid_socket(soap->socket) && !soap->keep_alive) {
|
|
soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN
|
|
}
|
|
#else
|
|
if (soap_valid_socket(soap->socket) && !soap->keep_alive) {
|
|
soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN
|
|
}
|
|
#endif
|
|
|
|
// receive data
|
|
data->rcvRead = data->frecv(soap, data->rcvBuffer, size);
|
|
|
|
data->connect_count = 0; // for next round
|
|
soap->fsend = http_digest_fsend;
|
|
soap->fposthdr = http_digest_fposthdr;
|
|
|
|
// this new connection used by gSoap for further comm
|
|
soap->keep_alive = 1;
|
|
|
|
// return it to soap
|
|
memcpy(buf, data->rcvBuffer, data->rcvRead);
|
|
|
|
_out:
|
|
if (data->sendBuffer) {
|
|
free(data->sendBuffer);
|
|
data->sendBuffer = NULL;
|
|
}
|
|
|
|
int ret;
|
|
ret = data->rcvErr ? 0 : data->rcvRead;
|
|
|
|
delete data->rcvBuffer;
|
|
data->rcvBuffer = NULL;
|
|
|
|
soap->userid = data->username;
|
|
soap->passwd = data->password;
|
|
if (data->connect_count)
|
|
reset_data(data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* disconnect function. Originally here for debugging.
|
|
* Adds sleep for SYN_DELAY.
|
|
* Arguments:
|
|
* soap - pointer to the soap runtime environment
|
|
*
|
|
* Returns value of real disconnect function
|
|
*/
|
|
static int http_digest_disconnect(struct soap *soap)
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
|
|
// delay sending SYN to allow AMT to close connection gracefully
|
|
usleep(SYN_DELAY);
|
|
int ret = data->fclose(soap);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* reset_data function.
|
|
*
|
|
* Arguments:
|
|
* data - pointer to the runtime data to be reset
|
|
*/
|
|
static void reset_data(struct http_digest_data *data)
|
|
{
|
|
data->endpoint = NULL;
|
|
data->host = NULL;
|
|
data->port = 0;
|
|
data->username = NULL;
|
|
data->password = NULL;
|
|
data->rcvErr = false;
|
|
memset(data->requestUrl, 0, sizeof(data->requestUrl));
|
|
memset(data->method, 0, sizeof(data->method));
|
|
}
|
|
|
|
#if 0 // ifdef out to avoid compiler warning
|
|
static void soap_dump(struct soap *soap) // for debugging
|
|
{
|
|
struct http_digest_data *data =
|
|
(struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
|
|
fprintf(stderr, "==== SOAP DUMP:\n");
|
|
fprintf(stderr, "\tsoap->userid = %s\n", soap->userid);
|
|
fprintf(stderr, "\tsoap->passwd = %s\n", soap->passwd);
|
|
fprintf(stderr, "\tsoap->host = %s\n", soap->host);
|
|
fprintf(stderr, "\tsoap->path = %s\n", soap->path);
|
|
fprintf(stderr, "\tsoap->port = %d\n", soap->port);
|
|
fprintf(stderr, "\tsoap->socket = %d\n", soap->socket);
|
|
fprintf(stderr, "\tsoap->keep_alive = %d\n", soap->keep_alive);
|
|
fprintf(stderr, "\tsoap->fopen = %s\n", soap->fopen == http_digest_fopen
|
|
? "http_digest_fopen" : "default fopen");
|
|
fprintf(stderr, "\tsoap->fsend = %s\n", soap->fsend == http_digest_fsend
|
|
? "http_digest_fsend" : "default fsend");
|
|
fprintf(stderr, "\tsoap->frecv = %s\n", soap->frecv == http_digest_frecv
|
|
? "http_digest_frecv" : "default frecv");
|
|
fprintf(stderr, "\tsoap->fposthdr = %s\n", soap->fposthdr == http_digest_fposthdr
|
|
? "http_digest_fposthdr" : "default fposthdr");
|
|
fprintf(stderr, "\tsoap->fclose = %s\n", soap->fclose == http_digest_disconnect
|
|
? "http_digest_disconnect" : "default fclose");
|
|
if (data)
|
|
data_dump(data);
|
|
else
|
|
fprintf(stderr, "NO DATA\n");
|
|
valid_socket(soap->socket);
|
|
fprintf(stderr, "====\n\n");
|
|
fflush(stderr);
|
|
}
|
|
|
|
static void data_dump(struct http_digest_data *data) // for debugging
|
|
{
|
|
fprintf(stderr, "==== DATA DUMP:\n");
|
|
fprintf(stderr, "\tdata->connect_count = %d\n", data->connect_count);
|
|
fprintf(stderr, "\tdata->endpoint = %s\n", data->endpoint);
|
|
fprintf(stderr, "\tdata->host = %s\n", data->host);
|
|
fprintf(stderr, "\tdata->port = %d\n", data->port);
|
|
fprintf(stderr, "\tdata->username = %s\n", data->username);
|
|
fprintf(stderr, "\tdata->password = %s\n", data->password);
|
|
fprintf(stderr, "\tdata->requestUrl = %s\n", data->requestUrl);
|
|
fprintf(stderr, "\tdata->method = %s\n", data->method);
|
|
}
|
|
#endif
|
|
|
|
static bool valid_socket(int fd)
|
|
{
|
|
struct stat sbuf;
|
|
bool ret = (fstat(fd, & sbuf) == 0);
|
|
fprintf(stderr, "+++ valid_socket: %s\n", ret ? "TRUE" : "FALSE");
|
|
return ret;
|
|
}
|