Cannot read from anonymous pipe - c++

I began to study the anonymous pipe in windows and ran into a problem.
The program should create a child process that will handle the commands given ones to enter. But I can not read from the pipe until the child process works.
Here is the programm
#define _CRT_SECURE_NO_DEPRECATE
#include "windows.h"
#include "cstdio"
#include "tchar.h"
#include "io.h"
#include "conio.h"
#include "cstring"
static const int bufSize = 20;
int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPTSTR szCmdline = _tcsdup(TEXT("Project2\\Release\\command_handler.exe"));
HANDLE writePipe, readPipe, writePipeInput, readPipeInput;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if(!CreatePipe(&readPipe, &writePipe, &sa, 0) || !CreatePipe(&readPipeInput, &writePipeInput, &sa, 0))
{
printf("ERROR: cannot create pipe\n");
system("pause");
exit(1);
}
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = writePipe;
si.hStdInput = readPipeInput;
DWORD dwByteRead, dwByteWrite;
if(!CreateProcess(0, szCmdline, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi)) {
printf("ERROR: cannot create process\n");
system("pause");
exit(2);
}
CloseHandle(writePipe);
CloseHandle(readPipeInput);
printf("Procces ready!\n");
TQueue <int> q;
TQueue <int> tmp;
char c;
char s[bufSize];
int value;
for ( ; ; ) {
int i;
for(i = 0;i < bufSize && (s[i] = getchar()) != '\n';++i);
WriteFile(writePipeInput,s,i + 1,&dwByteWrite,NULL);
ReadFile(readPipe, &c, (int)1, &dwByteRead, NULL);
printf("Get : '%c'\n",c);
switch (c) {
case '+':
ReadFile(readPipe, &value, sizeof(int), &dwByteRead, NULL);
printf("%d\n",value);
q.push(value);
printf("OK\n");
break;
case '-':
if (!q.empty()) {
q.pop();
}
printf("OK\n");
break;
case 'f':
if (!q.empty()) {
printf("%d\nOK\n",q.firstEl());
} else {
printf("Queue is empty\nOK\n");
}
break;
case 'p':
if (q.empty()) {
printf("empty");
}
while (!q.empty()) {
tmp.push(q.firstEl());
printf("%d ",q.firstEl());
q.pop();
}
printf("\nOK\n");
while (!tmp.empty()) {
q.push(tmp.firstEl());
tmp.pop();
}
break;
case 'e':
printf("%s\nOK\n",q.empty()?"yes!":"no");
break;
case 'q':
break;
case '?':
printf("ERROR: undefined command\n");
break;
default:
printf("Fatal error: wrong symbol from hadeler '%c'\n",c);
//system("pause");
//exit(3);
}
if (c == 'q') break;
}
CloseHandle(readPipe);
CloseHandle(writePipeInput);
printf("Exited normaly\n");
system("pause");
}
And this is handler
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
static const int sz = 10;
char read_cmd(char *s) {
char c = getchar();
int i;
for (i = 0;(i < sz) && (c != ' ') && (c != '\n');i++) {
s[i] = c;
c = getchar();
// printf("%c",s[i]);
}
s[i] = '\0';
return c;
}
char correct(char res) {
if (res != '\n') {
while (getchar() != '\n');
return 0;
}
return 1;
}
int main() {
char s[sz + 1];
char c, res;
int val = 0;
for ( ; ; ) {
res = read_cmd(s);
if (strcmp(s,"add") == 0) {
val = 0;
while ((c = getchar()) >= '0' && c <= '9') {
val = val * 10 + c - '0';
}
res = c;
c = '+';
}else if (strcmp(s,"pop") == 0) {
c = '-';
}else if (strcmp(s,"first") == 0) {
c = 'f';
}else if (strcmp(s,"print") == 0) {
c = 'p';
}else if (strcmp(s,"empty") == 0) {
c = 'e';
}else if (strcmp(s,"help") == 0) {
c = 'h';
}else if (strcmp(s,"quit") == 0) {
c = 'q';
}else {
c = '?';
}
if (!correct(res)) c = '?';
printf("%c",c);
if (c == 'q') break;
if (c == '+') printf("%d",val);
}
return 0;
}
So if I run it and immediately type "quit" it works, otherwise it stop when trying get result from output pipe.
Why is this happening?
Thanks in advance for your reply.

