Merge pull request #330 from CarterLi/dev

Networking: support nowait connection
This commit is contained in:
Linus Dierheimer
2022-11-04 11:51:55 +01:00
committed by GitHub
9 changed files with 291 additions and 151 deletions
+2 -1
View File
@@ -220,7 +220,6 @@ set(LIBFASTFETCH_SRC
src/common/parsing.c
src/common/settings.c
src/common/library.c
src/common/networking.c
src/common/bar.c
src/logo/logo.c
src/logo/builtin.c
@@ -293,6 +292,7 @@ set(LIBFASTFETCH_SRC
if(LINUX OR APPLE OR ANDROID OR BSD)
list(APPEND LIBFASTFETCH_SRC
src/common/processing_linux.c
src/common/networking_linux.c
src/detection/users/users_linux.c
src/detection/terminalshell/terminalshell_linux.c
src/detection/localip/localip_linux.c
@@ -360,6 +360,7 @@ endif()
if(WIN32)
list(APPEND LIBFASTFETCH_SRC
src/common/processing_windows.c
src/common/networking_windows.c
src/detection/host/host_windows.cpp
src/detection/bios/bios_windows.cpp
src/detection/board/board_windows.cpp
+5 -5
View File
@@ -305,13 +305,13 @@ FF_THREAD_ENTRY_DECL_WRAPPER(ffDetectGTK4, FFinstance*)
void startDetectionThreads(FFinstance* instance)
{
#ifdef FF_HAVE_THREADS
ffThreadCreateAndDetach(ffConnectDisplayServerThreadMain, instance);
ffThreadDetach(ffThreadCreate(ffConnectDisplayServerThreadMain, instance));
#ifdef FF_DETECT_QT_GTK
ffThreadCreateAndDetach(ffDetectQtThreadMain, instance);
ffThreadCreateAndDetach(ffDetectGTK2ThreadMain, instance);
ffThreadCreateAndDetach(ffDetectGTK3ThreadMain, instance);
ffThreadCreateAndDetach(ffDetectGTK4ThreadMain, instance);
ffThreadDetach(ffThreadCreate(ffDetectQtThreadMain, instance));
ffThreadDetach(ffThreadCreate(ffDetectGTK2ThreadMain, instance));
ffThreadDetach(ffThreadCreate(ffDetectGTK3ThreadMain, instance));
ffThreadDetach(ffThreadCreate(ffDetectGTK4ThreadMain, instance));
#endif
#else
-115
View File
@@ -1,115 +0,0 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <synchapi.h>
static BOOL WINAPI initWsaData(PINIT_ONCE once, PVOID param, PVOID* context)
{
(void)once;
(void)param;
static WSADATA wsaData;
*context = &wsaData;
return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0;
}
//Types of winsock2 are full of mess. Disable warnings for them and keep clean for posix
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#else
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
#define closesocket close
#endif
//Must be included after <winsock2.h>
#include "fastfetch.h"
#include "common/networking.h"
FFSockType ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout)
{
#ifdef _WIN32
static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
WSADATA* pData;
if(!InitOnceExecuteOnce(&once, initWsaData, NULL, (LPVOID*) &pData))
return INVALID_SOCKET;
#endif
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo* addr;
if(getaddrinfo(host, "80", &hints, &addr) != 0)
return INVALID_SOCKET;
FFSockType sockfd = (FFSockType)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(sockfd == INVALID_SOCKET)
{
freeaddrinfo(addr);
return INVALID_SOCKET;
}
if(timeout > 0)
{
struct timeval timev;
timev.tv_sec = 0;
timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
}
if(connect(sockfd, addr->ai_addr, addr->ai_addrlen) == -1)
{
closesocket(sockfd);
freeaddrinfo(addr);
return INVALID_SOCKET;
}
freeaddrinfo(addr);
FFstrbuf command;
ffStrbufInitA(&command, 64);
ffStrbufAppendS(&command, "GET ");
ffStrbufAppendS(&command, path);
ffStrbufAppendS(&command, " HTTP/1.1\nHost: ");
ffStrbufAppendS(&command, host);
ffStrbufAppendS(&command, "\r\n");
ffStrbufAppendS(&command, headers);
ffStrbufAppendS(&command, "\r\n");
if(send(sockfd, command.chars, command.length, 0) == -1)
{
ffStrbufDestroy(&command);
closesocket(sockfd);
return INVALID_SOCKET;
}
ffStrbufDestroy(&command);
return sockfd;
}
bool ffNetworkingRecvHttpResponse(FFSockType sockfd, FFstrbuf* buffer)
{
ssize_t received = recv(sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
if(received > 0)
{
buffer->length += (uint32_t) received;
buffer->chars[buffer->length] = '\0';
}
closesocket(sockfd);
return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
}
bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer)
{
FFSockType sockfd = ffNetworkingSendHttpRequest(host, path, headers, timeout);
if(sockfd != INVALID_SOCKET)
return ffNetworkingRecvHttpResponse(sockfd, buffer);
return false;
}
+27 -10
View File
@@ -3,20 +3,37 @@
#ifndef FF_INCLUDED_common_networking
#define FF_INCLUDED_common_networking
#include "common/thread.h"
#include "util/FFstrbuf.h"
#ifdef _WIN32
typedef uintptr_t FFSockType; //SOCKET, unsigned
#ifndef INVALID_SOCKET //Don't conflict with <winsock2.h>
#define INVALID_SOCKET ((uintptr_t)~0)
#include <minwindef.h>
#endif
typedef struct FFNetworkingState {
#ifdef _WIN32
uintptr_t sockfd;
OVERLAPPED overlapped;
#else
int sockfd;
FFstrbuf host;
FFstrbuf command;
#ifdef FF_HAVE_THREADS
FFThreadType thread;
#endif
#endif
#else
typedef int FFSockType; // signed
#define INVALID_SOCKET (-1)
#endif
} FFNetworkingState;
FFSockType ffNetworkingSendHttpRequest(const char* host, const char* path, const char* headers, uint32_t timeout);
bool ffNetworkingRecvHttpResponse(FFSockType sockfd, FFstrbuf* buffer);
bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer);
bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers);
bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout);
static inline bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer)
{
FFNetworkingState state;
if(ffNetworkingSendHttpRequest(&state, host, path, headers))
return ffNetworkingRecvHttpResponse(&state, buffer, timeout);
return false;
}
#endif
+103
View File
@@ -0,0 +1,103 @@
#include "fastfetch.h"
#include "common/networking.h"
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
static void connectAndSend(FFNetworkingState* state)
{
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo* addr;
if(getaddrinfo(state->host.chars, "80", &hints, &addr) != 0)
goto error;
state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(state->sockfd == -1)
{
freeaddrinfo(addr);
goto error;
}
if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1)
{
close(state->sockfd);
freeaddrinfo(addr);
goto error;
}
freeaddrinfo(addr);
if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0)
{
close(state->sockfd);
goto error;
}
goto exit;
error:
state->sockfd = -1;
exit:
ffStrbufDestroy(&state->host);
ffStrbufDestroy(&state->command);
}
FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*);
bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
{
ffStrbufInitS(&state->host, host);
ffStrbufInitA(&state->command, 64);
ffStrbufAppendS(&state->command, "GET ");
ffStrbufAppendS(&state->command, path);
ffStrbufAppendS(&state->command, " HTTP/1.1\nHost: ");
ffStrbufAppendS(&state->command, host);
ffStrbufAppendS(&state->command, "\r\n");
ffStrbufAppendS(&state->command, headers);
ffStrbufAppendS(&state->command, "\r\n");
#ifdef FF_HAVE_THREADS
state->thread = ffThreadCreate(connectAndSendThreadMain, state);
return state->thread != NULL;
#else
connectAndSend(state);
return state->sockfd != -1;
#endif
}
bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout)
{
#ifdef FF_HAVE_THREADS
ffThreadJoin(state->thread);
#endif
if(state->sockfd == -1)
return false;
if(timeout > 0)
{
struct timeval timev;
timev.tv_sec = 0;
timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
}
ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
if(received > 0)
{
buffer->length += (uint32_t) received;
buffer->chars[buffer->length] = '\0';
}
close(state->sockfd);
return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
}
+127
View File
@@ -0,0 +1,127 @@
#include <mswsock.h>
#include <ws2tcpip.h>
//Must be included after <mswsock.h>
#include "fastfetch.h"
#include "common/networking.h"
static LPFN_CONNECTEX ConnectEx;
static BOOL WINAPI initWsaData(PINIT_ONCE once, PVOID param, PVOID* context)
{
(void)once;
(void)param;
static WSADATA wsaData;
*context = &wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return FALSE;
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
return FALSE;
//Dummy socket needed for WSAIoctl
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == INVALID_SOCKET)
return FALSE;
DWORD dwBytes;
GUID guid = WSAID_CONNECTEX;
if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&ConnectEx, sizeof(ConnectEx),
&dwBytes, NULL, NULL) != 0)
return FALSE;
return closesocket(sockfd) == 0;
}
bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
{
static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
WSADATA* pData;
if(!InitOnceExecuteOnce(&once, initWsaData, NULL, (LPVOID*) &pData))
return false;
memset(state, 0, sizeof(*state));
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo* addr;
if(getaddrinfo(host, "80", &hints, &addr) != 0)
return false;
state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(state->sockfd == INVALID_SOCKET)
{
freeaddrinfo(addr);
return false;
}
{
//ConnectEx requires the socket to be initially bound
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = INADDR_ANY,
.sin_port = 0,
};
if(bind(state->sockfd, (SOCKADDR *)&addr, sizeof(addr)) != 0)
{
printf("bind %d\n", WSAGetLastError());
return false;
}
}
FFstrbuf command;
ffStrbufInitA(&command, 64);
ffStrbufAppendS(&command, "GET ");
ffStrbufAppendS(&command, path);
ffStrbufAppendS(&command, " HTTP/1.1\nHost: ");
ffStrbufAppendS(&command, host);
ffStrbufAppendS(&command, "\r\n");
ffStrbufAppendS(&command, headers);
ffStrbufAppendS(&command, "\r\n");
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, command.chars, command.length, NULL, &state->overlapped);
freeaddrinfo(addr);
ffStrbufDestroy(&command);
if(!result && WSAGetLastError() != WSA_IO_PENDING)
{
closesocket(state->sockfd);
return false;
}
ffStrbufDestroy(&command);
return true;
}
bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout)
{
DWORD transfer, flags;
if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags))
{
closesocket(state->sockfd);
return false;
}
if(timeout > 0)
{
//https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-setsockopt
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
}
ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0);
if(received > 0)
{
buffer->length += (uint32_t) received;
buffer->chars[buffer->length] = '\0';
}
closesocket(state->sockfd);
return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n");
}
+14 -9
View File
@@ -9,35 +9,40 @@
#if defined(_WIN32)
#include <handleapi.h>
#include <synchapi.h>
#include <process.h> // Win32 <process.h> isn't available on MSYS2
#include <process.h>
#define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT
typedef SRWLOCK FFThreadMutex;
typedef HANDLE FFThreadType;
static inline void ffThreadMutexLock(FFThreadMutex* mutex) { AcquireSRWLockExclusive(mutex); }
static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { ReleaseSRWLockExclusive(mutex); }
static inline void ffThreadCreateAndDetach(unsigned (__stdcall* func)(void*), void* data) {
uintptr_t newThread = _beginthreadex(NULL, 0, func, data, 0, NULL);
if(newThread)
CloseHandle((HANDLE)newThread);
static inline FFThreadType ffThreadCreate(unsigned (__stdcall* func)(void*), void* data) {
return (FFThreadType)_beginthreadex(NULL, 0, func, data, 0, NULL);
}
#define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; }
static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); }
static inline void ffThreadJoin(FFThreadType thread) { WaitForSingleObject(thread, INFINITE); }
#else
#include <pthread.h>
#define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
typedef pthread_mutex_t FFThreadMutex;
typedef pthread_t FFThreadType;
static inline void ffThreadMutexLock(FFThreadMutex* mutex) { pthread_mutex_lock(mutex); }
static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { pthread_mutex_unlock(mutex); }
static inline void ffThreadCreateAndDetach(void* (* func)(void*), void* data) {
pthread_t newThread;
if(pthread_create(&newThread, NULL, func, data) == 0)
pthread_detach(newThread);
static inline FFThreadType ffThreadCreate(void* (* func)(void*), void* data) {
FFThreadType newThread = NULL;
pthread_create(&newThread, NULL, func, data);
return newThread;
}
#define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static void* fn ## ThreadMain (void* data) { fn((paramType)data); return NULL; }
static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); }
static inline void ffThreadJoin(FFThreadType thread) { pthread_join(thread, NULL); }
#endif
#else //FF_HAVE_THREADS
#define FF_THREAD_MUTEX_INITIALIZER 0
typedef char FFThreadMutex;
static inline void ffThreadMutexLock(FFThreadMutex* mutex) { FF_UNUSED(mutex) }
static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) { FF_UNUSED(mutex) }
#define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType)
#endif //FF_HAVE_THREADS
#endif
+7 -6
View File
@@ -5,12 +5,13 @@
#define FF_PUBLICIP_MODULE_NAME "Public IP"
#define FF_PUBLICIP_NUM_FORMAT_ARGS 1
static FFSockType sockfd;
static FFNetworkingState state;
static int status = -1;
void ffPreparePublicIp(FFinstance* instance)
{
if(instance->config.publicIpUrl.length == 0)
sockfd = ffNetworkingSendHttpRequest("ipinfo.io", "/ip", NULL, instance->config.publicIpTimeout);
status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/ip", NULL);
else
{
FFstrbuf host;
@@ -27,7 +28,7 @@ void ffPreparePublicIp(FFinstance* instance)
host.chars[pathStartIndex] = '\0';
}
sockfd = ffNetworkingSendHttpRequest(host.chars, path.length == 0 ? "/" : path.chars, NULL, instance->config.publicIpTimeout);
status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL);
ffStrbufDestroy(&path);
ffStrbufDestroy(&host);
@@ -36,10 +37,10 @@ void ffPreparePublicIp(FFinstance* instance)
void ffPrintPublicIp(FFinstance* instance)
{
if(sockfd == 0)
if(status == -1)
ffPreparePublicIp(instance);
if(sockfd == INVALID_SOCKET)
if(status == 0)
{
ffPrintError(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIP, "Failed to connect to an IP detection server");
return;
@@ -47,7 +48,7 @@ void ffPrintPublicIp(FFinstance* instance)
FFstrbuf result;
ffStrbufInitA(&result, 4096);
bool success = ffNetworkingRecvHttpResponse(sockfd, &result);
bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.publicIpTimeout);
if(success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n");
if(!success || result.length == 0)
+6 -5
View File
@@ -5,23 +5,24 @@
#define FF_WEATHER_MODULE_NAME "Weather"
#define FF_WEATHER_NUM_FORMAT_ARGS 1
static FFSockType sockfd;
static FFNetworkingState state;
static int status = -1;
void ffPrepareWeather(FFinstance* instance)
{
FFstrbuf path;
ffStrbufInitS(&path, "/?format=");
ffStrbufAppend(&path, &instance->config.weatherOutputFormat);
sockfd = ffNetworkingSendHttpRequest("wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n", instance->config.weatherTimeout);
status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n");
ffStrbufDestroy(&path);
}
void ffPrintWeather(FFinstance* instance)
{
if(sockfd == 0)
if(status == -1)
ffPrepareWeather(instance);
if(sockfd == INVALID_SOCKET)
if(status == 0)
{
ffPrintError(instance, FF_WEATHER_MODULE_NAME, 0, &instance->config.weather, "Failed to connect to 'wttr.in'");
return;
@@ -29,7 +30,7 @@ void ffPrintWeather(FFinstance* instance)
FFstrbuf result;
ffStrbufInitA(&result, 4096);
bool success = ffNetworkingRecvHttpResponse(sockfd, &result);
bool success = ffNetworkingRecvHttpResponse(&state, &result, instance->config.weatherTimeout);
if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n");
if(!success || result.length == 0)