MQL4: How to read a CSV from a URL - algorithmic-trading

I'm using this URL to fetch some content from a Quandl website:
https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv?exclude_column_names=true&rows=1&api_key=my_api_key
The Quandl server returns in response to the above request a value of this:
2016-08-01, 144598.0
I need to use the value of 144598.0 within an MQL4 Script, so:
Q1. How do I fetch the content from the URL above to be used within an MQL4 Script?
A very helpful user from SO (https://stackoverflow.com/users/3666197/user3666197) provided the following script (original found at MQL4: Read single value from CSV) (a couple parts added in by myself) to help me achieve this, however, I couldn't get it to work:
// Code created with the help of Stack Overflow question
// https://stackoverflow.com/questions/39279634/mql4-read-single-value-from-csv/39284875#39284875
// Question by p.luck:
// https://stackoverflow.com/users/5551849/p-luck
// Answer by user3666197:
// https://stackoverflow.com/users/3666197/user3666197
void OnStart()
{
string cookie = NULL,
headers;
char post[],
result[];
int res;
/* TODO: *
* Must allow MT4 to access the server URL, *
* you should add URL "https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv" *
* in the list of allowed URLs *
* ( MT4 -> Tools -> Options -> [Tab]: "Expert Advisors" ): */
string aDataSOURCE_URL = "https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv";
string aDataSOURCE_API = "?exclude_column_names=true&rows=1&api_key=my_api_key";
//-- Create the body of the POST request for API specifications and API-authorization
ArrayResize( post,
StringToCharArray( aDataSOURCE_API, // string text |--> [in] String to copy.
post, // uchar &array[] <--| [out] Array of uchar type.
0, // int start = 0 |--> [in] Position from which copying starts. Default - 0.
WHOLE_ARRAY, // int count = -1 |--> [in] Number of array elements to copy. Defines length of a resulting string. Default value is -1, which means copying up to the array end, or till terminating '\0'. Terminating zero will also be copied to the recipient array, in this case the size of a dynamic array can be increased if necessary to the size of the string. If the size of the dynamic array exceeds the length of the string, the size of the array will not be reduced.
CP_UTF8 // uint cp = CP_ACP |--> [in] The value of the code page. For the most-used code pages provide appropriate constants.
)
- 1
);
//-- Reset the last error code
ResetLastError();
//-- Loading a html page from Quandl
int timeout = 5000; //-- Timeout below 1000 (1 sec.) is not enough for slow Internet connection
res = WebRequest( "POST", // const string method |--> [in] HTTP method.
aDataSOURCE_URL, // const string URL |--> [in] URL.
cookie, // const string cookie |--> [in] Cookie value.
NULL, // const string referrer |--> [in] Value of the Referer header of the HTTP request.
timeout, // int timeout |--> [in] Timeout in milliseconds.
post, // const char &data |--> [in] Data array of the HTTP message body
ArraySize( post ), // int data_size |--> [in] Size of the data[] array.
result, // char &result <--| [out] An array containing server response data.
headers // string &result_headers <--| [out] Server response headers.
);
//-- Check errors
if ( res == -1 )
{ Print( "WebRequest Error. Error code = ", GetLastError() ); //-- Perhaps the URL is not listed, display a message about the necessity to add the address
MessageBox( "Add the address '" + aDataSOURCE_URL + "' in the list of allowed URLs on tab 'Expert Advisors'", "Error", MB_ICONINFORMATION );
}
else //-- Load was successfull
{
PrintFormat( "The data has been successfully loaded, size = %d bytes.", ArraySize( result ) );
//-- parse the content ---------------------------------------
/*
"2016-08-01, 144598.0"
*/
//-- consume the content -------------------------------------
//...
}
}
I have added the URL of https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csvto the list of allowed URLs in MT4.
If I enable AutoTrading and drag the script, named (tutorial7), on to a chart of USDCAD,M1,I get these messages within the Experts tab:
Script tutorial7 USDCAD,M1: loaded successfuly
tutorial7 USDCAD,M1: initialized
tutorial7 USDCAD,M1: The data has been successfully loaded, size = 0 bytes
tutorial7 USDCAD,M1: uninit reason 0
Surely if the "The data has successfully loaded" it shouldn't say "size = 0 bytes"?
Should this script work correctly if I just copy and paste it straight in to the MetaQuotes Language Editor and compile it?
Apart from adding the URL to the allowed URLs in MT4 and copying and pasting this code in to a script, is there anything else I must do?
If so, how?
I am running the above code as a Script not an Expert Advisor; is this okay?
Q2. Can I set the fetched value of 144598.0 as a variable within my script?
I need to make the value of 144598.0 a variable so that it can be compared to another value.
Would something like this work:
void OnStart()
{
... // above code which fetches the value from the .csv URL
double x = 155876.0 // value manually typed in
y = 144598.0 // value fetched from the .csv URL using the above code
// ignores column 1 consisting of 2016-08-01
if ( x > y ) {
// execute code
}
else {
// execute other code
}
}

A2: YES! This is the easiest part of the journey.A1: BUT!How does it work in practice? How does MetaTrader Terminal actually speak to Quandl, so to get it?
Let me first illustrate the issue of remote, HttpServer-side processing.
There is an easy to prototype program curl ( Linux & DOS versions available ) that will show right inside a terminal window ( or a Windows cmd window ) how the remote HttpServer at Quandl responds to various compositions of the locally assembled requests, communicated over the HTTP-protocol.
Notice, that the just retyped URL produces the whole history to be delivered.
C:\>curl https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
DATE,VALUE
2016-08-01,144598.0
2016-07-01,144447.0
...
..
.
1939-03-01,30280.0
1939-02-01,30101.0
1939-01-01,29923.0
Once we add further parameters to the plain URL, the remote-side ( the HttpServer ) changes the reply to just the one row we are interested in:
C:\>curl -G --data rows=1 --data exclude_column_names=true https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
2016-08-01,144598.0
Cool, looks great, almost the single value we want!
But here the magic comes.
The real interchange ( dialogue ) between the local process ( curl now, but MetaTrader Terminal later ) looks this way ( using a --verbose option in curl commandline ):
C:\>curl --verbose -G --data rows=1 --data exclude_column_names=true https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
* Trying 54.174.87.84...
* Connected to www.quandl.com (54.174.87.84) port 443 (#0)
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 70 bytes...
* schannel: sent initial handshake data: sent 70 bytes
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 4096 length 4096
* schannel: encrypted data length: 4017
* schannel: encrypted data buffer: offset 4017 length 4096
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 4569 length 5041
* schannel: sending next handshake data: sending 318 bytes...
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 51 length 5041
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 3/3)
* schannel: incremented credential handle refcount = 1
* schannel: stored credential handle in session cache
> GET /api/v3/datasets/FRED/PAYEMS.csv?rows=1&exclude_column_names=true HTTP/1.1
> Host: www.quandl.com
> User-Agent: curl/7.45.0
> Accept: */*
>
* schannel: client wants to read 16384 bytes
* schannel: encdata_buffer resized 17408
* schannel: encrypted data buffer: offset 0 length 17408
* schannel: encrypted data got 653
* schannel: encrypted data buffer: offset 653 length 17408
* schannel: decrypted data length: 623
* schannel: decrypted data added: 623
* schannel: decrypted data cached: offset 623 length 16384
* schannel: encrypted data buffer: offset 0 length 17408
* schannel: decrypted data buffer: offset 623 length 16384
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 623
* schannel: decrypted data buffer: offset 0 length 16384
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Disposition: attachment; filename=FRED-PAYEMS.csv
< Content-Transfer-Encoding: binary
< Content-Type: text/csv
< Date: Wed, 07 Sep 2016 12:47:20 GMT
< ETag: W/"adfdb97850c493cdd03e2036574bc404"
< Server: openresty
< Vary: Origin
< X-API-Version: 2015-04-09
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Rack-CORS: preflight-hit; no-origin
< X-RateLimit-Limit: 50
< X-RateLimit-Remaining: 42
< X-Request-Id: c609e92d-22d2-40e7-b7d4-cacb07467c76
< X-Runtime: 0.023534
< X-XSS-Protection: 1; mode=block
< Content-Length: 20
< Connection: keep-alive
<
2016-08-01,144598.0
* Connection #0 to host www.quandl.com left intact
Notice the row GET /api/v3/datasets/FRED/PAYEMS.csv?rows=1&exclude_column_names=true
So the magic is to make MetaTrader Terminal to assemble the same, together with allowing the URL in the permitted list in the configuration ( that you have done already in the other post ).
Also might have noticed, that the HTTP GET sends just the <HttpServer_relative_part_of_the.URL>
The magic is in making MQL4 code send the same request as was seen above and get the data back.
WebRequest() has to use HTTP GET as the Quandl HttpServer does not respond to a HTTP POST version of the same request example, returning 0 bytes ( just omit the -G switch from the curl examples above ).
Meeting all the conditions at once should result in receiving 2016-08-01,144598.0 and using:
int anAmountOfBytesRECVd = 0; // how many bytes Quandl sent
string headers_FromHttpSERVER; // stores as a string
char anAnswerFromHttpSERVER[]; // stores as a byte-array ( char[] )
double y_value = NULL;
anAmountOfBytesRECVd = WebRequest( "GET", // MUST use HTTP GET <- Quandl tested
...
anAnswerFromHttpSERVER,
headers_FromHttpSERVER
);
y_value = StrToDouble( CharArrayToString( anAnserFromHttpSERVER, // [2|0|1|6|-|0|8|-|0|1|,|1|4|4|5|98|.|0]
11, //-----------------------^_______________( -1 == TILL EndOfSTRING )
-1
)
);

Related

When using --negotiate with curl on windows, SSL/TLS handshake fails

When using --negotiate (or ntlm) with curl on windows, SSL/TSL handshake fails despite having a valid kerberos ticket cached on my windows 10 (shown below). The same logic and commands works without any issue in Unix/Linux. Any idea/help on how to resolve this issue?
Klist details:
$: Klist
Client: username # XXXX.XXX
Server: cifs/XXXXXXX.XXX # XXXXXXX.XXX
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags XXXXXX -> forwardable renewable pre_authent name_canonicalize
Start Time: 8/27/2020 9:46:36 (local)
End Time: 8/27/2020 19:46:33 (local)
Renew Time: 9/27/2020 9:46:33 (local)
Session Key Type: AES-256-CTS-HMAC-SHA1-96
Cache Flags: 0
Kdc Called: XXXXXXXXX.XXX
curl command using ntlm or negotiate details:
>curl --ntlm -u : https://XXXXX.XXX -v
OR
>curl --negotiate -u : https://XXXXX.XXX -v
* Trying xx.xx.xx.xx...
* TCP_NODELAY set
* Connected to xxxx.xxx (xx.xx.xx.xx) port xxx (#0)
* schannel: SSL/TLS connection with xxxx.xxx port xxx (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 186 bytes...
* schannel: sent initial handshake data: sent 186 bytes
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 2/3)
* schannel: encrypted data got 4096
* schannel: encrypted data buffer: offset 4096 length 4096
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 2/3)
* schannel: encrypted data got 1024
* schannel: encrypted data buffer: offset 5120 length 5120
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 2/3)
* schannel: encrypted data got 817
* schannel: encrypted data buffer: offset 5937 length 6144
* schannel: sending next handshake data: sending 126 bytes...
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 2/3)
* schannel: encrypted data got 51
* schannel: encrypted data buffer: offset 51 length 6144
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with xxx.xxx port xxx (step 3/3)
* schannel: stored credential handle in session cache
> GET /login HTTP/1.1
> Host: xxx.xxx
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 915
* schannel: encrypted data buffer: offset 915 length 103424
* schannel: decrypted data length: 852
* schannel: decrypted data added: 852
* schannel: decrypted data cached: offset 852 length 102400
* schannel: encrypted data length: 34
* schannel: encrypted data cached: offset 34 length 103424
* schannel: decrypted data length: 5
* schannel: decrypted data added: 5
* schannel: decrypted data cached: offset 857 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 857 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 857
* schannel: decrypted data buffer: offset 0 length 102400
< HTTP/1.1 401
< Cache-Control: private
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-Application-Context: Apixxxxx:x
< X-RateLimit-Limit-Api_login_anonymous: 1000
< X-RateLimit-Remaining-Api_login_anonymous: 999
< X-RateLimit-Reset-Apixxx_login_anonymous: 0
< X-xxxxx-xxx: xxxxx.xxx
< Date: Thu, 27 Aug 2020 19:50:12 GMT
< Expires: 0
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: DENY
< WWW-Authenticate: Negotiate
< WWW-Authenticate: Basic realm="Please login with your Windows account"
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Strict-Transport-Security: max-age=xxx ; includeSubDomains
< TraceId: 5f480e7427f78fd5406fcbef0abf8e6c
< X-Content-Type-Options: nosniff
< Transfer-Encoding: chunked
<
* Ignoring the response-body
* Connection #0 to host xxx.xxx left intact
* Issue another request to this URL: 'https://xxx.xxx/login'
* Found bundle for host xxx.xxx: 0xxxxxxxxx [can pipeline]
* Re-using existing connection! (#0) with host xxx.xxx
* Connected to xxx.xxx (xx.xx.xxx.xx) port xxx (#0)
* Server auth using Negotiate with user ' '
> GET /login HTTP/1.1
> Host: xxx.xxx
> Authorization: Negotiate xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 814
* schannel: encrypted data buffer: offset 814 length 103424
* schannel: decrypted data length: 751
* schannel: decrypted data added: 751
* schannel: decrypted data cached: offset 751 length 102400
* schannel: encrypted data length: 34
* schannel: encrypted data cached: offset 34 length 103424
* schannel: decrypted data length: 5
* schannel: decrypted data added: 5
* schannel: decrypted data cached: offset 756 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 756 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 756
* schannel: decrypted data buffer: offset 0 length 102400
< HTTP/1.1 401
< Cache-Control: private
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-Application-Context: Apixxx:xxxxxxxxxxxxxxxx:x
< X-RateLimit-Limit-Apixxx_login_anonymous: 1000
< X-RateLimit-Remaining-Apixxx_login_anonymous: 999
< X-RateLimit-Reset-Apixxx_login_anonymous: 0
< X-XXXX-xxx: xxxx.xxx
< Date: Thu, 27 Aug 2020 19:50:12 GMT
< Expires: 0
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: DENY
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Strict-Transport-Security: max-age=xxx ; includeSubDomains
< TraceId: xxxxxxxxxxxxxxxxxxxxxxxxxxx
< X-Content-Type-Options: nosniff
< Transfer-Encoding: chunked
<
* Connection #0 to host xxx.xxx left intact
>curl --version
curl 7.55.1 (Windows) libcurl/7.55.1 WinSSL
Release-Date: [unreleased]
Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile SSPI Kerberos SPNEGO NTLM SSL

Websocket handshake: Incorrect 'Sec-WebSocket-Accept' header value

I am writing a websocket server in C++ and am not able to get the handshake to work. Chrome reports the error is due to a bad accept header, but I believe the value to be correct.
As one example exchange, the client sends the following key:
Sec-WebSocket-Key: ypX0m2zum/pt80mxlVo8PA==
and my server sends back:
Sec-WebSocket-Accept: Kl4mnqm5QA6bBmGf3EAN0nyGXws=
I have tested my server against the example in the RFC and it checks out. I don't know why its not being accepted. My theory is that I must be doing something else that generates the same error as a bad accept value.
Here is different request from a wireshark capture:
Hypertext Transfer Protocol
GET /websocket HTTP/1.1\r\n
Host: 127.0.0.1:8443\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: chrome-extension://eajaahbjpnhghjcdaclbkeamlkepinbl\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: en-US,en;q=0.9\r\n
Sec-WebSocket-Key: +zJ3/KI/Zrumgh+AjxopRQ==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
\r\n
[Full request URI: http://127.0.0.1:8443/websocket]
[HTTP request 1/1]
[Response in frame: 6]
And here is the response:
Hypertext Transfer Protocol
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: anTEIFyI/gTepr8Q3okBj81M2/4=\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.000245010 seconds]
[Request in frame: 4]
Can someone tell me what is wrong with the response? Is my accept value incorrect?
EDIT 1:
The code I use to create the response value. The websocket_key is grabbed from the request prior to this.
const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
int pre_hash_size = 36 + websocket_key.size();
char pre_hash[pre_hash_size];
memcpy(pre_hash, websocket_key.c_str(), websocket_key.size());
memcpy(pre_hash + websocket_key.size(), magic_string, 36);
unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-1"));
Botan::secure_vector<uint8_t> post_hash = hash1->process(reinterpret_cast<const uint8_t *>(pre_hash), pre_hash_size);
string accept_response = base64_encode(post_hash.data(), post_hash.size());
Here is the base 64 function:
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
Version: 1.01.00
Copyright (C) 2004-2017 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger#adp-gmbh.ch
*/
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
{
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--)
{
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
The problem was that when I concatenated the pre_hash string from the websocket key (sent by client) and the magic string (constant), I didn't account for the null terminator that the size() function includes in it's count. an extra space I had inadvertently added when parsing the request header.
Remember kiddies, C++ strings are null terminated and size() reflects that.

Suppressing schannel output on Windows

I've been using cURL for a while now, though not upgrading the executable very often. I've noticed with v7.60 that when using -v to get the headers as well, I get a smattering of * schannel: messages in the prompt, so that it goes like this:
$ curl -vs "https://example.com"
* Rebuilt URL to: https://example.com/
* Trying 93.184.216.34...
* TCP_NODELAY set
* Connected to example.com (93.184.216.34) port 443 (#0)
* schannel: SSL/TLS connection with example.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 176 bytes...
* schannel: sent initial handshake data: sent 176 bytes
* schannel: SSL/TLS connection with example.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with example.com port 443 (step 2/3)
* schannel: encrypted data got 4096
* schannel: encrypted data buffer: offset 4096 length 4096
* schannel: encrypted data length: 14
* schannel: encrypted data buffer: offset 14 length 4096
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with example.com port 443 (step 2/3)
* schannel: encrypted data got 817
* schannel: encrypted data buffer: offset 831 length 4096
* schannel: sending next handshake data: sending 126 bytes...
* schannel: SSL/TLS connection with example.com port 443 (step 2/3)
* schannel: encrypted data got 242
* schannel: encrypted data buffer: offset 242 length 4096
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with example.com port 443 (step 3/3)
* schannel: stored credential handle in session cache
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.60.0
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 1666
* schannel: encrypted data buffer: offset 1666 length 103424
* schannel: decrypted data length: 338
* schannel: decrypted data added: 338
* schannel: decrypted data cached: offset 338 length 102400
* schannel: encrypted data length: 1299
* schannel: encrypted data cached: offset 1299 length 103424
* schannel: decrypted data length: 1270
* schannel: decrypted data added: 1270
* schannel: decrypted data cached: offset 1608 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 1608 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 1608
* schannel: decrypted data buffer: offset 0 length 102400
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: max-age=604800
< Content-Type: text/html; charset=UTF-8
< Date: Tue, 05 Feb 2019 08:08:57 GMT
< Etag: "1541025663"
< Expires: Tue, 12 Feb 2019 08:08:57 GMT
< Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
< Server: ECS (dca/24C1)
< Vary: Accept-Encoding
< X-Cache: HIT
< Content-Length: 1270
<
[HTML here]
Is there any way to suppress these messages so that -v still yields me the headers, and I can use > NUL to discard the response body, all without the SChannel noise?
To head off the obvious suggestion, -I won't work for me: I need to be able to send GET and other requests this way because my work involves investigating third-party servers that can (and have been known to) respond to HEAD differently.

how to copy message inside icmp header

Here is the code which I don't quite understand.
struct icmphdr *icmp;
icmp = (struct icmphdr *)(sb->data + sb->nh.iph->ihl * 4);
....
char *cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
memcpy(cp_data, buffer, 4);
dev_queue_xmit(sb);
Basically what it does is copy the buffer to cp_data, which points to somewhere in icmphdr structure, but where does cp_data exactly points to? what is (char *)((char *)icmp + sizeof(struct icmphdr))?
cp_data, which points to somewhere in icmphdr structure
No, it does not, it points to the memory after the icmphdr.
That's where the ICMP payload is. Look at this image:
The ICMP payload (message) starts after the header which has a size of sizeof(struct icmphdr) so it is located at icmp + sizeof(struct icmphdr).
memcpy(cp_data, buffer, 4); therefore copies four bytes from buffer to the ICMP packet message.
sb->data + sb->nh.iph->ihl * 4 actually skips the IP packet header and points to the ICMP header (look at the above image again). The IP header is at sb->data, the ICMP header at sb->data + sb->nh.iph->ihl * 4 and the ICMP message at sb->data + sb->nh.iph->ihl * 4 + sizeof(struct icmphdr).
For example, ping (echo request / reply) uses the ICMP message field to send the data forth and back. It can also be used for ICMP tunneling.
Update:
if I want to get the size of data section, just do "size = 1500(MTU) - (sizeof(iphdr) + sizeof(icmphdr) + sizeof(ethhdr)). Is that correct?
No, not at all, for the following reasons:
MTU is just the maximal packet size that can be transmitted without fragmentation. ICMP packets should actually be smaller.
The MTU does not include the ethernet header, it defines the maximal packet length in layer 3 (IP, not ethernet!).
sizeof(iphdr) is incorrect because the header size can vary based on IP options. Use iphdr.ihl to get the size of the IP header in 32 bit words.
The correct way is to determine the total IP packet length and subtract IP header length and ICMP header length:
tot_len = sb->nh.iph->tot_len
iphdr_len = sb->nh.iph->ihl * 4
icmphdr_len = sizeof(icmphdr)
size = tot_len - iphdr_len - icmphdr_len
Note: You should always use ntohs to convert network byte order to host byte order.
In the code you pasted,
struct icmphdr *icmp; //is a pointer to the beginning of icmp header in a packet.
& by this line-
char *cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
you make cp_data point to the beginning of the payload of the icmp packet.
2.((char *)icmp + sizeof(struct icmphdr)); - icmp is typecasted to (char*) so that the addition of (char*)icmp + sizeof(struct icmphdr) will return the address (icmpheader start address) + headersize bytes.
Here is an example -
suppose you have an integer pointer and you increment it by one, it points to the next integer(that is it automatically advances 4 bytes ahead),while a character pointer advances by a single byte since a char is 1 byte.
And so, since memcpy is used to copy bytes from a buffer to where cp_data now points(the payload of the icmp packet), (char*)icmp + sizeof(struct icmphdr) is again typecasted to (char*).
Hope this helps!

How to disable G-WAN servlet internal cache?

gwan version: 3.12.26
servlet type: C and Perl
problem:
gwan internal cache make request not re-read the script
test:
create 'log' dir :
[bash]# mkdir -p /dev/shm/random-c
[bash]# chmod 777 /dev/shm/random-c
create /path/to/gwan/0.0.0.0_8080/#0.0.0.0/csp/random.c
// ============================================================================
// C servlet sample for the G-WAN Web Application Server (http://trustleap.ch/)
// ----------------------------------------------------------------------------
// hello.c: just used with Lighty's Weighttp to benchmark a minimalist servlet
// ============================================================================
// imported functions:
// get_reply(): get a pointer on the 'reply' dynamic buffer from the server
// xbuf_cat(): like strcat(), but it works in the specified dynamic buffer
// ----------------------------------------------------------------------------
#include <sys/time.h>
#include "gwan.h" // G-WAN exported functions
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//------------------
void init_random(){
struct /*sys/time.h->*/timeval res;
/*sys/time.h->*/gettimeofday(&res,NULL);
/*stdlib.h->*/srand( (unsigned int)/*stdlib.h->*/time(NULL) + res.tv_usec);
}
//------------------
char *get_rnd_char(int num){
char *char_list = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int char_list_len = 62;
char *ret = (char *)/*stdlib.h->*/malloc((num * sizeof(char)) + 1);
int i,r;
for(i=0;i<num;i++){
r=(int) (/*stdlib.h->*/rand() % char_list_len);
ret[i] = char_list[r==char_list_len ? r-1 : r];
}
ret[num] = '\0';
return ret;
}
//------------------
int main(int argc, char *argv[])
{
char *rnd_out; //-- random data for browser output and file input
char *rnd_file; //-- random file
char *rnd_path; //-- for speed let's make on ramdisk /dev/shm/random-c/
char *t;
FILE *F;
int num_char=10;
int arg_cnt=1;
if(argc>0){
//-- why nobody love C ? one of the reason is these kind parsing thing
while ((t = /*string.h->*/strtok(argv[0], "=")) != NULL) {
argv[0] = NULL;
if(arg_cnt == 2){
num_char = /*stdlib.h->*/atoi(t);
}
arg_cnt++;
}
}else{
//-- get random number betwen 1 to 1000
num_char = (rand() % 1000)+1;
}
init_random();
//-- create random data
rnd_out = get_rnd_char(num_char);
//-- creating "log" path
//-- why nobody love C ? more reason
rnd_file = get_rnd_char(20);
// "/dev/shm/random-c/xxxxxxxxxxxxxxxxxxxx" -> 38 chars + 1 for \0
rnd_path = (char *)/*stdlib.h->*/malloc((38 * sizeof(char)) + 1);
rnd_path[0] = '\0';
/*string.h->*/strcat(rnd_path,"/dev/shm/random-c/");
/*string.h->*/strcat(rnd_path,rnd_file);
//-- save to file
F = /*stdio.h->*/fopen(rnd_path,"w");
/*stdio.h->*/fprintf(F,"%s",rnd_out);
/*stdio.h->*/fclose(F);
//-- send output to browser
/*gwan.h->*/xbuf_cat(get_reply(argv), rnd_out);
//-- cleanup memory
//-- why nobody love C ? MAIN reason: no easy way of memory management
/*stdlib.h->*/free(rnd_file);
/*stdlib.h->*/free(rnd_out);
/*stdlib.h->*/free(rnd_path);
return 200; // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================
run on browser:
http://localhost:8080/?random.c
then you should have one 20char random file at /dev/shm/random-c/
here the 'problem', run:
ab -n 1000 'http://localhost:8080/?random.c'
my ubuntu have output:
Finished 1000 requests
Server Software: G-WAN
Server Hostname: localhost
Server Port: 8080
Document Path: /?random.c
Document Length: 440 bytes
Concurrency Level: 1
Time taken for tests: 0.368 seconds
Complete requests: 1000
Failed requests: 361
(Connect: 0, Receive: 0, Length: 361, Exceptions: 0)
Write errors: 0
Total transferred: 556492 bytes
HTML transferred: 286575 bytes
Requests per second: 2718.73 [#/sec] (mean)
Time per request: 0.368 [ms] (mean)
Time per request: 0.368 [ms] (mean, across all concurrent requests)
Transfer rate: 1477.49 [Kbytes/sec] received
try:
[bash]# ls /dev/shm/random-c/
the directory only list 4 or 5 random files, which expected was 1000files
tested on random.c and perl's version random.pl
so the back to beginning question, how to disable GWAN internal cache, I try to read gwan user guide for set something in handler, but found nothing (or I miss something in that guide ).
thanks for GWAN team for this great product.
any answer welcome .. thanks
I think that the feature you are talking about is micro caching. To disable it, the URI needs to be unique on each request within 200 ms. (Like adding random number on URI)
The G-WAN FAQ state:
"To spare the need for a frontend cache server (and to let G-WAN be used as a caching reverse-proxy) G-WAN supports micro-caching, a RESTful feature. When a given URI is invoked at high concurrencies and when generating the payload take a lot of time, then G-WAN will automatically cache a page for 200 milliseconds (the average latency on the Internet) to make sure that the cache is up-to-date: within 200 ms, consecutive requests provide the expected result. To prevent micro-caching from being triggered, use a changing query parameter (per user session id, random, counter, etc.) for concurrent requests."
Note that for v4.10+ caching is disabled by default, look at the gwan/init.c file.

Resources