re-arranging files for pcu control
[monitor.git] / cmdamt / httpDigest.cpp
1 //----------------------------------------------------------------------------
2 //
3 //  Copyright (C) Intel Corporation, 2003 - 2005.
4 //
5 //  File:       httpDigest.cpp 
6 //
7 //  Contents:   Sample code for a gSOAP plugin to implement HTTP Digest 
8 //              authentication.
9 //
10 //  Limitations:
11 //          - MIME, DIME and HTTP chunks (SOAP_IO_CHUNK) are not supported.
12 //          - This implementationn will internally buffer the entire outgoing 
13 //            message before sending
14 //          - This implementation will fail if challenge isn't received within 
15 //            SOAP_BUFLEN bytes read.
16 //          - This implementation will fail if challenge or response are larger
17 //            than the constants we used.
18 //          - This implementation calculates the digest response for each call 
19 //            and doesn't save information. 
20 //          - This implementation assumes that the algorithm is MD5 and that 
21 //            qop="auth".
22 //
23 // Usage:   Add the httpDigest.h and httpDigest.cpp files to your project 
24 //
25 //          In your source, just after calling soap_init(), register this 
26 //          plugin with soap_register_plugin( soap, http_digest ). 
27 //          Use soap.userid and soap.passwd for the username and password.
28 //          As in gSOAP, username and password have to be provided for each call.
29 //
30 //          e.g.
31 //              struct soap soap;
32 //              soap_init( &soap );
33 //              soap_register_plugin( &soap, http_digest );
34 //              soap.userid = "admin";
35 //              soap.passwd = "admin";
36 //              ...
37 //              soap_done(&soap);
38 //
39 //----------------------------------------------------------------------------
40
41 #include "httpDigest.h"
42 #include "digcalc.h"
43
44 #define HDR_SIZE 1024
45
46 const char HTTP_DIGEST_ID[12] = "HTTP-Digest"; // plugin identification 
47
48 struct http_digest_data 
49 {
50     struct soap *soap; // back pointer 
51
52     // send buffer parameters
53     char *sendBuffer;        // send buffer 
54     size_t sendMsgSize;      // total length of the message 
55     size_t sendBufferLength; // length of data in buffer 
56
57     // receive buffer parameters
58     char *rcvBuffer; // receive buffer 
59     size_t rcvSize;  // length of buffer 
60     size_t rcvIdx;   // currecnt index 
61     size_t rcvRead;  // amount of data read 
62
63     // open parameters
64     char *endpoint;
65     char *host;
66     int port;
67
68     // function pointers
69     int (*fopen)(struct soap*, const char*, const char*, int); // open function 
70     int (*fsend)(struct soap*, const char*, size_t); // send function 
71     size_t (*frecv)(struct soap*, char*, size_t);    // receive function 
72     int (*fposthdr)(struct soap*, const char*, const char*); // post header function 
73
74     char *username;
75     char *password;
76     char requestUrl[128];
77     char method[5];
78 };
79
80 static int http_digest_init(struct soap *soap, struct http_digest_data *data);
81 static int http_digest_copy(struct soap *soap, struct soap_plugin *dst, 
82                             struct soap_plugin *src);
83 static void http_digest_delete(struct soap *soap, struct soap_plugin *p);
84
85 static int http_digest_fopen(struct soap *soap, const char *endpoint, const char *host, 
86                              int port);
87 static int http_digest_fsend(struct soap *soap, const char *buf, size_t size);
88 static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size);
89 static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value);
90
91 /*
92  * The entry point for HTTP digest plugin
93  * Arguments:
94  *  soap        - pointer to the soap runtime environment 
95  *  p           - pointer to the soap plugin
96  *  arg         - reserved. Should be NULL
97  *
98  * Returns SOAP_OK for suceess. Other values for error
99  */
100 int http_digest(struct soap *soap, struct soap_plugin *p, void *arg) 
101 {
102     p->id = HTTP_DIGEST_ID;
103     p->data = (void*)malloc(sizeof(struct http_digest_data));
104     p->fcopy = http_digest_copy;
105     p->fdelete = http_digest_delete;
106     if (p->data) {
107         memset(p->data, 0, sizeof(struct http_digest_data));
108         if (http_digest_init(soap, (struct http_digest_data*)p->data)) {
109             free(p->data); 
110             return SOAP_EOM;
111         }
112         return SOAP_OK;
113     }
114     return SOAP_EOM;
115 }
116
117 /*
118  * Initializes the http digest data structure.
119  * Arguments:
120  *  soap        - pointer to the soap runtime environment 
121  *  data        - pointer to the http digest data structure
122  *
123  * Returns SOAP_OK for suceess. Other values for error
124  */
125 static int http_digest_init(struct soap *soap, struct http_digest_data *data) 
126 {
127     data->soap = soap;
128     data->fopen = soap->fopen;
129     soap->fopen = http_digest_fopen;
130     data->fsend = soap->fsend;
131     soap->fsend = http_digest_fsend;
132     data->frecv = soap->frecv;
133     soap->frecv = http_digest_frecv;
134     data->fposthdr = soap->fposthdr;
135     soap->fposthdr = http_digest_fposthdr;
136
137     return SOAP_OK;
138 }
139
140 /*
141  * Creates the HTTP digest response
142  * Arguments:
143  *  userName    - the user name
144  *  password    - the password
145  *  method      - the HTTP method ("GET", "POST)     
146  *  realm       - the realm for the authentication
147  *  uri         - the URI from the HTTP request
148  *  nonce       - the nonce from the challenge
149  *  cnonce      - client generated nonce
150  *  digestResponse - The result authorization string
151  *  length      - size of buffer for response
152  *
153  * Returns 0 for suceess. -1 for error
154  */
155 int CalculateResponse(char *userName, char *password, char *method, char *realm, char *uri,
156                        char *nonce, char *cnonce, char *digestResponse, size_t *length) 
157 {
158     size_t currOffset = 0, segmentLength;
159     static const char *INITIAL_HDR = "Authorization: Digest username=\"";
160     static const char *REALEM_HDR = "\", realm=\"";
161     static const char *ALGO_HDR = "\", qop=\"auth\", algorithm=\"MD5\", uri=\"";
162     static const char *NONCE_HDR = "\", nonce=\"";
163     static const char *NC_HDR = "\", nc=00000002, cnonce=\"";
164     static const char *RSP_HDR = "\", response=\"";
165
166     HASHHEX HA1;
167     HASHHEX HA2 = "";
168     HASHHEX response;
169
170     //"Authorization: Digest username="
171     segmentLength = strlen(INITIAL_HDR);
172     if (*length < (currOffset + segmentLength)) {
173         return -1;
174     }
175     memcpy(digestResponse + currOffset, INITIAL_HDR, segmentLength);
176     currOffset += segmentLength;
177
178     //"Authorization: Digest username="username
179     segmentLength = strlen(userName);
180     if (*length < (currOffset + segmentLength)) {
181         return -1;
182     }
183     memcpy(digestResponse + currOffset, userName, segmentLength);
184     currOffset += segmentLength;
185
186     //"Authorization: Digest username="username", realm="
187     segmentLength = strlen(REALEM_HDR);
188     if (*length < (currOffset + segmentLength)) {
189         return -1;
190     }
191     memcpy(digestResponse + currOffset, REALEM_HDR, segmentLength);
192     currOffset += segmentLength;
193
194     //"Authorization: Digest username="username", realm="realm
195     segmentLength = strlen(realm);
196     if (*length < (currOffset + segmentLength)) {
197         return -1;
198     }
199     memcpy(digestResponse + currOffset, realm, segmentLength);
200     currOffset += segmentLength;
201
202     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
203     //algorithm="MD5", uri="
204     segmentLength = strlen(ALGO_HDR);
205     if (*length < (currOffset + segmentLength)) {
206         return -1;
207     }
208     memcpy(digestResponse + currOffset, ALGO_HDR, segmentLength);
209     currOffset += segmentLength;
210
211     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
212     //algorithm="MD5", uri="/....Service
213     segmentLength = strlen(uri);
214     if (*length < (currOffset + segmentLength)) {
215         return -1;
216     }
217     memcpy(digestResponse + currOffset, uri, segmentLength);
218     currOffset+= segmentLength;
219
220     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
221     //algorithm="MD5", uri="/....Service", nonce="
222     segmentLength = strlen(NONCE_HDR);
223     if (*length < (currOffset + segmentLength)) {
224         return -1;
225     }
226     memcpy(digestResponse + currOffset, NONCE_HDR, segmentLength);
227     currOffset += segmentLength;
228
229     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
230     //algorithm="MD5", uri="/....Service", nonce="7a5c...
231     segmentLength = strlen(nonce);
232     if (*length < (currOffset + segmentLength)) {
233         return -1;
234     }
235     memcpy(digestResponse + currOffset, nonce, segmentLength);
236     currOffset += segmentLength;
237
238     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
239     //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
240     //cnonce="
241     segmentLength = strlen(NC_HDR);
242     if (*length < (currOffset + segmentLength)) {
243         return -1;
244     }
245     memcpy(digestResponse + currOffset, NC_HDR, segmentLength);
246     currOffset += segmentLength;
247
248     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
249     //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
250     //cnonce="ab341...
251     segmentLength = strlen(cnonce);
252     if (*length < (currOffset + segmentLength)) {
253         return -1;
254     }
255     memcpy(digestResponse + currOffset, cnonce, segmentLength);
256     currOffset += segmentLength;
257
258     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
259     //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
260     //cnonce="ab341...", response="
261     segmentLength = strlen(RSP_HDR);
262     if (*length < (currOffset + segmentLength)) {
263         return -1;
264     }
265     memcpy(digestResponse + currOffset, RSP_HDR, segmentLength);
266     currOffset += segmentLength;
267
268     //calc response
269     DigestCalcHA1("MD5", userName, realm, password, nonce, cnonce, HA1);
270     DigestCalcResponse(HA1, nonce, "00000002", cnonce, "auth", method, uri,
271                        HA2, response);
272
273     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
274     //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
275     //cnonce="ab341...", response="8bbf2...
276     segmentLength = strlen(response);
277     if (*length < (currOffset + segmentLength)) {
278         return -1;
279     }
280     memcpy(digestResponse + currOffset, response, segmentLength);
281     currOffset += segmentLength;
282
283     //"Authorization: Digest username="username", realm="myRealm", qop="auth",
284     //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
285     //cnonce="ab341...", response="8bbf2..."
286     if (*length < (currOffset + 2)) {
287         return -1;
288     }
289     memcpy(digestResponse + currOffset, "\"", 1);
290     currOffset += 1;
291
292     //add null termination
293     *(digestResponse+currOffset) = 0;
294     *length = currOffset;
295
296     return 0;
297 }
298
299 /*
300  * generate a 32 byte random hexadecimal string such as "4f6ba982..."
301  * Arguments:
302  *  outbuff     - buffer to fill 
303  */
304 void GenerateCNonce(char *outbuff) 
305 {
306     srand((unsigned int)time(NULL));
307     for(int i = 0; i < 32; i++) {
308         int num = (int)(((double) rand()/ ((double)RAND_MAX+1)) * 16);
309         switch(num) {
310         case 0: case 1: case 2: case 3: case 4: case 5:
311         case 6: case 7: case 8: case 9: 
312             outbuff[i] = '0' + num;
313             break;
314         case 10: case 11: case 12: case 13: case 14: case 15:
315             outbuff[i] = 'a' + (num-10);
316             break;
317         default:
318             outbuff[i] = 'f';
319         }
320     }
321     outbuff[32] = 0;
322 }
323
324 /*
325  * Creates the HTTP digest response
326  * Arguments:
327  *  data        - the HTTP digest structure
328  *  authHeader - the HTTP digest challenge
329  *  responseLength - size of response buffer
330  *  digestResponse - buffer for HTTP digest response
331  *
332  * Returns 0 for suceess. -1 for error
333  */
334 int CreateDigestResponse(struct http_digest_data *data, char *authHeader,
335                           size_t *responseLength, char *digestResponse) 
336 {
337     char cnonce[33];
338     char *realmPtrStart, *realmPtrEnd, *noncePtrStart, *noncePtrEnd;
339     size_t segmentLength;
340     char realm[HDR_SIZE], nonce[HDR_SIZE];;
341     
342     if (digestResponse == NULL || authHeader == NULL) {
343         return -1;
344     }
345
346     GenerateCNonce(cnonce);
347
348     //grep realm from challange
349     realmPtrStart = strstr(authHeader, "realm=");
350     if (realmPtrStart == NULL) {
351         return -1;
352     }
353     //point to start of realm
354     realmPtrStart += 7;
355     realmPtrEnd = strstr(realmPtrStart, "\"");
356     segmentLength = realmPtrEnd - realmPtrStart;
357     memcpy(realm, realmPtrStart, segmentLength);
358     //add NULL termination
359     realm[segmentLength] = 0;
360
361     //grep nonce from challange
362     noncePtrStart = strstr(authHeader, "nonce=");
363     if (noncePtrStart == NULL) {
364         return -1;
365     }
366     //point to start of nonce
367     noncePtrStart += 7;
368     noncePtrEnd = strstr(noncePtrStart, "\"");
369     segmentLength = noncePtrEnd - noncePtrStart;
370     memcpy(nonce, noncePtrStart, segmentLength);
371     //add NULL termination
372     nonce[segmentLength]=0;
373
374     return CalculateResponse(data->username, data->password, data->method, realm, 
375                              data->requestUrl, nonce, cnonce,digestResponse, responseLength);
376 }
377
378
379 /*
380  * Copies the contents of the plugin
381  * Arguments:
382  *  soap        - pointer to the soap runtime environment
383  *  dst     - the destination plugin
384  *  src     - the original plugin
385  *
386  * Returns SOAP_OK for suceess. Error value for error
387  */
388 static int http_digest_copy(struct soap *soap, struct soap_plugin *dst, 
389                             struct soap_plugin *src) 
390 {
391     *dst = *src;
392     dst->data = (void*)malloc(sizeof(struct http_digest_data));
393     if (!dst->data) {
394         return SOAP_EOM;
395     }
396     memcpy(dst->data, src->data, sizeof(struct http_digest_data));
397
398     ((struct http_digest_data*)dst->data)->sendBuffer = NULL;
399     return SOAP_OK;
400 }
401
402 /*
403  * Deletes the contents of the plugin
404  * Arguments:
405  *  soap        - pointer to the soap runtime environment
406  *  p       - the plugin
407  */
408 static void http_digest_delete(struct soap *soap, struct soap_plugin *p) 
409 {
410     struct http_digest_data *data = 
411         (struct http_digest_data*)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
412
413     if (data) {
414         if (data->sendBuffer) {
415             free(data->sendBuffer);
416         }
417         free(data);
418     }
419 }
420
421 /*
422  * Open function. Will be called when connection opened. Used for saving parameters.
423  * Arguments:
424  *  soap         - pointer to the soap runtime environment
425  *  endpoint - the URL
426  *  host     - machine to connect to
427  *  port     - port on the host
428  *
429  * Returns SOAP_OK for suceess. Error value for error 
430  */
431 static int http_digest_fopen(struct soap *soap, const char *endpoint, const char *host, 
432                              int port) 
433 {
434     struct http_digest_data *data =
435                     (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
436     data->endpoint = (char*)endpoint;
437     data->host = (char*)host;
438     data->port = port;
439     return data->fopen(soap, endpoint, host, port);
440 }
441
442 /*
443  * Post header function. Used to identify parameters of the HTTP message
444  * Arguments:
445  *  soap         - pointer to the soap runtime environment
446  *  key      - the header key
447  *  value    - the header value
448  *
449  * Returns SOAP_OK for suceess. Error value for error 
450  */
451 static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value) 
452 {
453     struct http_digest_data *data = 
454         (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
455     char *s1, *s2;
456
457     // if this is the initial POST header then we initialize our send buffer 
458     if (key && !value) {
459         data->sendMsgSize = 0;
460         data->sendBufferLength = 0;
461
462         if (data->sendBuffer) {
463             free(data->sendBuffer);
464             data->sendBuffer = NULL;
465         }
466
467         data->password = soap->passwd;
468         data->username = soap->userid;   
469         soap->passwd = soap->userid = NULL;
470         soap->frecv = http_digest_frecv;
471     
472         // read the method and URI form the key
473         if (!strncmp(key, "GET", 3))
474             memcpy(data->method, "GET", 4);
475         else if (!strncmp(key, "POST", 4))
476             memcpy(data->method, "POST", 5);
477         else
478             return soap->error = SOAP_EOM;
479
480         s1 = strstr(key, " ");
481         if (!s1) {
482             return soap->error = SOAP_EOM;
483         }
484         s1++;
485
486         s2 = strstr(s1, " ");
487         if (!s2) {
488             return soap->error = SOAP_EOM;
489         }
490         
491         if (sizeof(data->requestUrl) <= (size_t)(s2 - s1)) {
492             return soap->error = SOAP_EOM;
493         }
494         memcpy(data->requestUrl, s1, s2-s1);
495         data->requestUrl[s2-s1] = '\0';
496
497     } else if (value) {
498         // determine the maximum length of this message so that we can
499         // correctly determine when we have completed the send 
500         if (!strcmp(key, "Content-Length" )) {
501             data->sendMsgSize += strtoul(value, NULL, 10);
502         }
503     }
504
505     // calculate the header size
506     data->sendMsgSize += 2;
507     if (key) {
508         data->sendMsgSize += strlen(key);
509         if (value) {
510             data->sendMsgSize += (strlen(value) + 2);
511         }
512     }
513     return data->fposthdr(soap, key, value);
514 }
515
516 /*
517  * Send function. Used to buffer the sent data and save for resends
518  * Arguments:
519  *  soap         - pointer to the soap runtime environment
520  *  buf      - buffer to be sent
521  *  size     - size of data to send
522  *
523  * Returns SOAP_OK for suceess. Error value for error 
524  */
525 static int http_digest_fsend(struct soap *soap, const char *buf, size_t size) 
526 {
527     struct http_digest_data *data =
528                     (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
529     size_t newBufferLen = data->sendBufferLength + size;
530
531     if (!data->sendBuffer || (newBufferLen > data->sendMsgSize)) {
532         if (newBufferLen > data->sendMsgSize) {
533             data->sendMsgSize = newBufferLen;
534         }
535         data->sendBuffer = (char *)realloc(data->sendBuffer, data->sendMsgSize);
536         if (!data->sendBuffer) {
537             return SOAP_EOM;
538         }
539     }
540     memcpy(data->sendBuffer + data->sendBufferLength, buf, size);
541     data->sendBufferLength = newBufferLen;
542
543     // if we haven't got the entire length of the message yet, then
544     // we return to gsoap and let it continue 
545     if (data->sendBufferLength < data->sendMsgSize) {
546         return SOAP_OK;
547     }
548
549     // we've now got the entire message, now we can send the buffer 
550     return data->fsend(soap, data->sendBuffer, data->sendBufferLength);
551 }
552
553 /*
554  * Reads the next character. May need to read from the network
555  * Arguments:
556  *  data         - pointer to the http digest structure
557  *
558  * Returns the next character or EOF for failure. 
559  */
560 static char http_digest_getchar(struct http_digest_data *data) 
561 {
562     size_t res;
563     if (data->rcvIdx < data->rcvRead)
564         return data->rcvBuffer[data->rcvIdx++];
565     res = data->frecv(data->soap, (data->rcvBuffer + data->rcvRead),
566                       (data->rcvSize - data->rcvRead));
567     if (res <= 0)
568         return EOF;
569     data->rcvRead += res;
570     return data->rcvBuffer[data->rcvIdx++];
571 }
572
573 /*
574  * Reads the next HTTP header line.
575  * Arguments:
576  *  data         - pointer to the http digest structure
577  *  line     - buffer to store the line read
578  *  len      - length of the line buffer
579  *
580  * Returns SOAP_OK for suceess. Error value for error. 
581  */
582 static int http_digest_getline(struct http_digest_data *data, char *line, size_t len) 
583 {
584     unsigned int i = len;
585     char c = 0, *s = line;
586     for (;;) {
587         while (--i > 0) {
588             c = http_digest_getchar(data);
589             if (c == '\r')
590                 break;
591             if (c == EOF)
592                 return SOAP_EOF;
593             *s++ = c;
594         }
595         c = http_digest_getchar(data);
596         if (c == '\n') {
597             *s = '\0';
598             if (i+1 == len) // empty line: end of HTTP header
599                 break;
600             c = http_digest_getchar(data);
601             data->rcvIdx--; // return to previous character
602             if (c != ' ' && c != '\t') // HTTP line continuation? 
603                 break;
604         } else if ((int)c == EOF)
605             return SOAP_EOF;
606     }
607     return SOAP_OK;
608 }
609
610 /*
611  * receive function. Used to look for digest authentication challenge.
612  * If the challenge is found will calculate the response and resend.
613  * Arguments:
614  *  soap         - pointer to the soap runtime environment
615  *  buf      - buffer read data into
616  *  size     - size of buf
617  *
618  * Returns number of characters read. 0 for error. 
619  */
620 static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size) {
621     struct http_digest_data *data =
622                     (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
623     char header[HDR_SIZE], authResponse[HDR_SIZE];
624     static const char *CHALLANGE = "WWW-Authenticate: Digest";
625     static const unsigned int SYN_DELAY = 400000;
626     char *s;
627     unsigned long httpStatus;
628     size_t len;
629     int found = 0;
630
631     data->rcvBuffer = buf;
632     data->rcvSize = size;
633     data->rcvIdx = 0;
634     data->rcvRead = 0;
635
636     do {
637         if (http_digest_getline(data, header, HDR_SIZE))
638             goto _out;
639         if ((s = strchr(header, ' ')))
640             httpStatus = soap_strtoul(s, NULL, 10);
641         else
642             httpStatus = 0;
643
644         if ((httpStatus != 100) && (httpStatus != 401))
645             goto _out;
646
647         for (;;) {
648             if (http_digest_getline(data, header, SOAP_HDRLEN))
649                 goto _out;
650
651             if (!*header)
652                 break;
653
654             if ((httpStatus == 401) && !strncmp(header, CHALLANGE, strlen(CHALLANGE))) {
655                 found = 1;
656                 break;
657             }
658         }
659     } while (httpStatus == 100);
660
661     // if we got here httpStatus==401
662     if (!found)
663         goto _out;
664
665     // header is HTTP digest challenge
666     len = HDR_SIZE;
667     if (CreateDigestResponse(data, header, &len, authResponse))
668         goto _out;
669
670     s = strstr(data->sendBuffer, "\r\n");
671     if (!s)
672         goto _out;
673
674     s += 2; // point to the start of second line
675
676     // reset rcvRead so that if error occurs will return 0.
677     data->rcvRead = 0;
678
679     //delay sending SYN to allow AMT to close connection gracefully
680     usleep(SYN_DELAY); 
681     
682     soap->socket = data->fopen(soap, data->endpoint, data->host, data->port);
683     if (soap->error || !soap_valid_socket(soap->socket))
684         goto _out;
685     
686     if (data->fsend(soap, data->sendBuffer, s-data->sendBuffer) || 
687         data->fsend(soap, authResponse, len) ||
688         data->fsend(soap, "\r\n", 2) ||
689         data->fsend(soap, s, data->sendBufferLength - (s-data->sendBuffer)))
690         goto _out;
691     
692     // after send - send FIN if needed
693 #ifdef WITH_OPENSSL
694     if (!soap->ssl && soap_valid_socket(soap->socket) && !soap->keep_alive)
695         soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN 
696 #else
697     if (soap_valid_socket(soap->socket) && !soap->keep_alive)
698         soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN 
699 #endif
700
701     data->rcvRead = data->frecv(soap, buf, size);
702
703 _out:
704     if (data->sendBuffer) {
705         free(data->sendBuffer);
706         data->sendBuffer = NULL;
707     }
708     soap->frecv = data->frecv;
709     return data->rcvRead;
710 }
711
712
713
714
715