winhttp client and openssl server communication error - c++

I have encounted a strange communication problem when using winhttp to program http client and using openssl to program http server.
I use wireshark to analyze the communication. Everything seems ok when handshake between the two, and the handshake procedure is really quick. But after the handshake, winhttp client taken a really long time send a http request, then server received the request (the received packet is right) then server attempted to send response with ssl_write, but client cannot receive this packet.
please see notes in my code for advanced info.
Below is client's main communication code.
BOOL CHttpClient :: _Synchronize(BYTE* pbRequest, DWORD dwRequestCb,
BYTE** pbResponse, DWORD* dwResponseCb)
{
DWORD dwErrorCode = 0;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
DWORD dwLastStatusCode = 0;
BOOL bDone = FALSE;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
while (!bDone)
{
if (!WinHttpSendRequest(m_hRequest, NULL, 0, pbRequest, dwRequestCb, dwRequestCb, NULL))
{
dwErrorCode = GetLastError();
switch (dwErrorCode)
{
case ERROR_WINHTTP_CANNOT_CONNECT:
case ERROR_WINHTTP_TIMEOUT:
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
case ERROR_WINHTTP_CONNECTION_ERROR:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
case 12175:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
return FALSE;
}
}
// client always wait here for server's response and get a error code as 12002--ERROR_INTERNET_TIMEOUT
if (!WinHttpReceiveResponse(m_hRequest, NULL))
{
dwErrorCode = GetLastError(); // get error code 12002--ERROR_INTERNET_TIMEOUT
switch (dwErrorCode)
{
case ERROR_WINHTTP_RESEND_REQUEST:
continue;
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
continue;
default:
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
}
if (!DoServerAuth())
{
m_dwDefaultAddrIndex = (m_dwDefaultAddrIndex + 1) % m_lpConfig->dwHttpAddrNum;
if (FALSE == DoConnectAndOpenRequest(dwRequestCb == 0))
{
return FALSE;
}
continue;
}
if (!WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
NULL, &dwStatusCode, &dwSize, NULL))
{
return FALSE;
}
switch (dwStatusCode)
{
// success
case 200:
bDone = TRUE;
break;
// proxy need authentication
case 407:
if (dwLastStatusCode == 407)
{
bDone = TRUE;
}
else if (!DoProxyAuth())
return FALSE;
break;
default:
return FALSE;
}
dwLastStatusCode = dwStatusCode;
}
DWORD dwBytesAvailable = 0;
DWORD dwBytesTransfered = 0;
BYTE* pbTmpBuf = new BYTE [GetMaxTransferCb()];
// read http server reply
*dwResponseCb = 0;
do
{
if (!WinHttpQueryDataAvailable(m_hRequest, &dwBytesAvailable))
{
return FALSE;
}
if (!WinHttpReadData(m_hRequest, pbTmpBuf + *dwResponseCb, dwBytesAvailable,
&dwBytesTransfered))
{
return FALSE;
}
*dwResponseCb += dwBytesTransfered;
} while (dwBytesAvailable > 0);
*pbResponse = new BYTE [*dwResponseCb];
memcpy(*pbResponse, pbTmpBuf, *dwResponseCb);
RELEASE_ARRAY(pbTmpBuf);
// all done
return TRUE;
}
BOOL CHttpClient :: DoConnectAndOpenRequest(BOOL bGetVerb)
{
BOOL bOK = FALSE;
DWORD dwReqObjNameCch = (DWORD)rand() % (20 - 5) + 5;
BYTE* lpReqObjName = new BYTE [(dwReqObjNameCch + 6) * sizeof(TCHAR)];
DWORD dwOptionCode = 0;
__try
{
// release old handles
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
// connect to default address, change of default address is not my duty
if (NULL == (m_hConnect = WinHttpConnect(m_hSession,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].tszAddr,
m_lpConfig->lpHttpAddrs[m_dwDefaultAddrIndex].wPort, 0)))
__leave;
// random generate request object name
// now this method is slow and bad, we can improve later
if (!CryptGenRandom(m_hCryptProv, dwReqObjNameCch * sizeof(TCHAR), lpReqObjName))
return FALSE;
for (DWORD i = 0; i < dwReqObjNameCch * sizeof(TCHAR); i += sizeof(TCHAR))
{
lpReqObjName[i] = (lpReqObjName[i] % 26) + 97;
lpReqObjName[i + 1] = 0;
}
((LPTSTR)lpReqObjName)[dwReqObjNameCch] = _T('.');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 1] = _T('h');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 2] = _T('t');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 3] = _T('m');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 4] = _T('l');
((LPTSTR)lpReqObjName)[dwReqObjNameCch + 5] = 0;
// open request
if (NULL == (m_hRequest = WinHttpOpenRequest(m_hConnect, bGetVerb ? L"GET" : L"POST",
(LPTSTR)lpReqObjName, L"HTTP/1.1", WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE)))
__leave;
// set cert options
if (m_lpConfig->bSsl)
{
dwOptionCode = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_UNKNOWN_CA;
if (!WinHttpSetOption(m_hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwOptionCode,
sizeof(DWORD)))
__leave;
}
// all done
bOK = TRUE;
}
__finally
{
if (!bOK)
{
RELEASE_HINTERNET(m_hConnect);
RELEASE_HINTERNET(m_hRequest);
}
RELEASE_ARRAY(lpReqObjName);
}
return bOK;
}
Below is server's main communication code
#include <strsafe.h>
BOOL CSslServer :: StartService(SSL_CONFIG* lpSslServerConfig)
{
WSADATA lpWSAData;
WSAStartup(MAKEWORD(2, 2), &lpWSAData);
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
m_lpConfig = lpSslServerConfig;
m_SslCtx = SSL_CTX_new(SSLv3_server_method());
SSL_CTX_set_default_passwd_cb_userdata(m_SslCtx, (void*)"123456");
if (1 > SSL_CTX_use_certificate_file(m_SslCtx, m_lpConfig->szCertFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
if (1 > SSL_CTX_use_PrivateKey_file(m_SslCtx, m_lpConfig->szSkeyFileDir, SSL_FILETYPE_PEM))
{
return FALSE;
}
SSL_CTX_set_cipher_list(m_SslCtx, "ALL");
SSL_CTX_set_verify(m_SslCtx, SSL_VERIFY_NONE, NULL);
if (NULL == (m_hServerQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL)))
return FALSE;
if (NULL == (m_hWorkThread = CreateThread(NULL, 0, SslServerWorkThread, this, 0, NULL)))
{
StopService();
return FALSE;
}
return TRUE;
}
BOOL CSslServer :: StopService()
{
DWORD dwExitCode = 0;
if (m_hWorkThread != NULL)
{
SetEvent(m_hServerQuitEvt);
WaitForSingleObject(m_hWorkThread, 30000);
if(GetExitCodeThread(m_hWorkThread, &dwExitCode))
{
if(dwExitCode == STILL_ACTIVE)
TerminateThread(m_hWorkThread, 0);
}
}
SSL_CTX_free(m_SslCtx);
RELEASE_HANDLE(m_hServerQuitEvt);
RELEASE_HANDLE(m_hWorkThread);
m_WorkThreadList.empty();
return TRUE;
}
DWORD WINAPI SslServerWorkThread(LPVOID lpParam)
{
CSslServer* lpServer = (CSslServer*)lpParam;
SOCKET hListenSocket = INVALID_SOCKET;
TIMEVAL* lpWaitTime = new TIMEVAL;
fd_set ListenFds;
DWORD dwWaitForQuit = 0;
int nSelectRel = 0;
int nSize = 0;
SESSION_CONTEXT* lpSessionCtx = NULL;
WORK_THREAD_CONTEXT* lpWorkThreadCtx = NULL;
hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(hListenSocket, (sockaddr*)&lpServer->m_lpConfig->SslServerAddr,
sizeof(sockaddr_in));
if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN))
{
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
FD_ZERO(&ListenFds);
FD_SET(hListenSocket, &ListenFds);
lpWaitTime->tv_sec = 30;
lpWaitTime->tv_usec = 0;
do
{
nSelectRel = select(0, &ListenFds, NULL, NULL, lpWaitTime);
switch (nSelectRel)
{
case SOCKET_ERROR:
goto END;
case 1:
lpSessionCtx = new SESSION_CONTEXT;
nSize = sizeof(sockaddr_in);
if (INVALID_SOCKET == (lpSessionCtx->hClientSocket = accept(hListenSocket,
(struct sockaddr *)&lpSessionCtx->ClientAddress, &nSize)))
{
RELEASE(lpSessionCtx);
goto END;
}
lpSessionCtx->lpServer = lpServer;
lpSessionCtx->lpWorkConfig = lpServer->m_lpConfig->lpWorkConfig;
lpSessionCtx->SslSession = SSL_new(lpServer->m_SslCtx);
SSL_set_fd(lpSessionCtx->SslSession, lpSessionCtx->hClientSocket);
SSL_accept(lpSessionCtx->SslSession);
lpWorkThreadCtx = new WORK_THREAD_CONTEXT;
lpWorkThreadCtx->hWorkThreadQuitEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
lpWorkThreadCtx->hWorkThread = CreateThread(NULL, 0, WorkMain, lpSessionCtx,
0, NULL);
// WorkMain is not provided here, the function just analyze the received packet and
// invoke SendPacket to send response.
lpSessionCtx->lpWorkThreadCtx = lpWorkThreadCtx;
lpServer->m_WorkThreadList.push_back(lpWorkThreadCtx);
break;
default:
FD_SET(hListenSocket, &ListenFds);
}
dwWaitForQuit = WaitForSingleObject(lpServer->m_hServerQuitEvt, 0);
if (dwWaitForQuit == WAIT_FAILED || dwWaitForQuit == WAIT_ABANDONED)
{
goto END;
}
} while (dwWaitForQuit != WAIT_OBJECT_0);
END:
RELEASE_SOCKET(hListenSocket);
RELEASE(lpWaitTime);
return 0;
}
// I have examined the received packet. This function is ok
BOOL CSslServer :: ParseClientPacket(SSL* ssl, BYTE** lpBuf, DWORD* dwTransCb)
{
char* lpHeader = NULL;
BOOL bOk = FALSE;
int nRet = 0;
int nHeaderBufCb = 1024;
int nIndex = 0;
DWORD dwPackBufCb = 0;
__try
{
*dwTransCb = 0;
lpHeader = new char [nHeaderBufCb];
memset(lpHeader, 0, nHeaderBufCb);
if (0 >= (nRet = SSL_read(ssl, lpHeader, nHeaderBufCb)))
__leave;
nHeaderBufCb = lstrlenA(lpHeader);
for (nIndex = 20; nIndex < nHeaderBufCb - 15; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "Content-Length: ", 16))
{
sscanf_s(lpHeader + nIndex + 16, "%d", &dwPackBufCb);
break;
}
}
if (nIndex == nHeaderBufCb - 15)
__leave;
for (nIndex += 16; nIndex < nHeaderBufCb - 4; nIndex++)
{
if (0 == memcmp(lpHeader + nIndex, "\r\n\r\n", 4))
break;
}
if (nIndex == nHeaderBufCb - 4)
__leave;
*lpBuf = new BYTE [dwPackBufCb];
if (nRet - nIndex - 4 > 0)
{
memcpy(*lpBuf, lpHeader + nIndex + 4, nRet - nIndex - 4);
*dwTransCb = nRet - nIndex - 4;
}
while (*dwTransCb < dwPackBufCb)
{
if (0 >= SSL_read(ssl, *lpBuf + *dwTransCb, dwPackBufCb - *dwTransCb))
{
bOk = TRUE;
__leave;
}
*dwTransCb += nRet;
}
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHeader);
}
return bOk;
}
BOOL CSslServer :: SendPacket(SSL* ssl, BYTE* lpBuf, DWORD cb)
{
LPSTR lpHttpPacket = NULL;
DWORD dwHeaderLen = 0;
BOOL bOk = FALSE;
char szTime[50] = {0};
time_t lTime;
struct tm GmtTime;
__try
{
lpHttpPacket = new char [200 + cb];
memset(lpHttpPacket, 0, 200 + cb);
time(&lTime);
_gmtime64_s(&GmtTime, &lTime);
strftime(szTime, 50, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", &GmtTime);
StringCchPrintfA(lpHttpPacket, 200,
"HTTP/1.1 200 OK\r\nServer: Microsoft-IIS/8.0\r\nConnection: Keep-Alive\r\n%sContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
szTime, cb);
dwHeaderLen = lstrlenA(lpHttpPacket);
memcpy(lpHttpPacket + dwHeaderLen, lpBuf, cb);
if (0 >= SSL_write(ssl, lpHttpPacket, cb)) // the packet send by this sentence cannot be received by client
__leave; // observe by wireshark, this sentence send a ssl reassembled pdu
bOk = TRUE;
}
__finally
{
RELEASE_ARRAY(lpHttpPacket);
}
return bOk;
}
And below is wireshark snap.
NO. Time Source Destination Protocol Length Info
15951 3691.1 .23 .98 SSL 126 client hello
15952 3691.1 .98 .23 SSLv3 1109 server hello
15953 3691.1 .23 .98 SSLv3 386 client key exchange, change cipher spec, finished
15954 3691.1 .98 .23 SSLv3 121 change cipher spec, finished
16029 3706.6 .23 .98 http 301 POST ...... HTTP/1.1
16060 3711.9 .98 .23 SSLv3 83 [SSL segment of a ressembled PDU]
It takes me really a long time to solve this problem. I really hope someone and handle this.
best wishes

Related

interactive terminal with winsock

I want to Create a terminal which can connect different computer by IP address in windows.
I use CreateProcess because _popen cannot use.
file main.cpp
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <csignal>
#include <cstring>
#include <windows.h>
#include <strsafe.h>
#include <fcntl.h>
#include <io.h>
#include <ctime>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 2048
#define DEFAULT_PORT "7999"
static void ErrorExit(PTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
static const char header[] = "\
HTTP/1.0 200 OK\r\n\
Server: Windows VC++\r\n\
Content-Type: text/html; charset=big-5\r\n\
Access-Control-Allow-Origin: *\r\n\
\r\n";
static int running = true;
void callback(int) {
running = false;
puts("closing");
}
static HANDLE
g_hChildStd_IN_Rd = NULL,
g_hChildStd_IN_Wr = NULL,
g_hChildStd_OUT_Rd = NULL,
g_hChildStd_OUT_Wr = NULL;
#undef TEXT
#define TEXT(a) ((PTSTR)(L##a))
int __cdecl main(void)
{
SECURITY_ATTRIBUTES saAttr = {};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
{
PROCESS_INFORMATION piProcInfo = {};
STARTUPINFO siStartInfo = {};
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
char szCmdline[] = "cmd"; // powershell
if (!CreateProcessA( NULL, szCmdline,
NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo)) {
ErrorExit(TEXT("CreateProcess"));
}
}
signal(SIGINT, callback);
assert(SetConsoleOutputCP(CP_UTF8));
WSADATA wsaData = {};
struct addrinfo* result = NULL;
struct addrinfo hints = {};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
ErrorExit(TEXT("WSAStartup"));
}
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, DEFAULT_PORT, &hints, &result) != 0) {
WSACleanup();
ErrorExit(TEXT("getaddrinfo"));
}
SOCKET ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
ErrorExit(TEXT("socket"));
}
if (bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("bind"));
}
freeaddrinfo(result);
if (listen(ListenSocket, 5) == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("listen"));
}
puts("Server ready at http://localhost:" DEFAULT_PORT " [running]");
char recvbuf[DEFAULT_BUFLEN] = {};
while (running) {
SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
ErrorExit(TEXT("accept"));
}
{
struct sockaddr_in sockaddr;
int namelen = sizeof(sockaddr);
if (!getpeername(ClientSocket, (struct sockaddr*)&sockaddr, &namelen))
{
time_t timmer = time(NULL);
printf("%s\t#%s", inet_ntoa((in_addr)(*(in_addr*)&sockaddr.sin_addr.S_un.S_addr)), ctime(&timmer));
}
}
ZeroMemory(recvbuf, DEFAULT_BUFLEN);
intptr_t recvlen = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
if (recvlen == -1) {
fputs("recv error\n", stderr);
goto SH;
}
// fwrite(recvbuf, iResult, 1, stdout);
if (recvbuf[0] == 'G' && recvbuf[1] == 'E' && recvbuf[2] == 'T') { // HTTP GET method
send(ClientSocket, header, sizeof(header) - 1, 0);
DWORD dwRead;
// success on here
if (ReadFile(g_hChildStd_OUT_Rd, recvbuf, DEFAULT_BUFLEN, &dwRead, NULL)) {
send(ClientSocket, recvbuf, (int)dwRead, 0);
}
}
else {
send(ClientSocket, header, sizeof(header) - 1, 0); // HTTP POST method
char *ptr = strstr(recvbuf, "\r\n\r\n");
if (ptr == NULL || ptr[0] == '\0' || ptr[1] == '\0'||ptr[2]=='\0'|| ptr[3] == '\0'|| ptr[4] == '\0') {
fputs("parse error: POST request\n", stderr);
goto SH;
}
ptr += 4;
{
DWORD dwRead=0, dwWritten=0;
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, ptr, (DWORD)((recvlen+recvbuf)-ptr), &dwWritten, NULL);
if (!bSuccess) goto SH;
printf("wrote %lu\n", dwWritten);
// always block on ReadFile
bSuccess = ReadFile(g_hChildStd_OUT_Rd, recvbuf, DEFAULT_BUFLEN, &dwRead, NULL);
printf("read %lu\n", dwRead);
if (!bSuccess || dwRead == 0) goto SH;
send(ClientSocket, ptr, dwRead, 0);
}
}
SH:
if (shutdown(ClientSocket, SD_BOTH) == SOCKET_ERROR) {
puts("shutdown failed");
closesocket(ClientSocket);
WSACleanup();
ErrorExit(TEXT("shutdown"));
}
closesocket(ClientSocket);
}
puts("Exit Server [close]");
WSACleanup();
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
return 0;
}
file index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="big-5">
<title>terminal</title>
</head>
<body>
<style type="text/css">
input {
min-width: 900px;
color: green;
border-width: 0px;
}
span, input, nav {
font-family: Consolas;
font-style: oblique;
}
font{
font-size: 15px;
color: cornflowerblue;
font-family: monospace;
}
</style>
<nav id='version'>waiting for server</nav>
<pre id='shell'>
<input id="new" value="print('Silav gerokê')">
</pre>
<script type="text/javascript">
var _history = [];
var _index = 0;
var shell=document.getElementById('shell');
window.onload=function(){
var x=new XMLHttpRequest();
x.open("GET", "http://127.0.0.1:7999", true);
x.onreadystatechange=function(){
if (x.readyState == 4 && x.status == 200){
document.getElementById('version').innerText=x.response;
document.getElementById('new').focus();
}
}
x.send(null);
}
shell.addEventListener('keydown', function(_){
if(_.keyCode === 13){
_.preventDefault();
var old = document.getElementById('new');
var x=new XMLHttpRequest();
x.open("POST", "http://127.0.0.1:7999", true);
_history.push(old.value);
_index += 1;
x.send(old.value);
x.onreadystatechange=function(){
if (x.readyState == 4 && x.status == 200){
old.removeAttribute('id');
shell.appendChild(document.createElement('br'));
var ne = document.createElement('font');
ne.innerText=x.response;
shell.appendChild(ne);
var input = document.createElement('input');
input.setAttribute('id', 'new');
shell.appendChild(input);
input.focus();
}
}
}else if (_.keyCode==38) {
if (_index){
_index -=1 ;
document.getElementById('new').value=_history[_index];
}
}else if (_.keyCode==40) {
if (_history[_index+1]!=undefined){
_index+=1;
document.getElementById('new').value=_history[_index];
}
}
})
</script>
</body>
</html>
the HTTP GET method works, but POST not.
when I use cmd, block on ReadFile
when I use powershell, server just returns what it reads
it looks like this
What's wrong with my code?
Most Web Terminal use WebSocket for Duplex connection, e.g. Jupyter, WebSSH...
I use IO completion port(IOCP) for reading from child process's pipe, sending text to client, both of them are overlapped operation.
In my question, the pipe was created by CreatePipe, which is synchronous.In my answer, I use named pipe for asynchronous reading.So we don't have to waiting synchronously.
main.cpp
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <mstcpip.h>
#include <crtdbg.h>
#include <wchar.h>
#include <WinInet.h>
#include "Mine.h"
#include "llhttp.h"
#pragma comment(lib, "WS2_32")
#pragma comment(lib, "Mswsock")
#pragma comment(lib, "Wininet")
HANDLE heap, iocp;
#define assert(x) {if (!(x)){printf("error in %s.%d: %s, err=%d\n", __FILE__, __LINE__, #x, WSAGetLastError());}}
#include "types.h"
#include "pipe.cpp"
#include "handshake.cpp"
VOID CloseClient(IOCP* ctx) {
if (ctx->client) {
shutdown(ctx->client, SD_BOTH);
closesocket(ctx->client);
ctx->client = NULL;
ctx->state = State::AfterClose;
#define USEMALLOC 1
#ifdef USEMALLOC
free(ctx);
#else
HeapFree(heap, 0, ctx);
#endif
}
_ASSERT(_CrtCheckMemory());
}
IOCP* initSocket(SOCKET client) {
#ifdef USEMALLOC
IOCP* ctx = (IOCP*)malloc(sizeof(IOCP));
if (ctx == NULL)
return NULL;
ZeroMemory(ctx, sizeof(*ctx));
#else
IOCP* ctx = (IOCP*)HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(IOCP));
if (ctx == NULL)
return NULL;
#endif
ctx->client = client;
HANDLE ret = CreateIoCompletionPort((HANDLE)client, iocp, (ULONG_PTR)ctx, 0);
assert(ret);
_ASSERT(_CrtCheckMemory());
return ctx;
}
#include "frame.cpp"
int http_on_header_field(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
p->length = length;
p->at = at;
return 0;
}
int http_on_header_value(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
p->headers[std::string(p->at, p->length)] = std::string(at, length);
return 0;
}
int http_on_url(llhttp_t* parser, const char* at, size_t length) {
Parse_Data* p = (Parse_Data*)parser;
std::string tmp{ at, length };
DWORD escaped = 1;
char dummy;
HRESULT res = UrlUnescapeA((PSTR)tmp.data(), &dummy, &escaped, 0);
p->uri = (CHAR*)HeapAlloc(heap, 0, escaped + 1);
assert(p->uri);
*(CHAR*)&p->uri[escaped] = '\0';
p->uriLen = escaped;
res = UrlUnescapeA(tmp.data(), (PSTR)p->uri, &escaped, 0);
assert(res == S_OK);
return 0;
}
LPCWSTR encodePath(IOCP* ctx, Parse_Data &parse_data) {
int res = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, parse_data.uri, (int)parse_data.uriLen, (LPWSTR)ctx->buf, sizeof(ctx->buf) / 2);
ctx->state = State::ReadStaticFile;
LPCWSTR file = (LPWSTR)ctx->buf;
if (parse_data.uriLen == 2 && *parse_data.uri == '/') {
file = L"index.html";
}
else {
file += 1;
}
return file;
}
void processRequest(Parse_Data& parse_data, IOCP* ctx, enum llhttp_errno err) {
WSABUF* errBuf = &HTTP_ERR_RESPONCE::internal_server_error;
switch (parse_data.parser.method) {
case llhttp_method::HTTP_GET: {
switch (err) {
case HPE_OK:
{
if (ctx->firstCon) {
ctx->firstCon = false;
auto connection = parse_data.headers.find("Connection");
if (connection != parse_data.headers.end()) {
if (connection->second == "keep-alive" || connection->second == "Keep-Alive") {
ctx->keepalive = true;
DWORD yes = TRUE;
int success = setsockopt(ctx->client, SOL_SOCKET, SO_KEEPALIVE, (char*)&yes, sizeof(yes));
assert(success == 0);
puts("set tcp keep alive(SO_KEEPALIVE)");
auto keepalive = parse_data.headers.find("Keep-Alive");
if (keepalive == parse_data.headers.end())
keepalive = parse_data.headers.find("keep-alive");
if (keepalive != parse_data.headers.end()) {
auto s = keepalive->second.data();
auto timeouts = StrStrA(s, "timeout");
if (timeouts) {
int timeout;
int res = sscanf_s(timeouts + 7, "=%d", &timeout);
if (res > 0) {
printf("set TCP keep alive=%d\n", timeout);
int yes = TRUE;
res = setsockopt(ctx->client, SOL_SOCKET, TCP_KEEPIDLE, (char*)&yes, sizeof yes);
assert(res == 0);
}
else {
puts("Error: failed to parse keepalive seconds...");
}
}
}
}
else {
printf("I got Connection: %s\n", connection->second.data());
}
}
}
auto file = encodePath(ctx, parse_data);
HANDLE hFile = CreateFileW(file,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
errBuf = &HTTP_ERR_RESPONCE::not_found;
goto BAD_REQUEST_AND_RELEASE;
}
HANDLE r = CreateIoCompletionPort(hFile, iocp, (ULONG_PTR)ctx, 0);
assert(r);
const char* mine = getType(file);
ctx->hProcess = hFile;
LARGE_INTEGER fsize{};
BOOL bSuccess = GetFileSizeEx(hFile, &fsize);
assert(bSuccess);
int res = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %lld\r\n"
"Connection: %s\r\n\r\n", mine, fsize.QuadPart, ctx->keepalive ? "keep-alive" : "close");
assert(res > 0);
ctx->sendBuf->buf = ctx->buf;
ctx->sendBuf->len = (ULONG)res;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}break;
case HPE_PAUSED_UPGRADE:
{
auto upgrade = parse_data.headers.find("Upgrade");
auto pro = parse_data.headers.find("Sec-WebSocket-Protocol");
if (upgrade != parse_data.headers.end() && pro != parse_data.headers.end()) {
if (upgrade->second == "websocket") {
auto ws_key = parse_data.headers.find("Sec-WebSocket-Key");
if (ws_key != parse_data.headers.end()) {
ctx->state = State::AfterHandShake;
ws_key->second += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char buf[29];
BOOL ret = HashHanshake(ws_key->second.data(), (ULONG)ws_key->second.length(), buf);
assert(ret);
int len;
len = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Protocol: %s\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n", pro->second.data(), buf);
assert(len > 0);
ctx->process_name = StrDupA(pro->second.data());
ctx->sendBuf[0].buf = ctx->buf;
ctx->sendBuf[0].len = (ULONG)len;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}
else {
errBuf = &HTTP_ERR_RESPONCE::bad_request;
goto BAD_REQUEST_AND_RELEASE;
}
}break;
DEFAULT_UNREACHABLE;
}
}break;
case llhttp_method::HTTP_HEAD:
{
if (err == llhttp_errno::HPE_OK) {
auto file = encodePath(ctx, parse_data);
HANDLE hFile = CreateFileW(file,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
constexpr const char* msg = "HTTP/1.1 404 Not Found\r\n\r\n";
ctx->sendBuf[0].buf = (CHAR*)msg;
ctx->sendBuf[0].len = cstrlen(msg);
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
return;
}
FILETIME ftWrite;
SYSTEMTIME stUTC;
LARGE_INTEGER fsize;
if (GetFileTime(hFile, NULL, NULL, &ftWrite)) {
if (FileTimeToSystemTime(&ftWrite, &stUTC)) {
if (InternetTimeFromSystemTimeA(&stUTC, INTERNET_RFC1123_FORMAT, ctx->sErr.buf, sizeof(ctx->sErr.buf))) {
if (GetFileSizeEx(hFile, &fsize)) {
int len = snprintf(ctx->buf, sizeof(ctx->buf),
"HTTP/1.1 200 OK\r\n" // no "Accept-Ranges: bytes\r\n" now
"Last-Modified: %s\r\n"
"Conetnt-Length: %lld\r\n"
"\r\n", ctx->sErr.buf, fsize.QuadPart);
ctx->sendBuf[0].buf = ctx->buf;
ctx->sendBuf[0].len = len;
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
puts("sended");
return;
}
}
}
}
errBuf = &HTTP_ERR_RESPONCE::internal_server_error;
goto BAD_REQUEST_AND_RELEASE;
}
else {
goto BAD_REQUEST_AND_RELEASE;
}
}break;
default:
{
errBuf = &HTTP_ERR_RESPONCE::method_not_allowed;
BAD_REQUEST_AND_RELEASE:
ctx->state = State::AfterSendHTML;
WSASend(ctx->client, errBuf, 1, NULL, 0, &ctx->sendOL, NULL);
}
}
}
DWORD WINAPI WorkerThread(LPVOID WorkThreadContext) {
DWORD dwbytes = 0;
IOCP* ctx = NULL;
OVERLAPPED* ol = NULL;
for (;;) {
_ASSERT(_CrtCheckMemory());
BOOL ret = GetQueuedCompletionStatus((HANDLE)WorkThreadContext, &dwbytes, (PULONG_PTR)&ctx, &ol, INFINITE);
if (ret == FALSE) {
/*
* GetLastError() retrun thread local error
*/
if (ctx) {
int err=GetLastError();
switch (err) {
case ERROR_BROKEN_PIPE:
{
if (ctx->hProcess != NULL) {
*(PWORD)ctx->buf = htons(1000);
DWORD dwExitCode = 0;
const char* msg;
if (GetExitCodeProcess(ctx->hProcess, &dwExitCode)) {
msg = "process exit with code %u";
}
else {
msg = "process was exited(failed to get exit code)";
// ERROR_INVALID_HANDLE on GetExitCodeProcess
}
int n = snprintf(ctx->buf + 2, sizeof(ctx->buf) - 2, msg, dwExitCode);
assert(n > 0);
websocketWrite(ctx, ctx->buf, n + 2, &ctx->sendOL, ctx->sendBuf, Websocket::Opcode::Close);
CloseHandle(ctx->hProcess);
ctx->hProcess = NULL;
}
}break;
case ERROR_HANDLE_EOF:
{
BOOL res = CloseHandle(ctx->hProcess);
assert(res);
if (ctx->keepalive)
{
ctx->state = State::AfterRecv;
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = sizeof(ctx->buf);
ctx->recvOL.Offset = ctx->recvOL.OffsetHigh = 0; // reset reading position
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
continue;
}
else {
CloseClient(ctx);
}
}break;
default:
printf("undefined error(GetLastError=%d): I'm going to ignore it\n", err);
}
}
continue;
}
if (ctx == NULL || ol==NULL) {
assert(0);
continue;
}
if (dwbytes == 0) {
CloseClient(ctx);
continue;
}
switch (ctx->state) {
// Accept-Ranges: bytes
case State::AfterRecv:
{
ctx->buf[dwbytes] = '\0';
Parse_Data parse_data{};
enum llhttp_errno err = llhttp_execute(&parse_data.parser, ctx->buf, dwbytes);
if (err != HPE_OK && err != HPE_PAUSED_UPGRADE) {
printf("llhttp_execute error: %s\n", llhttp_errno_name(err));
CloseClient(ctx);
continue;
}
if (parse_data.parser.http_major != 1 || parse_data.parser.http_minor != 1) {
puts("expect HTTP/1.1");
CloseClient(ctx);
continue;
}
printf("%s [%s]\n", llhttp_method_name((llhttp_method)parse_data.parser.method), parse_data.uri);
processRequest(parse_data, ctx, err);
}break; // end case
case State::AfterHandShake:
{
ctx->state = State::WebSocketConnecting;
if (ctx->process_name == NULL) {
CloseClient(ctx);
continue;
}
int size = MultiByteToWideChar(CP_UTF8, 0, ctx->process_name, -1, NULL, 0) * 2;
if (size <= 0) {
assert(0);
CloseClient(ctx);
continue;
}
WCHAR* name = (WCHAR*)HeapAlloc(heap, 0, size+2);
name[size] = L'\0';
size = MultiByteToWideChar(CP_UTF8, 0, ctx->process_name, -1, name, size / 2);
LocalFree(ctx->process_name);
if (size <= 0) {
assert(0);
CloseClient(ctx);
continue;
}
printf("=== spawn process: %ws(%d) ===\n", name, size);
BOOL res = CreateCGI(name, ctx);
HeapFree(heap, 0, name);
if (res == FALSE) {
int n = snprintf(ctx->buf+2, sizeof(ctx->buf)-2, "Error: create process failed, GetLastError()=%d", GetLastError());
*(PWORD)ctx->buf = htons(1000);
assert(n > 0);
websocketWrite(ctx, ctx->buf, n + 2, &ctx->sendOL, ctx->sendBuf, Websocket::Opcode::Close);
continue;
}
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = 6;
ctx->dwFlags = MSG_WAITALL;
ctx->Reading6Bytes = true;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}break;
case State::ReadStaticFile:
{
(void)ReadFile(ctx->hProcess, ctx->buf, sizeof(ctx->buf), NULL, &ctx->recvOL);
ctx->state = State::SendPartFile;
}break;
case State::SendPartFile:
{
ctx->recvOL.Offset += dwbytes;
ctx->sendBuf->len = dwbytes;
WSASend(ctx->client, ctx->sendBuf, 1, NULL, 0, &ctx->sendOL, NULL);
ctx->state = State::ReadStaticFile;
}break;
case State::WebSocketConnecting: {
if (ol == &ctx->recvOL) {
if (ctx->Reading6Bytes) {
onRead6Complete(ctx);
}
else {
onRecvData(ctx);
}
}
else if (ol == &ctx->sOut.ol) {
websocketWrite(ctx, (CHAR*)ctx->sOut.buf, dwbytes, &ctx->sOut.sendOL, ctx->sOut.wsaBuf);
}
else if (ol == &ctx->sErr.ol) {
websocketWrite(ctx, (CHAR*)ctx->sErr.buf, dwbytes, &ctx->sErr.sendOL, ctx->sErr.wsaBuf);
}
else if (ol == &ctx->sOut.sendOL) {
(void)ReadFile(ctx->sOut.pipe, ctx->sOut.buf, sizeof(ctx->sOut.buf), NULL, &ctx->sOut.ol);
}
else if (ol == &ctx->sErr.sendOL) {
(void)ReadFile(ctx->sErr.pipe, ctx->sErr.buf, sizeof(ctx->sErr.buf), NULL, &ctx->sErr.ol);
}
else if (ol == &ctx->sendOL) {
CloseClient(ctx);
}
}break;
case State::AfterSendHTML: {
CloseClient(ctx);
}break;
case State::AfterClose:
{
assert(0);
}break;
default:
{
assert(0);
}
}
}
}
int main()
{
system("chcp 65001");
{
WSADATA wsaData{};
int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
assert(ret == 0);
}
iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
assert(iocp);
heap = GetProcessHeap();
BOOL ret = initHash();
assert(ret);
sockaddr_in ip4{ .sin_family = AF_INET, .sin_port = htons(80) };
SOCKET server = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (server == INVALID_SOCKET)
return 1;
if (bind(server, (struct sockaddr*)&ip4, sizeof(ip4)) == SOCKET_ERROR)
return 1;
if (listen(server, SOMAXCONN) == SOCKET_ERROR)
return 1;
CreateThread(NULL, 0, WorkerThread, iocp, 0, 0);
puts("server listening at http://localhost/ and ws://localhost/");
for (;;) {
sockaddr_in sIn{};
int sInLen = sizeof(sIn);
_ASSERT(_CrtCheckMemory());
SOCKET client = WSAAccept(server, (sockaddr*)&sIn, &sInLen, NULL, NULL);
IOCP* ctx = initSocket(client);
assert(ctx);
ctx->recvBuf[0].buf = ctx->buf;
ctx->recvBuf[0].len = sizeof(ctx->buf);
ctx->firstCon = true;
WSARecv(client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}
WSACleanup();
closeHash();
_ASSERT(_CrtCheckMemory());
}
types.cpp
namespace Websocket {
using BIT = BYTE;
enum Opcode : BYTE {
Continuation = 0,
Text = 0x1,
Binary = 0x2,
Close = 0x8,
Ping = 0x9,
Pong = 0xA
};
}
template <ULONG N>
consteval ULONG cstrlen(const char(&)[N]) {
return N - 1;
}
consteval ULONG cstrlen(const char* s) {
ULONG res = 0;
for (; *s; s++) {
res++;
}
return res;
}
enum class State : unsigned __int8 {
AfterRecv, ReadStaticFile, SendPartFile, AfterSendHTML, AfterHandShake, WebSocketConnecting, AfterClose
};
int http_on_header_field(llhttp_t* parser, const char* at, size_t length);
int http_on_header_value(llhttp_t* parser, const char* at, size_t length);
int http_on_url(llhttp_t* parser, const char* at, size_t length);
struct Parse_Data {
Parse_Data() : headers{}, uri{}, at{}, length{}, uriLen{} {
llhttp_settings_init(&settings);
settings.on_url = http_on_url;
settings.on_header_field = http_on_header_field;
settings.on_header_value = http_on_header_value;
llhttp_init(&parser, HTTP_REQUEST, &settings);
};
llhttp_t parser;
std::map<std::string, std::string> headers;
const CHAR* uri;
ULONG uriLen;
size_t length;
const char* at;
llhttp_settings_t settings;
};
struct Stdio {
OVERLAPPED ol, sendOL;
HANDLE pipe;
CHAR buf[100];
WSABUF wsaBuf[2];
};
struct IOCP {
SOCKET client;
State state;
OVERLAPPED recvOL, sendOL;
char buf[4096];
DWORD dwFlags;
WSABUF sendBuf[2], recvBuf[1];
bool Reading6Bytes;
unsigned __int64 payload_len;
BYTE header[4];
struct Stdio sIn, sOut, sErr;
HANDLE hProcess;
Websocket::Opcode op;
char* process_name;
bool keepalive, firstCon;
};
namespace HTTP_ERR_RESPONCE {
static char sinternal_server_error[] = "HTTP/1.1 500 Internal Server Error\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>500 Internal Server Error</title></head><body><h1 align=\"center\">500 Internal Server Error</h1><hr><p style=\"text-align: center\">The server has some error...<p/></body></html>",
snot_found[] =
"HTTP/1.1 404 Not Found\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1 align=\"center\">404 Not Found</h1><hr><p style=\"text-align: center\">The Request File is not found in the server<p/></body></html>",
smethod_not_allowed[] =
"HTTP/1.1 405 Method Not Allowed\r\n"
"Connection: close\r\n"
"Allow: GET, HEAD\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>405 Method Not Allowed</title></head><body><h1 align=\"center\">405 Method Not Allowed</h1><hr><p style=\"text-align: center\">You can only use GET, HEAD request<p/></body></html>",
sbad_request[] =
"HTTP/1.1 400 Bad Request\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n"
"<!DOCTYPE html><html><head><title>400 Bad Request</title></head><body><h1 align=\"center\">400 Bad Request</h1><hr><p style=\"text-align: center\">Thes server can't process the request<br>Maybe you miss some header(s)<p/></body></html>";
static WSABUF
internal_server_error = {
cstrlen(sinternal_server_error), sinternal_server_error
},
not_found = {
cstrlen(snot_found), snot_found
},
method_not_allowed = {
cstrlen(smethod_not_allowed), smethod_not_allowed
},
bad_request = {
cstrlen(sbad_request), sbad_request
};
}
pipe.cpp
HANDLE newPipe(WCHAR buf[50], DWORD dwOpenMode) {
HANDLE ret = 0;
static __int64 volatile c = 0;
for (;;) {
swprintf(buf, 50, L"\\\\?\\pipe\\child\\%lld-%lld", InterlockedIncrement64(&c), GetTickCount64());
ret = CreateNamedPipeW(
buf,
dwOpenMode,
PIPE_TYPE_BYTE,
1,
4096,
4096,
5000,
NULL);
if (ret != INVALID_HANDLE_VALUE)
return ret;
int err = GetLastError();
if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
printf("CreateNamedPipeW: %d\n", err);
return INVALID_HANDLE_VALUE;
}
}
}
BOOL CreateCGI(WCHAR* cmd, IOCP* ctx) {
{
STARTUPINFOW si{ .cb = sizeof(si), .dwFlags = STARTF_USESTDHANDLES };
{
WCHAR buf[50];
SECURITY_ATTRIBUTES sa{ .nLength = sizeof(SECURITY_ATTRIBUTES), .bInheritHandle = TRUE };
ctx->sOut.pipe = newPipe(buf, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED);
if (ctx->sOut.pipe == INVALID_HANDLE_VALUE) {
return FALSE;
}
si.hStdOutput = CreateFileW(buf, GENERIC_WRITE, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
return FALSE;
}
ctx->sErr.pipe = newPipe(buf, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED);
if (ctx->sErr.pipe == INVALID_HANDLE_VALUE) {
return FALSE;
}
si.hStdError = CreateFileW(buf, GENERIC_WRITE, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdError == INVALID_HANDLE_VALUE) {
return FALSE;
}
ctx->sIn.pipe = newPipe(buf, PIPE_ACCESS_OUTBOUND);
if (ctx->sIn.pipe == INVALID_HANDLE_VALUE) { return FALSE; }
si.hStdInput = CreateFileW(buf, GENERIC_READ, 0, &sa, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (si.hStdInput == INVALID_HANDLE_VALUE) {
return FALSE;
}
}
PROCESS_INFORMATION pInfo;
if (CreateProcessW(
NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pInfo)==FALSE){
return FALSE;
}
CloseHandle(pInfo.hThread);
ctx->hProcess = pInfo.hProcess;
CloseHandle(si.hStdError);
CloseHandle(si.hStdInput);
CloseHandle(si.hStdOutput);
}
HANDLE hRet;
hRet = CreateIoCompletionPort(ctx->sOut.pipe, iocp, (ULONG_PTR)ctx, 0);
if (hRet == NULL) {
return FALSE;
}
hRet = CreateIoCompletionPort(ctx->sErr.pipe, iocp, (ULONG_PTR)ctx, 0);
if (hRet == NULL) {
return FALSE;
}
(void)ReadFile(ctx->sOut.pipe, ctx->sOut.buf, sizeof(ctx->sOut.buf), NULL, &ctx->sOut.ol);
(void)ReadFile(ctx->sErr.pipe, ctx->sErr.buf, sizeof(ctx->sErr.buf), NULL, &ctx->sErr.ol);
return TRUE;
}
handshake.cpp
#include <bcrypt.h>
#pragma comment (lib, "Crypt32.lib")
#pragma comment (lib, "bcrypt.lib")
static BCRYPT_ALG_HANDLE hAlgorithm=NULL;
static PBYTE hashO = NULL;
static BCRYPT_HASH_HANDLE hHash = NULL;
BOOL initHash(){
DWORD hashLen, dummy;
// we need hash many times
if ((BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_HASH_REUSABLE_FLAG)) < 0)
return FALSE;
if ((BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashLen, sizeof(hashLen), &dummy, 0)<0))
return FALSE;
hashO = (PBYTE)HeapAlloc(heap, 0, hashLen);
if (NULL == hashO)
{
return FALSE;
}
if ((BCryptCreateHash(
hAlgorithm,
&hHash,
hashO,
hashLen,
NULL,
0,
0))<0)
{
return FALSE;
}
return TRUE;
}
void closeHash(){
if (hAlgorithm)
{
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if (hashO)
{
HeapFree(heap, 0, hashO);
}
}
BOOL HashHanshake(void* key, ULONG length, char *buf/*29 bytes*/){
BYTE sha1_o[20]{};
// SHA-1 20 bytes hash
if ((BCryptHashData(hHash, (PUCHAR)key, length, 0)) < 0)
return FALSE;
if ((BCryptFinishHash(hHash, sha1_o, sizeof(sha1_o), 0)) < 0)
return FALSE;
DWORD size=29;
// base64 encoding
return CryptBinaryToStringA(sha1_o, sizeof(sha1_o), CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF , buf, &size);
}
frame.cpp
void websocketWrite(IOCP* ctx, const char* msg, ULONG length, OVERLAPPED* ol, WSABUF wsaBuf[2], Websocket::Opcode op = Websocket::Binary) {
ULONG hsize = 2;
ctx->header[0] = 0b10000000 | BYTE(op);
if (length < 126){
ctx->header[1] = (BYTE)length;
}else if (length < 0b10000000000000000){
hsize += 2;
ctx->header[1] = 126;
ctx->header[2] = (BYTE)(length >> 8);
ctx->header[3] = (BYTE)length;
}else{
puts("error: data too long");
return;
}
wsaBuf[0].buf = (char*)ctx->header;
wsaBuf[0].len = hsize;
wsaBuf[1].buf = (char*)msg;
wsaBuf[1].len = length;
WSASend(ctx->client, wsaBuf, 2, NULL, 0, ol, NULL);
_ASSERT(_CrtCheckMemory());
}
void onRecvData(IOCP* ctx) {
PBYTE mask = (PBYTE)ctx->buf;
PBYTE payload = mask + 4;
for (unsigned __int64 i = 0; i < ctx->payload_len; ++i) {
payload[i] = payload[i] ^ mask[i % 4];
}
{
switch (ctx->op) {
case Websocket::Text:
case Websocket::Binary:
{
(void)WriteFile(ctx->sIn.pipe, payload, (DWORD)ctx->payload_len, NULL, &ctx->sIn.ol);
}break;
case Websocket::Close:
{
if (ctx->payload_len >= 2) {
WORD code = ntohs(*(PWORD)payload);
printf("Websocket: closed frame: (code: %u, reason: %.*s)\n", code, (int)(ctx->payload_len-2), payload+2);
websocketWrite(ctx, (const char*)payload, (ULONG)ctx->payload_len, &ctx->sendOL, ctx->sendBuf, Websocket::Close);
}
}return;
case Websocket::Ping:
{
websocketWrite(ctx, (const char*)payload, (ULONG)ctx->payload_len, &ctx->sIn.ol, ctx->sIn.wsaBuf, Websocket::Pong);
}return;
default: {
CloseClient(ctx);
}return;
}
}
ctx->recvBuf[0].len = 6;
ctx->dwFlags = MSG_WAITALL;
ctx->recvBuf[0].buf = ctx->buf;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
ctx->Reading6Bytes = true;
}
void onRead6Complete(IOCP *ctx) {
using namespace Websocket;
PBYTE data = (PBYTE)ctx->buf;
BIT FIN = data[0] & 0b10000000;
if (!FIN) {
puts("FIN MUST be 1");
CloseClient(ctx);
return;
}
ctx->op = Websocket::Opcode(data[0] & 0b00001111);
if (data[0] & 0b01110000) {
puts("RSV is not zero");
CloseClient(ctx);
return;
}
BIT hasmask = data[1] & 0b10000000;
if (!hasmask) {
puts("client MUST mask data");
CloseClient(ctx);
return;
}
ctx->payload_len = data[1] & 0b01111111;
PBYTE precv;
ULONG offset;
switch (ctx->payload_len) {
default:
offset = 0;
data[0] = data[2];
data[1] = data[3];
data[2] = data[4];
data[3] = data[5];
precv = data + 4;
break;
case 126:
ctx->payload_len = ((WORD)(data[2]) << 8) | (WORD)(data[3]);
offset = 2;
data[0] = data[4];
data[1] = data[5];
precv = data + 2;
break;
case 127:
offset = 8;
ctx->payload_len = ntohll(*(unsigned __int64*)&data[2]);
precv = data + 0;
break;
}
if (ctx->payload_len == 0) {
ctx->Reading6Bytes = true;
return;
}
if (ctx->payload_len+6 > sizeof(ctx->buf)) {
puts("Error: data too large!");
CloseClient(ctx);
return;
}
ctx->Reading6Bytes = false;
ctx->recvBuf[0].len = (ULONG)ctx->payload_len + offset;
ctx->recvBuf[0].buf = (char*)precv;
ctx->dwFlags = MSG_WAITALL;
WSARecv(ctx->client, ctx->recvBuf, 1, NULL, &ctx->dwFlags, &ctx->recvOL, NULL);
}
Mine.cpp
#include <map>
#include <string>
std::map<std::wstring, const char*> mineTypeData = {
{L"html", "text/html"},
{L"css", "text/css"},
// skip
};
#include <shlwapi.h>
#pragma comment (lib, "Shlwapi.lib")
const char* getType(const WCHAR* name) {
auto s = PathFindExtensionW(name);
if (s == NULL) {
return "text/plain; charset=utf-8";
}
s++;
auto r = mineTypeData.find(s);
if (r == mineTypeData.end())
return "text/plain; charset=utf-8";
return r->second;
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Terminal</title>
<link rel="stylesheet" href="https://unpkg.com/xterm#4.18.0/css/xterm.css" />
<script src="https://unpkg.com/xterm#4.18.0/lib/xterm.js"></script>
<script src="https://unpkg.com/xterm-addon-webgl#0.12.0-beta.15/lib/xterm-addon-webgl.js"></script>
<script src="https://unpkg.com/xterm-addon-fit#0.5.0/lib/xterm-addon-fit.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght#600&display=swap" rel="stylesheet">
</head>
<body style="height: 100%;width: 100%;">
<script src="index.js"></script>
</body>
</html>
index.js
var terminal;
this.onerror = alert;
cmd = prompt("process name(e.g. wsl, bash, cmd, powershell)", "cmd");
if (!cmd) {
throw "error: user refuse to enter process name";
}
var ws = new WebSocket('ws://' + location.host, cmd);
ws.onopen = () => {
terminal.write('* * * connection established * * *\r\n');
};
terminal = new Terminal({
fontFamily: "'Source Sans Pro', 'Lucida Console','Source Code Pro', 'monospace'",
cursorBlink: true,
scrollback: 1000,
windowsMode: true,
bellStyle: "sound",
});
terminal.open(document.body);
ws.onclose = e => {
if (e.reason != '') {
terminal.write("\r\n* * * connection closed * * *\r\n"+e.reason);
}
else {
terminal.write('\r\n* * *connection closed...* * *\r\n' + e.code);
}
};
ws.onerror = console.error;
ws.onmessage = (m) => {
m.data.startsWith && terminal.write(m.data);
m.data.text && m.data.text().then((t) => {
terminal.write(t);
});
};
var buf = String();
terminal.onData((e) => {
switch (e) {
case '\u0003':
terminal.write('^C');
terminal.write("\r\n");
ws.send(e);
break;
case '\u0004':
terminal.write('^D');
terminal.write("\r\n");
ws.send(e);
break;
case '\r':
ws.send(buf + '\n');
terminal.write("\r\n");
buf = "";
break;
case '\u007F':
if (terminal._core.buffer.x > 2) {
terminal.write('\b \b');
if (buf.length > 0) {
buf = buf.substr(0, buf.length - 1);
}
}
break;
default:
if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
buf += e;
terminal.write(e);
}
}
});
const fitAddon = new FitAddon.FitAddon();
terminal.loadAddon(fitAddon);
fitAddon.fit();
terminal.focus();

Some files send on a HLS Server using Http Put request are partially uploaded

I have to upload audio chunks continuously on a HLS Server as a part of my project. I am able to upload chunks sucessfully using HTTP Put method with wininet API in C++ using following code.
bool CHTTP::HttpPut(char *szFile,int fileType)
{
bool bErrorFlag = false;
if(m_hInternet == NULL)
{
int retStatus = OpenHTTPSession();
if(retStatus < 1)
{
return true;
}
}
char szPostURL[256];
INTERNET_BUFFERS BufferIn = {0};
DWORD dwBytesRead;
DWORD dwBytesWritten;
BYTE pBuffer[350000];
BOOL bRead, bRet;
static int flag = 1;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS );
char szLocalFilePath[256];
if(fileType == AUDIO_CHUNK)
sprintf(szLocalFilePath,"%s/%s",m_strFilePath,szFile);
else
strcpy(szLocalFilePath,szFile);
int iFileSize = 0;
if(fileType == AUDIO_CHUNK)
{
strcpy(szPostURL,m_strPostPath);
strcat(szPostURL,"/");
strcat(szPostURL,szFile);
}
else if(fileType == M3U8)
strcpy(szPostURL,m_szM3U8FileToPost);
else if(fileType == AUTO_M3U8)
strcpy(szPostURL,m_szM3U8AutoPost);
DWORD dwFlags =
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD |INTERNET_FLAG_SECURE;
m_hRequest = HttpOpenRequest(m_hHttpSession, (const char*)"PUT",szPostURL, "HTTP/1.1",NULL , (const char**)"*/*\0",dwFlags, 1);
if(m_hRequest==NULL)
{
bErrorFlag = true;
CloseHTTPSession();
return bErrorFlag;
}
else
{
bErrorFlag = false;
}
int num_of_try = 0;
while(num_of_try < 3)
{
char logDump[1000];
num_of_try++;
HANDLE hFile = CreateFile (szLocalFilePath, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
bErrorFlag = true;
break;
}
else if(!m_bLogFlagCreateErr)
{
m_bLogFlagCreateErr = true;
sprintf(logDump,"CreateFile success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
BufferIn.dwBufferTotal = GetFileSize (hFile, NULL);
iFileSize = BufferIn.dwBufferTotal;
if(!HttpSendRequestEx( m_hRequest, &BufferIn, NULL, HSR_INITIATE, 0))
{
bErrorFlag = true;
m_bLogFlagSend = false;
sprintf(logDump,"Error on HttpSendRequestEx %lu %s",GetLastError(),szFile);
WriteLog(mLogFile,logDump);
break;
}
else
{
bErrorFlag = false;
sprintf(logDump,"HttpSendRequest success %s",szFile);
WriteLog(mLogFile,logDump);
}
DWORD sum = 0;
int size = 0;
do
{
bRead = ReadFile (hFile, pBuffer,iFileSize,
&dwBytesRead, NULL);
if(dwBytesRead != iFileSize)
{
sprintf(logDump,"dwBytesRead %d iFileSize %d %s",dwBytesRead,iFileSize,szFile);
WriteLog(mLogFile,logDump);
}
if(dwBytesRead > 0)
{
bRet=InternetWriteFile( m_hRequest, pBuffer, dwBytesRead,
&dwBytesWritten);
while(dwBytesRead < dwBytesWritten && bRet)
{
sprintf(logDump,"dwBytesRead %d dwBytesWritten %d %s",dwBytesRead,dwBytesWritten,szFile);
WriteLog(mLogFile,logDump);
bRet=InternetWriteFile( m_hRequest, pBuffer+dwBytesWritten, dwBytesRead - dwBytesWritten ,&dwBytesWritten);
}
if(!bRet)
{
int error = GetLastError();
sprintf(logDump,"InternetWriteFile %lu %s",error,szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = true;
break;
}
else
{
sprintf(logDump,"InternetWriteFile buffer success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
}
}
while (dwBytesRead == iFileSize);
CloseHandle (hFile);
if(!HttpEndRequest(m_hRequest, NULL, 0, 0))
{
int error = GetLastError();
if(error != 12032)
{
sprintf(logDump,"HttpEndRequest %lu %s",error,szFile);
WriteLog(mLogFile,logDump)
bErrorFlag = true;
break;
}
else
{
bErrorFlag = true;
continue;
}
}
else
{
sprintf(logDump,"HttpEndRequest success %s",szFile);
WriteLog(mLogFile,logDump);
DWORD dwCode, dwCodeSize;
dwCodeSize = sizeof(DWORD);
if(!HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwCodeSize, NULL))
{
int error = GetLastError();
char tmp[256];
sprintf(tmp,"HttpQueryfails %d %s",error, szFile);
WriteLog(mLogFile,tmp);
}
else
{
if(dwCode != 201 && dwCode != 204)
{
char tmp[256];
sprintf(tmp,"dwcode %d is not 201 or 204 %s",dwCode,szFile);
WriteLog(mLogFile,tmp);
bErrorFlag = true;
break;
}
}
bErrorFlag = false;
break;
}
}
CloseHTTPSession();
return bErrorFlag;
}
Most of the times chunks uploaded are of full size. Randomly chunks uploaded on server are not of full size as shown in following image.
sample image
In such case I am not getting any error messages, even dwBytesWritten returned by InternetWriteFile function is also correct. I am unable to understand what should I do to solve it. Any help in this regard is appreciated.

Printing from a process spawned from a service on Windows 2008 server

The short version:
I have written a small program to print a report. This program is exec'ed from our proprietary server, that is running as a windows service. It works in our development environment, but not on our customer's network.
When I attempt to access the printing system in the failing case, I get a "No default printer" error. We had the customer create a new login account that has a default printer defined, and restarted the server using that login account. Same error.
Note that this error is generated when trying to find a specified printer, not when we try to print to it.
Is there any way to convince a server-spawned process that printers do, in fact, exist?
The long version:
In our "current" production environment, we have the following:
1. a proprietary server that runs as a service under windows.
2. a desktop client
--> accesses data via that service
--> uses fastreport4 to generate reports
--> developed using C++Builder6 (and VCL)
3. a PocketPC-based application that runs on scanning devices
--> uses Apache to communicate with the service
--> also uses Apache to poke a cgi-bin application that will bring up
the desktop app in stealth mode, run a report, and print it.
I have been tasked with re-implementing the Pocket-PC functionality on an Android-based scanning device, and removing the Apache layer from the architecture. In the Android app, I've written a communication layer to access the server (service) directly (the same way the desktop does). The server has the ability to exec applications, so I've written a small one to just gather data, and call fastreport to format and print it.
Works great. No problem. ... in our development environment. It works in our office network with the server running on a Windows 7 system. It works when run from the command line on our customer's Windows 2008 server. It does not work when run from the service on the customer's server.
So here's the code. In my current revision, I have try/catch and debug print statements around (nearly) every line of code. I removed them for readability.
bool __fastcall TFormReportRunner::mySetPrinter(const char* name)
{
char pDevice[MAX_PATH];
char pDriver[MAX_PATH];
char APort[100];
UINT ADeviceMode;
bool printerFound = false;
bool errorFound = false;
String PrinterPort = String(name).UpperCase();
TPrinter* Prntr;
// I added this bit to see if it helps. Seems to make no difference
bool rc = SetDefaultPrinter("");
Prntr = Printer();
if (Prntr == NULL)
{
LogErrorMsg("Printer() returned null.");
return false;
}
int i = Prntr->Printers->Count - 1;
for (; i >= 0; i--)
{
// In the failing case, this next statement is the one that causes an exception.
Prntr->PrinterIndex = i;
Prntr->GetPrinter(pDevice, pDriver, APort, ADeviceMode);
DWORD SizeNeeded = 0;
HANDLE PrinterHandle;
if (OpenPrinter(pDevice, &PrinterHandle, NULL) == 0)
{
LogErrorMsg("Could not open printer");
return false;
}
GetPrinter(PrinterHandle, 2, NULL, 0, &SizeNeeded);
if (SizeNeeded == 0)
{
ClosePrinter(PrinterHandle);
LogErrorMsg("Could not retrieve printer info size");
return false;
}
PRINTER_INFO_2 PrinterInfo2;
char* buffer = new char[SizeNeeded];
if (GetPrinter(PrinterHandle, 2, buffer, SizeNeeded, &SizeNeeded) == 0)
{
ClosePrinter(PrinterHandle);
delete [] buffer;
LogErrorMsg("Could not retrieve printer info");
return false;
}
String PortName = ((PRINTER_INFO_2*)buffer)->pPortName;
delete [] buffer;
ClosePrinter(PrinterHandle);
if (PrinterPort == PortName)
{
frxReport1->PrintOptions->Printer = pDevice;
break;
}
}
Prntr->PrinterIndex = i;
return true;
}
One of the customer's IT guys says that in order to have the Apache version work they have to run Apache as administrator with a defined default printer AND have that admin account logged in for printing to work. He suspects that if we run our service in the same configuration it will start to work. I have not been able to replicate that on our network. My admin account always works, whether anyone is currently logged into the system, or not. But this is Windows 7/Professional, and not a server version.
This has got to be possible... Apache's doing it. If it can find a printer and print to it, I should be able to, right?
Any hints or help will be greatly appreciated. Anything. Really. :)
Thanks,
-Karen
Edit: Adding server-side code.
A couple of notes, first. One, this was written 20-ish years ago (by someone else). Two, it is NOT a VCL app (and was compiled with the Microsoft compiler). Three, we will not change it. Recompiling with current compilers is WAY too risky for something that is otherwise working.
int myServer::RunProcess2(ClientCall *call, vector<string> &args, vector<string> &env, const char* input, unsigned int insize,
string *output, string *error)
{
CancelProcess2(); // only one process allowed per connection
string cmdline;
for (unsigned int i = 0; i < args.size(); i++)
{
if (i != 0)
cmdline += ' ';
cmdline += args[i];
}
env.push_back(EnvPATH);
int size = 1;
for (unsigned int i = 0; i < env.size(); i++)
{
size += env[i].size() + 1;
}
char *environment = (char*)malloc(size);
if (environment == NULL)
{
call->error = "Could not allocate memory for process environment variables";
return 0;
}
char *ptr = environment;
for (unsigned int i = 0; i < env.size(); i++)
{
size = env[i].size() + 1;
memcpy(ptr, env[i].c_str(), size);
ptr += size;
}
ptr[0] = '\0';
HANDLE hInputReadPipe = NULL, hInputWritePipe = NULL;
HANDLE hReadPipe, hWritePipe;
HANDLE hErrorReadPipe, hErrorWritePipe;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
// create output pipe
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 4096) == 0)
{
free(environment);
call->error = "Error creation Pipe";
return 0;
}
// create error pipe
if (CreatePipe(&hErrorReadPipe, &hErrorWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
if (insize > 0)
{
// create input pipe
if (CreatePipe(&hInputReadPipe, &hInputWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
}
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hWritePipe;
si.hStdError = hErrorWritePipe;
si.hStdInput = hInputReadPipe;
PROCESS_INFORMATION pi;
if (CreateProcess(NULL, (char*)cmdline.c_str(), NULL, NULL, true, 0, environment, NULL, &si, &pi) == 0)
{
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
if (hInputReadPipe != NULL)
{
CloseHandle(hInputReadPipe);
CloseHandle(hInputWritePipe);
}
free(environment);
call->error = string("Error executing command: ") + cmdline + "\n" + GetErrorText();
return 0;
}
report_handle = pi.hProcess;
CloseHandle(hErrorWritePipe);
CloseHandle(hWritePipe);
if (hErrorReadPipe != NULL)
CloseHandle(hInputReadPipe);
char buffer[4097];
DWORD BytesAvail;
DWORD BytesRead = 0;
DWORD BytesWritten = 0;
DWORD BytesToRead = sizeof(buffer) - 1;
DWORD BytesToWrite = insize;
bool finished_readpipe = false, finished_errorpipe = false;
bool finished_inputpipe = (insize == 0);
int wait_time = 1;
bool error_occurred = false;
while (finished_readpipe == false || finished_errorpipe == false || finished_inputpipe == false)
{
if (finished_inputpipe == false)
{
if (BytesToWrite <= 0)
{
CloseHandle(hInputWritePipe);
hInputWritePipe = NULL;
finished_inputpipe = true;
continue;
}
BytesAvail = 1000;
/*if (PeekNamedPipe(hInputWritePipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
DWORD temp = GetLastError();
// pipe has been closed
finished_inputpipe = true;
continue;
}*/
if (BytesAvail > 0)
{
if (BytesAvail > BytesToWrite)
BytesAvail = BytesToWrite;
if (WriteFile(hInputWritePipe, input, BytesAvail, &BytesWritten, NULL) == 0)
{
if (GetLastError() == ERROR_NO_DATA)
{
int a = 2; // Pipe was closed (normal exit path).
}
finished_inputpipe = true;
continue;
}
input += BytesWritten;
BytesToWrite -= BytesWritten;
if (BytesToWrite == 0)
{
finished_inputpipe = true;
}
continue;
}
}
if (finished_readpipe == false)
{
while (true)
{
if (PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_readpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_readpipe = true;
break;
}
if (BytesRead == 0)
{
finished_readpipe = true;
break;
}
buffer[BytesRead] = '\0';
*output += buffer;
if (output->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Output limit reached";
}
}
if (finished_readpipe == true)
continue;
}
if (finished_errorpipe == false)
{
while (true)
{
if (PeekNamedPipe(hErrorReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_errorpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hErrorReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_errorpipe = true;
break;
}
if (BytesRead == 0)
{
finished_errorpipe = true;
break;
}
buffer[BytesRead] = '\0';
*error += buffer;
if (error->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Error output limit reached";
}
}
if (finished_errorpipe == true)
continue;
}
// don't tie up the server
if (wait_time < 100)
wait_time++;
Sleep(wait_time);
}
if (error_occurred == false)
WaitForSingleObject(pi.hProcess, INFINITE);
process_mutex.lock();
report_handle = NULL;
process_mutex.unlock();
DWORD exit_code = 0;
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hReadPipe);
CloseHandle(hErrorReadPipe);
if (hInputWritePipe != NULL)
CloseHandle(hInputWritePipe);
free(environment);
return exit_code;
}

Thread not completing properly, preventing application close

I'm writing a win32 form application and drawing it with Direct2D. I have a few cross threaded functions to do animations on it and I'm doing web requests with WinHTTP. The issue is, when I use any WinHttp functions (even just opening an HINTERNET session), it will cause the thread not to terminate properly. After I run the 'login' process once, the program cannot exit calmly. I've posted the relevant code below:
//the login process
void __cdecl processloginasync(void* arg)
{
//getting text from textboxes, etc.
if(usernamestr.find(L'#') != wstring::npos && usernamestr.find(L".") != wstring::npos) {
swapdrawmode(1);
_beginthread(loadwheel,NULL,arg);
void* result = NULL;
unsigned sz = 0;
int rescode = web_request(L"appurl.mywebsite.com/func.php",ss.str().c_str(),result,sz);
//other code to handle the reply...
swapdrawmode(0);
}
else {
error_str = L"Invalid email address.";
err = TRUE;
}
if(err == TRUE) {
textopacity = 0;
animatemode = 0;
_beginthread(animatetext,NULL,arg);
}
//I realize I haven't called 'free' on result, I'll fix that.
}
//the web_request function
int web_request (const wchar_t* server, const wchar_t* object, void*& dest, unsigned& size)
{
vector<void*> retval;
vector<unsigned> szs;
HINTERNET hSess = NULL, hConn = NULL, hReq = NULL;
int res = 0;
DWORD dwDownloaded = 0;
DWORD dwSize = 0;
DWORD retcode = NULL;
short err = FALSE;
const wchar_t* accepted_types[] = {
L"image/*",
L"text/*",
NULL
};
hSess = WinHttpOpen(L"smartCARS2 Web/1.1",WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(hSess)
hConn = WinHttpConnect(hSess,server,INTERNET_DEFAULT_HTTP_PORT, NULL);
else {
err = TRUE;
retcode = HTTP_OPEN_FAILED;
}
if(hConn)
hReq = WinHttpOpenRequest(hConn, NULL, object, NULL, WINHTTP_NO_REFERER,accepted_types,NULL);
else {
err = TRUE;
retcode = HTTP_CONN_FAILED;
}
if(hReq)
res = WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
else {
err = TRUE;
retcode = HTTP_OPENREQ_FAILED;
}
if(res)
res = WinHttpReceiveResponse(hReq, NULL);
else {
err = TRUE;
retcode = HTTP_SEND_REQ_FAILED;
}
DWORD tsize = 0;
if(res) {
do {
dwSize = 0;
if(!WinHttpQueryDataAvailable(hReq, &dwSize)) {
retcode = HTTP_COULD_NOT_QUERY_SIZE;
err = TRUE;
break;
}
if(!dwSize)
break;
tsize += dwSize;
void* rets = malloc(dwSize + 1);
if(!rets) {
break;
}
if(!WinHttpReadData(hReq, (void*)rets, dwSize, &dwDownloaded)) {
retcode = HTTP_COULD_NOT_READ_DATA;
err = TRUE;
break;
}
if(!dwDownloaded) {
retcode = HTTP_COULD_NOT_DOWNLOAD;
err = TRUE;
break;
}
szs.push_back(dwSize);
retval.push_back(rets);
} while(dwSize > 0);
}
size = tsize;
unsigned int sz = retval.size();
dest = malloc(tsize);
tsize = 0;
for(unsigned i = 0; i < sz; i++) {
memcpy((BYTE*)dest + tsize,retval[i],szs[i]);
free(retval[i]);
tsize += szs[i];
}
if(hSess)
WinHttpCloseHandle(hSess);
if(hConn)
WinHttpCloseHandle(hConn);
if(hReq)
WinHttpCloseHandle(hReq);
if(err == TRUE)
return retcode;
return 0;
}
As far as I know, as soon as the main thread terminates, the others are not waited for. So the problem is probably in your main thread. You just need to attach a debugger if not already being debugged (Debug | Attach to process in VS) to a zombie process and press "Break all", then use "Threads" and "Call stack" windows to figure what's happening.

modem call in c++

I'm trying to make a modem call in one end, and in the other end the program answers the call. It doesn't seem to detect the carrier. Am I doing anything wrong? Am I missing something?
int main(int argc, char** argv)
{
ParseArgs(argc,argv);
SerialPort* port = SerialPort::New();
if(!port)
Error(ErrorNoMemory,"Can't create port");
int error = port->Open(PortNum, SendFlag);
if(error)
Error(error,"Can't open port");
error = port->Initialise(PortBaud);
if(error)
Error(error,"Can't initialise port");
if(ReceiveFlag)
{
port->Listen();
Receive(port); //after the call is stablished I send a file
}
else
{
port->Dial(PhoneNumber);
Send(port);
}
port->Close();
delete port;
return 0;
}
The part of opening the port:
int Open(unsigned port, bool SendFlag)
{
// make name for port...
char name[] = "\\\\.\\com???.???";
char* nameNumber = name+sizeof(name)-8;
char* nameEnd = nameNumber;
if(port>999){
return ErrorInvalidPort;
}
if(port>99)
{
*nameEnd++ = '0'+port/100;
port %= 100;
}
if(port>9)
{
*nameEnd++ = '0'+port/10;
port %= 10;
}
*nameEnd++ = '0'+port;
*nameEnd = 0;
// open port...
hSerial = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hSerial==INVALID_HANDLE_VALUE)
{
switch(GetLastError())
{
case ERROR_FILE_NOT_FOUND:
return ErrorInvalidPort;
case ERROR_ACCESS_DENIED:
return ErrorPortInUse;
default:
return Error();
}
}
SetupComm( hSerial, 1024, 1024 );
if (!SendFlag)
{
if (!SetCommMask(hSerial, EV_RING |EV_RLSD))
// Error setting communications mask
printf("error mascara");
}
else
{
if (!SetCommMask(hSerial, EV_RLSD))
{
// Error setting communications mask
printf("error mascara");
}
}
return 0;
}
The initialise part:
int Initialise(unsigned baud)
{
// flush port...
if(!FlushFileBuffers(hSerial))
return Error();
if(!PurgeComm(hSerial, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR))
return Error();
// configure port...
DCB dcb ;
if(!GetCommState(hSerial, &dcb))
return Error();
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if(!SetCommState(hSerial, &dcb))
{
if(GetLastError()==ERROR_INVALID_PARAMETER)
return ErrorInvalidSettings;
return Error();
}
// set timeouts to zero so read/writes return immediately...
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier= 0;
if(!SetCommTimeouts(hSerial, &timeouts))
return Error();
// flush port again...
if(!PurgeComm(hSerial, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR))
return Error();
return 0;
}
The dial part:
int Dial(char *telefono)
{
unsigned long int n = 0;
DWORD dwCommEvent;
DWORD bytes;
DWORD dwRead;
char cadena[15];
char chRead;
sprintf(cadena, "ATDT%s\r", telefono);
if (!WriteFile( hSerial, "ATH1\r", strlen("ATH1\r"), (&(n)), 0 ))
{
printf("error");
}
FlushFileBuffers( hSerial );
Sleep(1000);
if (!WriteFile( hSerial, cadena, strlen(cadena), (&(n)), 0))
{
printf("error");
}
FlushFileBuffers( hSerial );
Sleep(10000);
printf("Marcamos");
do
{
printf("Espero eventos");
if(WaitCommEvent(hSerial, &dwCommEvent, NULL))
{
if(dwCommEvent & EV_RLSD)
{
printf("rlsd");
break;
}
else
{
printf("otro");
}
}
printf("fin del bucle");
} while(true);
return 0;
}
The listening part:
int Listen()
{
DWORD dwCommEvent;
unsigned long int n = 0;
do
{
printf("ESpero eventos");
if(WaitCommEvent(hSerial, &dwCommEvent, NULL))
{
if(dwCommEvent & EV_RING)
{
printf("RING");
if (!WriteFile( hSerial, "ATA\r", strlen("ATA\r"), (&(n)), 0 ))
{
printf("error");
}
FlushFileBuffers( hSerial );
break;
}
else if (dwCommEvent & EV_RLSD )
{
break;
}
}
printf("fin del bucle");
} while(true);
return 0;
}
You might have an easier time of it using Microsoft's Telephony API (TAPI) rather than trying to talk to the modem directly.
You may want to try issuing an ATS0=1 on the receiving side and let the modem autoanswer (substitute the number of rings you want instead of 1 if you wish).
You may discover that smart modems don't always pass through the ring (EV_RING) signal.
EDIT: Don't forget to 'ATS0=0' when you don't want the modem to auto-answer any more.
Maybe you need to apply the A0 and / or S0=1 Hayes command on the answering side.