diff --git a/.gitignore b/.gitignore index b883f1f..a61f079 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.exe +/rcs diff --git a/Makefile b/Makefile index a518f3d..3abe7d7 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,54 @@ -winediscordipcbridge.exe: main.c - i686-w64-mingw32-gcc -masm=intel main.c -o winediscordipcbridge +OUTPUT ?= winediscordipcbridge.exe +DEBUG ?= false + + +CC := i686-w64-mingw32-gcc +MC := i686-w64-mingw32-windmc +RC := i686-w64-mingw32-windres + +STRIP := i686-w64-mingw32-strip + +<> := $(wildcard src/*.c) +<> := $(wildcard res/*.mc) +<> := $(wildcard res/*.rc) +<> := $(<>:src/%.c=obj/%.o) +<> += $(<>:res/%.mc=obj/%.rc.o) +<> += $(<>:res/%.rc=obj/%.rc.o) + +<< := @echo +ifneq ($(shell eval 'echo -e'),-e) + << += -e +endif + +src/%.h rcs/%.rc : res/%.mc + $(<<) " MC\t"$(<) + @mkdir -p rcs && $(MC) -b -h src -r rcs $(<) + +obj/%.rc.o: res/%.rc + $(<<) " RC\t"$(@) + @$(RC) -I src $(<) $(@) + +obj/%.rc.o: rcs/%.rc + $(<<) " RC\t"$(@) + @$(RC) -I src $(<) $(@) + +obj/%.o: src/%.c + $(<<) " CC\t$(<)" + @mkdir -p obj && $(CC) -masm=intel -c $(<) -o $(@) + +all: $(OUTPUT) + +obj/service-manager.o: src/error.h + +$(OUTPUT): $(<>) + $(<<) " LINK\t$(@)" + @$(CC) $(^) -o $(@) -lshlwapi + @test "$(DEBUG)" = "true" || $(<<:@%=%) "STRIP\t$(@)" + @test "$(DEBUG)" = "true" || $(STRIP) $(@) + +clean: + @rm -fv $(<>) rcs/* $(<>:res/%.mc=src/%.h) $(<>:res/%.rc=src/%.h) $(OUTPUT) + @rm -rf rcs obj + +.PHONY: clean all +.INTERMEDIATE: $(<>:res/%.mc=src/%.h) $(<>:res/%.rc=src/%.h) $(<>:res/%.mc=rcs/%.rc) diff --git a/README.md b/README.md index fa54a0c..30596b6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ The way it works is simply by bridging the gap between Windows named pipes Compiling ========= - i686-w64-mingw32-gcc -masm=intel main.c -o winediscordipcbridge +```sh +make +``` Usage (Wine) ============ @@ -24,6 +26,18 @@ Start the bridge first, wait for it to start listening to the pipe, and then launch your program/game. The two programs need to be running under the same wineprefix. +Usage (wine service) +==================== + +```sh +WINEPREFIX= wine ./winediscordipcbridge.exe install +``` + +Run this (^) snippet to install the bridge as a service, next time you start +anything in wine, the bridge will automatically start and connect your game +and discord. + + Usage (Steam Proton) ==================== diff --git a/main.c b/main.c deleted file mode 100644 index e381de2..0000000 --- a/main.c +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#include -#include -#include - -#pragma region -struct sockaddr_un { - unsigned short sun_family; /* AF_UNIX */ - char sun_path[108]; /* pathname */ -}; -/* struct sockaddr { */ -/* unsigned short sa_family; // address family, AF_xxx */ -/* char sa_data[14]; // 14 bytes of protocol address */ -/* }; */ -#define AF_UNIX 1 -#define SOCK_STREAM 1 -#define F_SETFL 4 -#define O_RDONLY 00000000 -#define O_WRONLY 00000001 -#define O_CREAT 00000100 -#define O_APPEND 00002000 -#define O_NONBLOCK 00004000 -#define BUFSIZE 2048 // size of read/write buffers - -#pragma endregion wine-specific header thingy -#pragma region -__declspec(naked) int l_close(int fd) { - __asm__ ( - "push ebx\n\t" - - "mov eax, 0x06\n\t" - "mov ebx, [esp + 4 + 4]\n\t" - "int 0x80\n\t" - - "pop ebx\n\t" - "ret" - ); -} -__declspec(naked) int l_socketcall(int call, void* args) { - __asm__ ( - "push ebx\n\t" - - "mov eax, 0x66\n\t" - "mov ebx, [esp + 4 + 4]\n\t" - "mov ecx, [esp + 4 + 8]\n\t" - "int 0x80\n\t" - - "pop ebx\n\t" - "ret" - ); -} -__declspec(naked) int l_open(const char* filename, int flags, int mode) { - __asm__ ( - "push ebx\n\t" - - "mov eax, 0x05\n\t" - "mov ebx, [esp + 4 + 4]\n\t" - "mov ecx, [esp + 4 + 8]\n\t" - "mov edx, [esp + 4 + 12]\n\t" - "int 0x80\n\t" - - "pop ebx\n\t" - "ret" - ); -} -__declspec(naked) int l_write(unsigned int fd, const char* buf, unsigned int count) { - __asm__ ( - "push ebx\n\t" - - "mov eax, 0x04\n\t" - "mov ebx, [esp + 4 + 4]\n\t" - "mov ecx, [esp + 4 + 8]\n\t" - "mov edx, [esp + 4 + 12]\n\t" - "int 0x80\n\t" - - "pop ebx\n\t" - "ret" - ); -} -__declspec(naked) int l_read(unsigned int fd, char* buf, unsigned int count) { - __asm__ ( - "push ebx\n\t" - - "mov eax, 0x03\n\t" - "mov ebx, [esp + 4 + 4]\n\t" - "mov ecx, [esp + 4 + 8]\n\t" - "mov edx, [esp + 4 + 12]\n\t" - "int 0x80\n\t" - - "pop ebx\n\t" - "ret" - ); -} -#pragma endregion syscall wrappers -#pragma region -int l_socket(int domain, int type, int protocol) { - void* args[3]; - args[0] = (void*)(int*)domain; - args[1] = (void*)(int*)type; - args[2] = (void*)(int*)protocol; - return l_socketcall(1, args); -} -int l_connect(int sockfd, const struct sockaddr *addr, unsigned int addrlen) { - void* args[3]; - args[0] = (void*)(int*)sockfd; - args[1] = (void*)addr; - args[2] = (void*)(int*)addrlen; - return l_socketcall(3, args); -} -/* int send(int sockfd, const void* buf, unsigned int len, int flags) { */ -/* void* args[4]; */ -/* args[0] = (void*)(int*)sockfd; */ -/* args[1] = (void*)buf; */ -/* args[2] = (void*)(unsigned int*)len; */ -/* args[3] = (void*)(int*)flags; */ -/* return l_socketcall(9, args); */ -/* } */ -/* int recv(int fd, void* buf, unsigned int len, int flags) { */ -/* void* args[4]; */ -/* args[0] = (void*)(int*)fd; */ -/* args[1] = (void*)buf; */ -/* args[2] = (void*)(unsigned int*)len; */ -/* args[3] = (void*)(int*)flags; */ -/* return l_socketcall(10, args); */ -/* } */ -#pragma endregion socketcall wrappers - -char* getenv_(char* name) // written by https://github.com/Francesco149 -{ - static char buf[1024 * 1024]; - static char* end = 0; - unsigned int namelen; - char* p; - - if (!end) { - int fd, n; - - fd = l_open("/proc/self/environ", 0, 0); - if (fd < 0) { - return 0; - } - - n = l_read((unsigned int)fd, buf, (unsigned int)sizeof(buf)); - if (n < 0) { - return 0; - } - - l_close(fd); - end = buf + n; - } - - namelen = strlen(name); - - for (p = buf; p < end;) { - if (!strncmp(p, name, namelen)) { - return p + namelen + 1; /* skip name and the = */ - } - - for (; *p && p < end; ++p); /* skip to next entry */ - ++p; - } - - return 0; -} - -static const char* get_temp_path() -{ - const char* temp = getenv_("XDG_RUNTIME_DIR"); - temp = temp ? temp : getenv_("TMPDIR"); - temp = temp ? temp : getenv_("TMP"); - temp = temp ? temp : getenv_("TEMP"); - temp = temp ? temp : "/tmp"; - return temp; -} - -static HANDLE hPipe = INVALID_HANDLE_VALUE; -static int sock_fd; -DWORD WINAPI winwrite_thread(LPVOID lpvParam); - -int _tmain(VOID) -{ - BOOL fConnected = FALSE; - DWORD dwThreadId = 0; - HANDLE hThread = NULL; - LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0"); - - // The main loop creates an instance of the named pipe and - // then waits for a client to connect to it. When the client - // connects, a thread is created to handle communications - // with that client, and this loop is free to wait for the - // next client connect request. It is an infinite loop. - - _tprintf( TEXT("Pipe Server: Main thread awaiting client connection on %s\n"), lpszPipename); - hPipe = CreateNamedPipe( - lpszPipename, // pipe name - PIPE_ACCESS_DUPLEX, // read/write access - PIPE_TYPE_BYTE | // message type pipe - PIPE_READMODE_BYTE | // message-read mode - PIPE_WAIT, // blocking mode - 1, // max. instances - BUFSIZE, // output buffer size - BUFSIZE, // input buffer size - 0, // client time-out - NULL); // default security attribute - - if (hPipe == INVALID_HANDLE_VALUE) - { - _tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError()); - return -1; - } - - // Wait for the client to connect; if it succeeds, - // the function returns a nonzero value. If the function - // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. - - fConnected = ConnectNamedPipe(hPipe, NULL) ? - TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - - if (fConnected) - { - printf("Client connected\n"); - - printf("Creating socket\n"); - - if ((sock_fd = l_socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - printf("Failed to create socket\n"); - return 1; - } - - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - - const char *const temp_path = get_temp_path(); - - char *paths[] = { - "%s/discord-ipc-%d", - "%s/app/com.discordapp.Discord/discord-ipc-%d", - "%s/snap.discord-canary/discord-ipc-%d", - "%s/snap.discord/discord-ipc-%d" - }; - - char connected = 0; - for (int p = 0; p < sizeof(paths) / sizeof(paths[0]); p++) { - for (int pipeNum = 0; pipeNum < 10; ++pipeNum) { - - snprintf(addr.sun_path, sizeof(addr.sun_path), paths[p], temp_path, pipeNum); - printf("Attempting to connect to %s\n", addr.sun_path); - - if (l_connect(sock_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { - printf("Failed to connect\n"); - } else { - connected = 1; - goto breakout; - } - } - } -breakout:; - - if (!connected) { - printf("Could not connect to discord client\n"); - return 1; - } - - - printf("Connected successfully\n"); - - hThread = CreateThread( - NULL, // no security attribute - 0, // default stack size - winwrite_thread, // thread proc - (LPVOID) NULL, // thread parameter - 0, // not suspended - &dwThreadId); // returns thread ID - - if (hThread == NULL) - { - _tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError()); - return 1; - } - - - for (;;) { - char buf[BUFSIZE]; - DWORD bytes_read = 0; - BOOL fSuccess = ReadFile( - hPipe, // handle to pipe - buf, // buffer to receive data - BUFSIZE, // size of buffer - &bytes_read, // number of bytes read - NULL); // not overlapped I/O - if (!fSuccess) { - if (GetLastError() == ERROR_BROKEN_PIPE) { - printf("winread EOF\n"); - return 0; - } else { - printf("Failed to read from pipe\n"); - return 1; - } - } - - printf("%d bytes w->l\n", bytes_read); - /* uncomment to dump the actual data being passed from the pipe to the socket */ - /* for(int i=0;iw\n", bytes_read); - /* uncomment to dump the actual data being passed from the socket to the pipe */ - /* for(int i=0;i +#include +#include +#include "syscalls.h" +#include "server.h" +#define PRIVATE static // private functions +#define INFO(FMTSTR, ...) \ + if(bStandalone) \ + _tprintf(TEXT(FMTSTR), ## __VA_ARGS__) + +static BOOL bStandalone = FALSE; +static HANDLE hPipe = INVALID_HANDLE_VALUE; +static int sock_fd; + +// Private Function definitions +PRIVATE DWORD WINAPI winwrite_thread(LPVOID lpvParam); +PRIVATE int iListenPipe(); + +int iServerMain(BOOL bStandaloneArg) +{ + LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0"); + bStandalone = bStandaloneArg; + int ret = 0; + + // The main loop creates an instance of the named pipe and + // then waits for a client to connect to it. When the client + // connects, a thread is created to handle communications + // with that client, and this loop is free to wait for the + // next client connect request. It is an infinite loop. + + INFO("Pipe Server: Main thread awaiting client connection on %s\n", lpszPipename); + hPipe = CreateNamedPipe( + lpszPipename, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_BYTE | // message type pipe + PIPE_READMODE_BYTE | // message-read mode + PIPE_WAIT, // blocking mode + 1, // max. instances + BUFSIZE, // output buffer size + BUFSIZE, // input buffer size + 0, // client time-out + NULL); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) + { + INFO("CreateNamedPipe failed, GLE=%d.\n", GetLastError()); + return -1; + } + + ret = iListenPipe(); + CloseHandle(hPipe); + hPipe = INVALID_HANDLE_VALUE; + + return ret; +} + +PRIVATE int iListenPipe() +{ + BOOL fConnected = FALSE; + DWORD dwThreadId = 0; + HANDLE hThread = NULL; + BOOL connected = FALSE; + + fConnected = ConnectNamedPipe(hPipe, NULL) ? + TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + // Wait for the client to connect; if it succeeds, + // the function returns a nonzero value. If the function + // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. + if (!fConnected) return 1; + + INFO("Client connected\n"); + INFO("Creating socket\n"); + + if ((sock_fd = l_socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + INFO("Failed to create socket\n"); + return 1; + } + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + + const char *const temp_path = get_temp_path(); + + char *paths[] = + { + "%s/discord-ipc-%d", + "%s/app/com.discordapp.Discord/discord-ipc-%d", + "%s/snap.discord-canary/discord-ipc-%d", + "%s/snap.discord/discord-ipc-%d" + }; + + for (int p = 0; p < sizeof(paths) / sizeof(paths[0]) && !connected; p++) + { + for (int pipeNum = 0; pipeNum < 10 && !connected; ++pipeNum) + { + + StringCchPrintf(addr.sun_path, sizeof(addr.sun_path), paths[p], temp_path, pipeNum); + INFO("Attempting to connect to %s\n", addr.sun_path); + + connected = !(l_connect(sock_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0); + if (!connected) + INFO("Failed to connect\n"); + } + } + + if (!connected) + { + INFO("Could not connect to discord client\n"); + return 1; + } + + INFO("Connected successfully\n"); + hThread = CreateThread( + NULL, // no security attribute + 0, // default stack size + winwrite_thread, // thread proc + (LPVOID) NULL, // thread parameter + 0, // not suspended + &dwThreadId); // returns thread ID + + if (hThread == NULL) + { + INFO("CreateThread failed, GLE=%d.\n", GetLastError()); + return 1; + } + + while(bStandalone || bSvcRunning()) + { + char buf[BUFSIZE]; + DWORD bytes_read = 0; + BOOL fSuccess = ReadFile( + hPipe, // handle to pipe + buf, // buffer to receive data + BUFSIZE, // size of buffer + &bytes_read, // number of bytes read + NULL); // not overlapped I/O + if (!fSuccess) + { + CloseHandle(hThread); + + if (GetLastError() == ERROR_BROKEN_PIPE) + { + INFO("winread EOF\n"); + return 0; + } + INFO("Failed to read from pipe\n"); + return 1; + } + + INFO("%d bytes w->l\n", bytes_read); + /* pass -D__WDBRIDGE_DUMP_PIPE to gcc + * to dump the actual data being passed + * from the pipe to the socket */ +#ifdef __WDBRIDGE_DUMP_PIPE + for(int i = 0; i < bytes_read; i++) + putchar(buf[i]); + INFO("\n"); +#endif + + int total_written = 0, written = 0; + + while (total_written < bytes_read) + { + written = l_write(sock_fd, buf + total_written, bytes_read - total_written); + if (written < 0) + { + INFO("Failed to write to socket\n"); + CloseHandle(hThread); + return 1; + } + total_written += written; + written = 0; + } + } + + CloseHandle(hThread); + return 0; +} + +PRIVATE DWORD WINAPI winwrite_thread(LPVOID lpvParam) +{ + + for (;;) + { + char buf[BUFSIZE]; + int bytes_read = l_read(sock_fd, buf, BUFSIZE); + if (bytes_read < 0) + { + INFO("Failed to read from socket\n"); + l_close(sock_fd); + return 1; + } + else if (bytes_read == 0) + { + INFO("EOF\n"); + break; + } + + INFO("%d bytes l->w\n", bytes_read); + /* uncomment to dump the actual data being passed from the socket to the pipe */ + /* for(int i=0;i +#include +#include +#include "server.h" +#include "service-manager.h" +#define ARGV1(X) (argc > 1 && lstrcmpi( argv[1], TEXT(X)) == 0) +#define basename PathFindFileName(argv[0]) + +int _tmain(int argc, TCHAR *argv[]) +{ + if(ARGV1("install")) return iAddService(); + else if(ARGV1("remove")) return iDelService(); + else if(ARGV1("service")) return iRunService(); + else if(argc > 1) + return (_tprintf( + TEXT("Run Standalone : %s\n" + "Install Service: %s install\n" + "Remove Service : %s remove\n"), + basename, basename, basename) * 0); + return iServerMain(TRUE); +} diff --git a/src/server.h b/src/server.h new file mode 100644 index 0000000..60b6b6c --- /dev/null +++ b/src/server.h @@ -0,0 +1,9 @@ +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include + +int iServerMain(BOOL); +BOOL bSvcRunning(VOID); + +#endif /* __SERVER_H__ */ diff --git a/src/service-manager.c b/src/service-manager.c new file mode 100644 index 0000000..1abbed8 --- /dev/null +++ b/src/service-manager.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include "server.h" +#include "service-manager.h" +#include "service.h" +#include "error.h" + +int iAddService() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + TCHAR szUnquotedPath[MAX_PATH]; + if( !GetModuleFileName( NULL, szUnquotedPath, MAX_PATH ) ) { + _tprintf(TEXT("Cannot install service (%d)\n"), GetLastError()); + return 1; + } + + TCHAR szPath[MAX_PATH]; + StringCbPrintf(szPath, MAX_PATH, TEXT("\"%s\" service"), szUnquotedPath); + + // Open SCM database handle. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + if (NULL == schSCManager) { + _tprintf(TEXT("OpenSCManager failed (%d)\n"), GetLastError()); + return 1; + } + + // Register service + schService = CreateService( + schSCManager, // SCM database + SVCNAME, // name of service + SVCNAME, // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_IGNORE, // error control type + szPath, // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService == NULL) { + _tprintf(TEXT("CreateService failed (%d)\n"), GetLastError()); + CloseServiceHandle(schSCManager); + return 1; + } else _tprintf(TEXT("Service installed successfully\n")); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return 0; +} + +int iDelService(void) +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + SERVICE_STATUS ssStatus; + + // Open SCM database handle. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + if (NULL == schSCManager) { + _tprintf(TEXT("OpenSCManager failed (%d)\n"), GetLastError()); + return 1; + } + + // Open service handle. + schService = OpenService( + schSCManager, // SCM database + SVCNAME, // name of service + DELETE); // need delete access + if (schService == NULL) { + _tprintf(TEXT("OpenService failed (%d)\n"), GetLastError()); + CloseServiceHandle(schSCManager); + return 1; + } + + // Mark service as deleted + if (! DeleteService(schService) ) { + _tprintf(TEXT("DeleteService failed (%d)\n"), GetLastError()); + } else _tprintf(TEXT("Service deleted successfully\n")); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return 0; +} + +int iRunService() +{ + SERVICE_TABLE_ENTRY DispatchTable[] = { + { SVCNAME, (LPSERVICE_MAIN_FUNCTION) vSvcMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher( DispatchTable )) return 0; + + vReportError(TEXT("StartServiceCtrlDispatcher")); + return 1; +} + +VOID vReportError(LPTSTR szFunction) +{ + HANDLE hEvSrc; + LPCTSTR lpszStrs[2]; + TCHAR tcBuf[80]; + + hEvSrc = RegisterEventSource(NULL, SVCNAME); + + if(hEvSrc) { + StringCchPrintf(tcBuf, 80, TEXT("%s failed with %d"), szFunction, GetLastError()); + lpszStrs[0] = SVCNAME; + lpszStrs[1] = tcBuf; + + ReportEvent(hEvSrc, // event log handle + EVENTLOG_ERROR_TYPE, // event type + 0, // event category + SVC_ERROR, // event identifier + NULL, // no security identifier + 2, // size of lpszStrings array + 0, // no binary data + lpszStrs, // array of strings + NULL); + } +} diff --git a/src/service-manager.h b/src/service-manager.h new file mode 100644 index 0000000..4cf8513 --- /dev/null +++ b/src/service-manager.h @@ -0,0 +1,8 @@ +#ifndef __SERVICE_MANAGER_H__ +#define __SERVICE_MANAGER_H__ + +int iAddService(void); +int iDelService(void); +int iRunService(void); + +#endif /* __SERVICE_MANAGER_H__ */ diff --git a/src/service.c b/src/service.c new file mode 100644 index 0000000..63b85f6 --- /dev/null +++ b/src/service.c @@ -0,0 +1,93 @@ +#include +#include + +#include "service.h" +#include "server.h" +#define PRIVATE static // private functions + +PRIVATE SERVICE_STATUS gSvcStatus; +PRIVATE SERVICE_STATUS_HANDLE gSvcStatusHandle; +PRIVATE HANDLE ghSvcStopEvent = NULL; + +PRIVATE VOID vReportState(DWORD, DWORD, DWORD); +PRIVATE VOID WINAPI vHandleEvent(DWORD); + +// decls +BOOL bSvcRunning() +{ + return ( + ghSvcStopEvent == NULL + || WaitForSingleObjectEx(ghSvcStopEvent, 0, TRUE) == WAIT_TIMEOUT + ); +} + +VOID WINAPI vSvcMain(DWORD dw, LPTSTR * str) +{ + // Get Service Status Handle + gSvcStatusHandle = RegisterServiceCtrlHandler( + SVCNAME, + vHandleEvent); + + if( !gSvcStatusHandle ) + return vReportError(TEXT("RegisterServiceCtrlHandler")); + + // These SERVICE_STATUS members remain as set here + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + vReportState(SERVICE_START_PENDING, NO_ERROR, 3000); + + ghSvcStopEvent = CreateEvent( + NULL, // default security attributes + TRUE, // manual reset event + FALSE, // not signaled + NULL); // no name + + if ( ghSvcStopEvent == NULL) + { + vReportState( SERVICE_STOPPED, GetLastError(), 0 ); + return; + } + + vReportState( SERVICE_RUNNING, NO_ERROR, 0 ); + while(bSvcRunning()) + { + iServerMain(FALSE); + } + + vReportState( SERVICE_STOPPED, NO_ERROR, 0 ); +} + +PRIVATE VOID vReportState(DWORD dwCState, DWORD dwW32ECode, DWORD dwWHint) +{ + static DWORD dwCheckPoint = 1; + gSvcStatus.dwCurrentState = dwCState; + gSvcStatus.dwWin32ExitCode = dwW32ECode; + gSvcStatus.dwWaitHint = dwWHint; + + if (dwCState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else + gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ( (dwCState == SERVICE_RUNNING) || + (dwCState == SERVICE_STOPPED) ) + gSvcStatus.dwCheckPoint = 0; + else gSvcStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the SCM. + SetServiceStatus( gSvcStatusHandle, &gSvcStatus ); +} + +PRIVATE VOID WINAPI vHandleEvent(DWORD dwCtrl) +{ + switch(dwCtrl) + { + case SERVICE_CONTROL_STOP: + vReportState(SERVICE_STOP_PENDING, NO_ERROR, 0); + SetEvent(ghSvcStopEvent); + vReportState(gSvcStatus.dwCurrentState, NO_ERROR, 0); + return; + default: + break; + } +} diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..7bf7131 --- /dev/null +++ b/src/service.h @@ -0,0 +1,12 @@ +#ifndef __SERVICE_H__ +#define __SERVICE_H__ +#include + +#define SVCNAME TEXT("DiscordIPCBridge") + +VOID WINAPI + vSvcMain(DWORD, LPTSTR*); // Service Entry Point +VOID + vReportError(LPTSTR); // Service Error Reporter + +#endif /* __SERVICE_H__ */ diff --git a/src/syscalls.c b/src/syscalls.c new file mode 100644 index 0000000..0f07ad8 --- /dev/null +++ b/src/syscalls.c @@ -0,0 +1,146 @@ +#include +#include "syscalls.h" + +// syscall wrappers +SFUNC int l_close(int fd) +{ + __asm__ ( + "push ebx\n\t" + "mov eax, 0x06\n\t" + "mov ebx, [esp + 4 + 4]\n\t" + "int 0x80\n\t" + "pop ebx\n\t" + "ret" + ); +} + +SFUNC int l_socketcall(int call, void* args) +{ + __asm__ ( + "push ebx\n\t" + "mov eax, 0x66\n\t" + "mov ebx, [esp + 4 + 4]\n\t" + "mov ecx, [esp + 4 + 8]\n\t" + "int 0x80\n\t" + "pop ebx\n\t" + "ret" + ); +} + +SFUNC int l_open(const char* filename, int flags, int mode) +{ + __asm__ ( + "push ebx\n\t" + "mov eax, 0x05\n\t" + "mov ebx, [esp + 4 + 4]\n\t" + "mov ecx, [esp + 4 + 8]\n\t" + "mov edx, [esp + 4 + 12]\n\t" + "int 0x80\n\t" + "pop ebx\n\t" + "ret" + ); +} + +SFUNC int l_write(unsigned int fd, const char* buf, unsigned int count) +{ + __asm__ ( + "push ebx\n\t" + "mov eax, 0x04\n\t" + "mov ebx, [esp + 4 + 4]\n\t" + "mov ecx, [esp + 4 + 8]\n\t" + "mov edx, [esp + 4 + 12]\n\t" + "int 0x80\n\t" + "pop ebx\n\t" + "ret" + ); +} + +SFUNC int l_read(unsigned int fd, char* buf, unsigned int count) +{ + __asm__ ( + "push ebx\n\t" + "mov eax, 0x03\n\t" + "mov ebx, [esp + 4 + 4]\n\t" + "mov ecx, [esp + 4 + 8]\n\t" + "mov edx, [esp + 4 + 12]\n\t" + "int 0x80\n\t" + "pop ebx\n\t" + "ret" + ); +} + +// socket wrappers +CFUNC int l_socket(int domain, int type, int protocol) +{ + void* args[3]; + args[0] = (void*)(int*)domain; + args[1] = (void*)(int*)type; + args[2] = (void*)(int*)protocol; + return l_socketcall(1, args); +} + +CFUNC int l_connect(int sockfd, const struct sockaddr *addr, unsigned int addrlen) +{ + void* args[3]; + args[0] = (void*)(int*)sockfd; + args[1] = (void*)addr; + args[2] = (void*)(int*)addrlen; + return l_socketcall(3, args); +} +/* int send(int sockfd, const void* buf, unsigned int len, int flags) { */ +/* void* args[4]; */ +/* args[0] = (void*)(int*)sockfd; */ +/* args[1] = (void*)buf; */ +/* args[2] = (void*)(unsigned int*)len; */ +/* args[3] = (void*)(int*)flags; */ +/* return l_socketcall(9, args); */ +/* } */ +/* int recv(int fd, void* buf, unsigned int len, int flags) { */ +/* void* args[4]; */ +/* args[0] = (void*)(int*)fd; */ +/* args[1] = (void*)buf; */ +/* args[2] = (void*)(unsigned int*)len; */ +/* args[3] = (void*)(int*)flags; */ +/* return l_socketcall(10, args); */ +/* } */ + +// env wrappers +CFUNC char* getenv_(char* name) // written by https://github.com/Francesco149 +{ + static char buf[1024 * 1024]; + static char* end = 0; + unsigned int namelen; + char* p; + if (!end) { + int fd, n; + fd = l_open("/proc/self/environ", 0, 0); + if (fd < 0) { + return 0; + } + n = l_read((unsigned int)fd, buf, (unsigned int)sizeof(buf)); + if (n < 0) { + return 0; + } + l_close(fd); + end = buf + n; + } + namelen = strlen(name); + for (p = buf; p < end;) { + if (!strncmp(p, name, namelen)) { + return p + namelen + 1; /* skip name and the = */ + } + for (; *p && p < end; ++p); /* skip to next entry */ + ++p; + } + return 0; +} + +CFUNC const char* get_temp_path() +{ + const char* temp = getenv_("XDG_RUNTIME_DIR"); + temp = temp ? temp : getenv_("TMPDIR"); + temp = temp ? temp : getenv_("TMP"); + temp = temp ? temp : getenv_("TEMP"); + temp = temp ? temp : "/tmp"; + return temp; +} diff --git a/src/syscalls.h b/src/syscalls.h new file mode 100644 index 0000000..53aacd8 --- /dev/null +++ b/src/syscalls.h @@ -0,0 +1,34 @@ +#ifndef __SYSCALLS_H__ +#define __SYSCALLS_H__ + +#include + +typedef struct sockaddr_un { + unsigned short sun_family; /* AF_UNIX */ + char sun_path[108]; /* pathname */ +} sockaddr_un; + +#define AF_UNIX 1 +#define SOCK_STREAM 1 +#define F_SETFL 4 +#define O_RDONLY 00000000 +#define O_WRONLY 00000001 +#define O_CREAT 00000100 +#define O_APPEND 00002000 +#define O_NONBLOCK 00004000 +#define BUFSIZE 2048 // size of read/write buffers +#define SFUNC __declspec(naked) // inline asm function +#define CFUNC // C function + +// linux syscall wrappers +SFUNC int l_close(int); +SFUNC int l_socketcall(int, void*); +SFUNC int l_open(const char*, int, int); +SFUNC int l_write(unsigned int, const char*, unsigned int); +SFUNC int l_read(unsigned int, char*, unsigned int); +CFUNC int l_socket(int, int, int); +CFUNC int l_connect(int, const struct sockaddr*, unsigned int); +CFUNC char* getenv_(char*); +CFUNC const char* get_temp_path(); + +#endif