I rewrote the program again and it worked. Honestly, I do not know that there was not correct. Here is a working version:
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 256
template <class T> class TQueue {
private:
class TQueEl {
public:
T value;
TQueEl *next, *prev;
};
TQueEl *first, *last;
int size;
public:
int& operator = (TQueue <T>& b){
while (!empty ()) {
pop ();
}
while (!b.empty ()) {
push(b.firstEl ());
b.pop ();
}
}
TQueue () {
size = 0;
first = last = nullptr;
}
void push (T val) {
if (size == 0) {
first = last = new TQueEl;
first -> next = first -> prev = nullptr;
first -> value = val;
} else {
last -> next = new TQueEl;
last -> next -> prev = last;
last = last -> next;
last -> next = nullptr;
last -> value = val;
}
size++;
}
T firstEl () {
return first -> value;
}
void pop () {
TQueEl *tmp=first;
if (size == 1) {
first = last = nullptr;
}
else if ( empty() ) {
// error("pop of empty queue");
}
else {
first = first -> next;
first -> prev = nullptr;
}
size--;
delete tmp;
}
bool empty () {
return !size;
}
~TQueue () {
TQueEl *tmp;
while (first != nullptr) {
tmp = first;
first = first -> next;
delete tmp;
}
}
};
DWORD WINAPI rerout(LPVOID params) {
DWORD dwByteWrite, dwByteRead;
char buffer[BUFSIZE];
HANDLE hParentStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE childInput = (HANDLE) params;
for ( ; ; ) {
ReadFile(hParentStdIn, buffer, BUFSIZE, &dwByteRead, NULL);
WriteFile(childInput, buffer, dwByteRead, &dwByteWrite, NULL);
}
return 0;
}
int main() {
HANDLE childInputRd = NULL;
HANDLE childInputWr = NULL;
HANDLE childOutputRd = NULL;
HANDLE childOutputWr = NULL;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if ( ! CreatePipe(&childOutputRd, &childOutputWr, &sa, 0) ) {
printf("Fatal error: cannot create child output pipe\n");
system("pause");
exit(1);
}
if (! CreatePipe(&childInputRd, &childInputWr, &sa, 0)) {
printf("Fatal error: cannot create child input pipe\n");
system("pause");
exit(2);
}
TCHAR szCmdline[]=TEXT("Project2\\Release\\command_handler.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.hStdError = childOutputWr;
si.hStdOutput = childOutputWr;
si.hStdInput = childInputRd;
si.dwFlags = STARTF_USESTDHANDLES;
if ( !CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ) {
printf("Fatal error: cannot create child process\n");
system("pause");
exit(3);
}
else {
CloseHandle(childInputRd);
CloseHandle(childOutputWr);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
DWORD reroutThreadID;
HANDLE inputRerouting = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)rerout,(LPVOID) childInputWr,0 , &reroutThreadID);
if (inputRerouting == NULL) {
printf("Fatal error: cannot create thread\n");
system("pause");
exit (4);
}
TQueue <int> q;
TQueue <int> tmp;
DWORD dwRead;
CHAR c;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;) {
ReadFile( childOutputRd, &c, sizeof(char), &dwRead, NULL);
switch (c) {
case '+':
int value;
ReadFile( childOutputRd, &value, sizeof(int), &dwRead, NULL);
q.push(value);
printf("OK\n");
break;
case '-':
if (!q.empty()) {
q.pop();
printf("OK\n");
}
else {
printf("Queue is empty!\n");
}
break;
case 'f':
if (!q.empty()) {
printf("%d\n",q.firstEl());
} else {
printf("Queue is empty!\n");
}
break;
case 'p':
printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");
if (q.empty()) {
printf("empty");
}
while (!q.empty()) {
tmp.push(q.firstEl());
printf("%d ",q.firstEl());
q.pop();
}
while (!tmp.empty()) {
q.push(tmp.firstEl());
tmp.pop();
}
printf("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\nOK\n");
break;
case 'e':
printf("%s\n",q.empty()?"YES":"NO");
break;
case 'q':
break;
case '?':
printf("ERROR: undefined command\n");
break;
default:
printf("Fatal error: wrong symbol from hadeler '%c'\n",c);
system("pause");
exit(5);
}
if (c == 'q') break;
}
CloseHandle(inputRerouting);
CloseHandle(childInputWr);
CloseHandle(childOutputRd);
system("pause");
return 0;
}
And the handler:
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 5
HANDLE hStdin, hStdout;
DWORD dwRead, dwWritten;
void readCmd(char* s) {
ReadFile(hStdin, s, 1, &dwRead, NULL);
int i;
for (i = 0;(i < BUFSIZE) && (s[i] != ' ') && (s[i] != '\n') && (s[i] != '\r');i++) {
ReadFile(hStdin, s + i + 1, 1, &dwRead, NULL);
}
s[i] = '\0';
}
bool correct() {
char c;
ReadFile(hStdin, &c, sizeof(char), &dwRead, NULL);
if (c != '\n') {
while (c != '\n') ReadFile(hStdin, &c, sizeof(char), &dwRead, NULL);
return 0;
}
return 1;
}
int main() {
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if ( (hStdout == INVALID_HANDLE_VALUE) || (hStdin == INVALID_HANDLE_VALUE) ) {
ExitProcess(1);
}
int val;
char s[BUFSIZE + 1];
char c,digit;
for (;;) {
readCmd(s);
if (strcmp(s,"add") == 0) {
val = 0;
ReadFile(hStdin, &digit, 1, &dwRead, NULL);
if (digit == '\n') {
c = '?';
WriteFile(hStdout,&c,sizeof(char),&dwRead,NULL);
continue;
}
else if (digit < '0' || digit > '9') {
c = '?';
}
else {
c = '+';
}
while (digit >= '0' && digit <= '9') {
val = val * 10 + digit - '0';
ReadFile(hStdin, &digit, 1, &dwRead, NULL);
}
}else if (strcmp(s,"pop") == 0) {
c = '-';
}else if (strcmp(s,"first") == 0) {
c = 'f';
}else if (strcmp(s,"print") == 0) {
c = 'p';
}else if (strcmp(s,"empty") == 0) {
c = 'e';
}else if (strcmp(s,"help") == 0) {
c = 'h';
}else if (strcmp(s,"quit") == 0) {
c = 'q';
}else {
c = '?';
}
if (!correct()) c = '?';
WriteFile(hStdout, &c, 1, &dwRead, NULL);
if (c == 'q') break;
if (c == '+') WriteFile(hStdout, &val, sizeof(int), &dwRead, NULL);
}
return 0;
}
Thank you all for your help!

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.

multi-serial port communication on windows

I am working on a project which need to operate two serial port as the same time on windows, but whatever I try I can only open one serial port. Unless repeat open and close, I can operate the two ports but it is too slow. I need to write and read first port and formalize it by protocol and sent it to another port, but I can not initial two port. I was using open and close two ports repeatly to do that before but it was toooooooo slow. Anybody know how to open two port in the same time; Thx!!!
here are my code here:
#include <iostream>
#include <iomanip>
#include "udp.h"
#include "Serial.h"
#include "Formalized.h"
using namespace std;
bool sthread = false;
unsigned char status[6] = {};
HANDLE hMutex = NULL;
DWORD WINAPI ULTRAread(LPVOID lpParamter)
{
Cserial ultra;
unsigned char input0[7] = { 0x00,0x01,0x00,0x01,0x01,0xE5,0xAC };
unsigned char input1[7] = { 0x00,0x02,0x00,0x01,0x01,0xE5,0xE8 };
unsigned char input2[7] = { 0x00,0x03,0x00,0x01,0x01,0xE4,0x14 };
unsigned char input3[7] = { 0x00,0x04,0x00,0x01,0x01,0xE5,0x60 };
unsigned char input4[7] = { 0x00,0x05,0x00,0x01,0x01,0xE4,0x9C };
unsigned char input5[7] = { 0x00,0x06,0x00,0x01,0x01,0xE4,0xD8 };
unsigned char* input[6] = { input0,input1,input2,input3,input4,input5 };
unsigned char pool[8] = {};
if (!ultra.Initcom("COM10")) //ultrasonic COM interface
{
cout << "Init ultrasonic failure \n";
getchar();
sthread = false;
}
else
{
sthread = true;
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < 6; i++)
{
if (ultra.Write(input[i], 7))
{
ultra.Read(pool, 8);
}
switch (pool[5])
{
case 0x01:
status[i] = 0x01;
break;
case 0x00:
status[i] = 0x00;
break;
}
}
Sleep(5000);
ReleaseMutex(hMutex);
}
}
}
int main()
{
HANDLE uThread = CreateThread(NULL, 0, ULTRAread, NULL, 0, NULL);
hMutex = CreateMutex(NULL, FALSE, "ultrasonics status");
Cserial zigbee;
cUDP UWB;
char buff[300];
char data[256];
if (!UWB.Initial(6685)) //latitude and longitude port
{
cout << "UWB Port connecting failure \n";
getchar();
}
else
{
cout << "UWB Com connecting success \n";
}
if (!zigbee.Initcom("COM7")) //zigbee access port
{
cout << "Zigbee Initial Failure! \n";
getchar();
}
else
{
cout << "Zigbee Initial Success! \n";
}
for (;;)
{
WaitForSingleObject(hMutex, INFINITE);
switch (sthread)
{
case TRUE: //ultrasonics trustful
WaitForSingleObject(hMutex, INFINITE);
cout << "1";
ReleaseMutex(hMutex);
break;
case FALSE:
cout << "2";
break;
}
Sleep(5000);
}
CloseHandle(uThread);
getchar();
return 0;
}
#include"Serial.h"
Cserial::Cserial()
{
hcom = INVALID_HANDLE_VALUE;
}
Cserial::~Cserial()
{
}
bool Cserial::Initcom(LPCSTR Port)
{
hcom = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL, 0);
if (hcom == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "fail serial1\n");
return false;
}
SetupComm(hcom, 1024, 1024);
DCB dcb;
GetCommState(hcom, &dcb);
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = 0;
dcb.StopBits = 1;
SetCommState(hcom, &dcb);
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts(hcom, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 10;
CommTimeouts.ReadTotalTimeoutConstant = 100;
CommTimeouts.WriteTotalTimeoutMultiplier = 1;
CommTimeouts.WriteTotalTimeoutConstant = 10;
if (!SetCommTimeouts(hcom, &CommTimeouts))
{
fprintf(stderr, "fail serial2\n");
return FALSE;
}
return true;
}
void Cserial::Uninitcom()
{
CloseHandle(hcom);
hcom = INVALID_HANDLE_VALUE;
}
bool Cserial::Clearcom()
{
PurgeComm(hcom, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT);
return TRUE;
}
bool Cserial::Write(unsigned char* buf, int len)
{
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
DWORD dwWrittenLen = 0;
if (!WriteFile(hcom, buf, len, &dwWrittenLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}
bool Cserial::Read(unsigned char* buf, int len)
{
DWORD dwReadLen = 0;
if (hcom == INVALID_HANDLE_VALUE)
{
return FALSE;
}
if (!ReadFile(hcom, buf, len, &dwReadLen, NULL))
{
return FALSE;
}
else
{
return TRUE;
}
}

C++ Pipe Skills that makes the ReadFile and WriteFile functions available at the same time

Pipe is being used, If server call WriteFile/ReadFile, client have to call ReadFile/WriteFile for synchronize each other.I implemented it like this.
Example :
**COMMON : **
BOOL RecvData(DWORD &dwScore)
{
DWORD dwRead = 0;
if(0 == ReadFile(g_hPipe, &dwScore, sizeof(DWORD), &dwRead, 0))
return FALSE;
if (sizeof(DWORD) != dwRead)
return FALSE;
return TRUE;
}
BOOL SendData(DWORD dwScore)
{
DWORD dwSend = 0;
if(0 == WriteFile(g_hPipe, &dwScore, sizeof(DWORD), &dwSend, 0))
return FALSE;
if (sizeof(DWORD) != dwSend)
return FALSE;
return TRUE;
}
**SERVER : **
VOID StartServerManager()
{
DWORD dwScore = 0;
while (TRUE)
{
if(FALSE == RecvData(dwScore))
continue;
//.... do something dwScore + dwSkill ...
if(FALSE == SendData(dwScore))
continue;
}
}
**CLIENT : **
#define VILLA_READ_MODE 0xDEDC
#define VILLA_WRITE_MODE 0xCDEC
VOID StartClientManager()
{
DWORD dwScore = 4;
DWORD dwMode = VILLA_WRITE_MODE;
while(TRUE)
{
switch(dwMode)
{
case VILLA_WRITE_MODE:
SendData(dwScore);
dwMode = VILLA_READ_MODE;
break;
case VILLA_READ_MODE:
RecvData(dwScore);
dwMode = VILLA_WRITE_MODE;
break;
}
}
}
If I change StartServerManager() into like this
VOID StartServerManager()
{
DWORD dwScore = 0;
while (TRUE)
{
if(FALSE == RecvData(dwScore))
continue;
//.... do something dwScore + dwSkill ...
if(FALSE == SendData(dwScore))
continue;
if(FALSE == RecvData(dwScore))
continue;
if(FALSE == RecvData(dwScore))
continue;
}
}
Function will block at ReadFile()
if(0 == ReadFile(g_hPipe, &dwScore, sizeof(DWORD), &dwRead, 0))
BOOL RecvData(DWORD &dwScore)
{
DWORD dwRead = 0;
if(0 == ReadFile(g_hPipe, &dwScore, sizeof(DWORD), &dwRead, 0))
return FALSE;
if (sizeof(DWORD) != dwRead)
return FALSE;
return TRUE;
}
Implemented with threads, too.
Is there any method pass block on ReadFile?
I thought about this...
#define VILLA_READ_MODE 0xDEDC
#define VILLA_WRITE_MODE 0xCDEC
VOID StartClientManager()
{
HANDLE hThread[2];
DWORD dwModeRead = VILLA_READ_MODE;
DWORD dwModeWrite = VILLA_WRITE_MODE;
hThread[1] = CreateThread(NULL, 0, SYNCTHREAD, &dwModeRead, 0, 0);
hThread[2] = CreateThread(NULL, 0, SYNCTHREAD, &dwModeWrite, 0, 0);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
}
DWORD WINAPI SYNCTHREAD(DWORD &dwMode)
{
switch (dwMode)
{
case VILLA_READ_MODE:
RecvData(dwScore);
break;
case VILLA_WRITE_MODE:
SendData(dwScore);
break;
}
}

Change Mac address by adapter description

I'm using the excellent tool provided by Nate True # http://devices.natetrue.com/macshift/
It changes the Mac address by the adapter name. This is the source code:
const int versionMajor = 1;
const int versionMinor = 1;
#include <windows.h>
#include <objbase.h>
#include <netcon.h>
#include <stdio.h>
#include "validmacs.h"
void SetMAC(char * AdapterName, char * NewMAC) {
HKEY hListKey = NULL;
HKEY hKey = NULL;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
0, KEY_READ, &hListKey);
if (!hListKey) {
printf("Failed to open adapter list key\n");
return;
}
FILETIME writtenTime;
char keyNameBuf[512], keyNameBuf2[512];
DWORD keyNameBufSiz = 512;
DWORD crap;
int i = 0;
bool found = false;
while (RegEnumKeyEx(hListKey, i++, keyNameBuf, &keyNameBufSiz, 0, NULL, NULL, &writtenTime)
== ERROR_SUCCESS) {
_snprintf(keyNameBuf2, 512, "%s\\Connection", keyNameBuf);
hKey = NULL;
RegOpenKeyEx(hListKey, keyNameBuf2, 0, KEY_READ, &hKey);
if (hKey) {
keyNameBufSiz = 512;
if (RegQueryValueEx(hKey, "Name", 0, &crap, (LPBYTE)keyNameBuf2, &keyNameBufSiz)
== ERROR_SUCCESS && strcmp(keyNameBuf2, AdapterName) == 0) {
printf("Adapter ID is %s\n", keyNameBuf);
found = true;
break;
}
RegCloseKey(hKey);
}
keyNameBufSiz = 512;
}
RegCloseKey(hListKey);
if (!found) {
printf("Could not find adapter name '%s'.\nPlease make sure this is the name you gave it in Network Connections.\n", AdapterName);
return;
}
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}",
0, KEY_READ, &hListKey);
if (!hListKey) {
printf("Failed to open adapter list key in Phase 2\n");
return;
}
i = 0;
char buf[512];
while (RegEnumKeyEx(hListKey, i++, keyNameBuf2, &keyNameBufSiz, 0, NULL, NULL, &writtenTime)
== ERROR_SUCCESS) {
hKey = NULL;
RegOpenKeyEx(hListKey, keyNameBuf2, 0, KEY_READ | KEY_SET_VALUE, &hKey);
if (hKey) {
keyNameBufSiz = 512;
if ((RegQueryValueEx(hKey, "NetCfgInstanceId", 0, &crap, (LPBYTE)buf, &keyNameBufSiz)
== ERROR_SUCCESS) && (strcmp(buf, keyNameBuf) == 0)) {
RegSetValueEx(hKey, "NetworkAddress", 0, REG_SZ, (LPBYTE)NewMAC, strlen(NewMAC) + 1);
//printf("Updating adapter index %s (%s=%s)\n", keyNameBuf2, buf, keyNameBuf);
//break;
}
RegCloseKey(hKey);
}
keyNameBufSiz = 512;
}
RegCloseKey(hListKey);
}
void ResetAdapter(char * AdapterName) {
struct _GUID guid = {0xBA126AD1,0x2166,0x11D1,0};
memcpy(guid.Data4, "\xB1\xD0\x00\x80\x5F\xC1\x27\x0E", 8);
unsigned short * buf = new unsigned short[strlen(AdapterName)+1];
void (__stdcall *NcFreeNetConProperties) (NETCON_PROPERTIES *);
HMODULE NetShell_Dll = LoadLibrary("Netshell.dll");
if (!NetShell_Dll) {
printf("Couldn't load Netshell.dll\n");
return;
}
NcFreeNetConProperties = (void (__stdcall *)(struct tagNETCON_PROPERTIES *))GetProcAddress(NetShell_Dll, "NcFreeNetconProperties");
if (!NcFreeNetConProperties) {
printf("Couldn't load required DLL function\n");
return;
}
for (unsigned int i = 0; i <= strlen(AdapterName); i++) {
buf[i] = AdapterName[i];
}
CoInitialize(0);
INetConnectionManager * pNCM = NULL;
HRESULT hr = ::CoCreateInstance(guid,
NULL,
CLSCTX_ALL,
__uuidof(INetConnectionManager),
(void**)&pNCM);
if (!pNCM)
printf("Failed to instantiate required object\n");
else {
IEnumNetConnection * pENC;
pNCM->EnumConnections(NCME_DEFAULT, &pENC);
if (!pENC) {
printf("Could not enumerate Network Connections\n");
}
else {
INetConnection * pNC;
ULONG fetched;
NETCON_PROPERTIES * pNCP;
do {
pENC->Next(1, &pNC, &fetched);
if (fetched && pNC) {
pNC->GetProperties(&pNCP);
if (pNCP) {
if (wcscmp(pNCP->pszwName, buf) == 0) {
pNC->Disconnect();
pNC->Connect();
}
NcFreeNetConProperties(pNCP);
}
}
} while (fetched);
pENC->Release();
}
pNCM->Release();
}
FreeLibrary(NetShell_Dll);
CoUninitialize ();
}
bool IsValidMAC(char * str) {
if (strlen(str) != 12) return false;
for (int i = 0; i < 12; i++) {
if ((str[i] < '0' || str[i] > '9')
&& (str[i] < 'a' || str[i] > 'f')
&& (str[i] < 'A' || str[i] > 'F')) {
return false;
}
}
return true;
}
void ShowHelp() {
printf("Usage: macshift [options] [mac-address]\n\n");
printf("Options:\n");
printf("\t-i [adapter-name] The adapter name from Network Connections.\n");
printf("\t-r Uses a random MAC address. This is the default.\n");
printf("\t-d Restores the original MAC address.\n");
printf("\t--help Shows this screen.\n\n");
printf("Macshift uses special undocumented functions in the Windows COM Interface that\n");
printf(" allow you to change an adapter's MAC address without needing to restart.\n");
printf("When you change a MAC address, all your connections are closed automatically\n");
printf(" and your adapter is reset.\n");
}
//Generates a random MAC that is actually plausible
void RandomizeMAC(char * newmac) {
_snprintf(newmac, 6, "%06X", rand() % numMacs);
for (int i = 3; i < 6; i++) {
_snprintf(&newmac[i*2], 2, "%02X", rand() & 0xFF);
}
newmac[12] = 0;
}
int main(int argc, char * * argv) {
printf("Macshift v%i.%i, MAC Changing Utility by Nathan True, macshift#natetrue.com\n\n", versionMajor, versionMinor);
//Parse commandline arguments
char * adapter = "Wireless";
char newmac[13];
int i;
if (argc == 1) {
ShowHelp();
return 0;
}
//Start out with a random MAC
srand(GetTickCount());
RandomizeMAC(newmac);
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case '-': //Extended argument
if (strcmp(argv[i]+2, "help") == 0) {
ShowHelp();
return 0;
}
break;
case 'r': //Random setting, this is the default
break;
case 'i': //Adapter name follows
if (argc > i + 1) adapter = argv[++i];
break;
case 'd': //Reset the MAC address
newmac[0] = 0;
}
}
else {
if (IsValidMAC(argv[i])) strncpy(newmac, argv[i], 13);
else printf("MAC String %s is not valid. MAC addresses must m/^[0-9a-fA-F]{12}$/.\n", argv[i]);
}
}
printf("Setting MAC on adapter '%s' to %s...\n", adapter, newmac[0] ? newmac : "original MAC");
SetMAC(adapter, newmac);
printf("Resetting adapter...\n");
fflush(stdout);
ResetAdapter(adapter);
printf("Done\n");
return 0;
}
I would like to change the Mac address by the description of an adapter, in addition to the name. So I need to modify this code so that if a matching name is not found, it falls back to changing the mac based on the description.
This is an example adapter:
name: Local Area Connection
description: Marvell Yukon 88E8055 PCI-E Gigabit Ethernet Controller
Unfortunately, being a Java developer I have limited experience with C++, so any help would be greatly appreciated.
Here's a C sample of GetAdaptersInfo, it prints out each adapters name and description (you can easily adapt it for your code by calling SetMAC on a match):
VOID EnumerateNetworkAdapters(VOID)
{
ULONG len = 0;
if (ERROR_BUFFER_OVERFLOW == GetAdaptersInfo(NULL, &len))
{
IP_ADAPTER_INFO *ipai;
ipai = malloc(len);
if (NO_ERROR == GetAdaptersInfo(ipai, &len))
{
IP_ADAPTER_INFO *p = ipai;
do
{
printf("name=%s description=%s\n", p->AdapterName, p->Description);
}
while (p = p->Next);
}
free(ipai);
}
}
Link against "Iphlpapi.lib".
Use GetAdaptersAddresses to get the adapter name(PIP_ADAPTER_ADDRESSES::FriendlyName) by comparing the description(PIP_ADAPTER_ADDRESSES::Description) before the call to SetMAC(). Example can be found in above MSDN link.