Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 112 +++++++++++++++++++++++++++++++++++++++++-- include/wine/http.h | 35 ++++++++++++++ 2 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 include/wine/http.h
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 3a8aa3953d..e57cce9e3c 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#include <stdarg.h> - #include "ntstatus.h" #define WIN32_NO_STATUS +#include "wine/http.h" #include "winternl.h" -#include "winioctl.h" #include "ddk/wdm.h" #include "wine/debug.h" #include "wine/heap.h" @@ -46,10 +44,116 @@ DECLARE_CRITICAL_SECTION(http_cs); struct request_queue { struct list entry; + HTTP_URL_CONTEXT context; + char *url; };
static struct list request_queues = LIST_INIT(request_queues);
+ +static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) +{ + const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer; + unsigned short port; + char *url, *endptr; + int count = 0; + const char *p; + + TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context)); + + if (!strncmp(params->url, "https://", 8)) + { + FIXME("HTTPS is not implemented.\n"); + return STATUS_NOT_IMPLEMENTED; + } + else if (strncmp(params->url, "http://", 7) || !strchr(params->url + 7, ':') + || params->url[strlen(params->url) - 1] != '/') + return STATUS_INVALID_PARAMETER; + if (!(port = strtol(strchr(params->url + 7, ':') + 1, &endptr, 10)) || *endptr != '/') + return STATUS_INVALID_PARAMETER; + + if (!(url = heap_alloc(strlen(params->url)))) + return STATUS_NO_MEMORY; + strcpy(url, params->url); + + for (p = url; *p; ++p) + if (*p == '/') ++count; + if (count > 3) + FIXME("Binding to relative URIs is not implemented; binding to all URIs instead.\n"); + + EnterCriticalSection(&http_cs); + + if (queue->url && !strcmp(queue->url, url)) + { + LeaveCriticalSection(&http_cs); + heap_free(url); + return STATUS_OBJECT_NAME_COLLISION; + } + else if (queue->url) + { + FIXME("Binding to multiple URLs is not implemented.\n"); + LeaveCriticalSection(&http_cs); + heap_free(url); + return STATUS_NOT_IMPLEMENTED; + } + + queue->url = url; + queue->context = params->context; + + LeaveCriticalSection(&http_cs); + + return STATUS_SUCCESS; +} + +static NTSTATUS http_remove_url(struct request_queue *queue, IRP *irp) +{ + const char *url = irp->AssociatedIrp.SystemBuffer; + + TRACE("host %s.\n", debugstr_a(url)); + + EnterCriticalSection(&http_cs); + + if (!queue->url || strcmp(url, queue->url)) + { + LeaveCriticalSection(&http_cs); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + heap_free(queue->url); + queue->url = NULL; + + LeaveCriticalSection(&http_cs); + return STATUS_SUCCESS; +} + +static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + struct request_queue *queue = stack->FileObject->FsContext; + NTSTATUS ret; + + switch (stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_HTTP_ADD_URL: + ret = http_add_url(queue, irp); + break; + case IOCTL_HTTP_REMOVE_URL: + ret = http_remove_url(queue, irp); + break; + default: + FIXME("Unhandled ioctl %#x.\n", stack->Parameters.DeviceIoControl.IoControlCode); + ret = STATUS_NOT_IMPLEMENTED; + } + + if (ret != STATUS_PENDING) + { + irp->IoStatus.Status = ret; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } + else + IoMarkIrpPending(irp); + return ret; +} + static NTSTATUS WINAPI dispatch_create(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -76,6 +180,7 @@ static void close_queue(struct request_queue *queue) list_remove(&queue->entry); LeaveCriticalSection(&http_cs);
+ heap_free(queue->url); heap_free(queue); }
@@ -130,6 +235,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path)
driver->MajorFunction[IRP_MJ_CREATE] = dispatch_create; driver->MajorFunction[IRP_MJ_CLOSE] = dispatch_close; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_ioctl; driver->DriverUnload = unload;
return STATUS_SUCCESS; diff --git a/include/wine/http.h b/include/wine/http.h new file mode 100644 index 0000000000..354c289387 --- /dev/null +++ b/include/wine/http.h @@ -0,0 +1,35 @@ +/* + * Copyright 2019 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINE_HTTP_H +#define __WINE_WINE_HTTP_H + +#include <windef.h> +#include <http.h> +#include <winioctl.h> + +#define IOCTL_HTTP_ADD_URL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, 0) +#define IOCTL_HTTP_REMOVE_URL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, 0) + +struct http_add_url_params +{ + HTTP_URL_CONTEXT context; + char url[1]; +}; + +#endif
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/httpapi/httpapi_main.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/dlls/httpapi/httpapi_main.c b/dlls/httpapi/httpapi_main.c index 4536aa8446..cc52273967 100644 --- a/dlls/httpapi/httpapi_main.c +++ b/dlls/httpapi/httpapi_main.c @@ -18,13 +18,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#include <stdarg.h> - -#include "windef.h" -#include "winbase.h" +#include "wine/http.h" #include "winternl.h" -#include "http.h" #include "wine/debug.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(httpapi);
@@ -186,13 +183,36 @@ ULONG WINAPI HttpCreateHttpHandle(HANDLE *handle, ULONG reserved) FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0)); }
+static ULONG add_url(HANDLE queue, const WCHAR *urlW, HTTP_URL_CONTEXT context) +{ + struct http_add_url_params *params; + ULONG ret = ERROR_SUCCESS; + OVERLAPPED ovl; + int len; + + len = WideCharToMultiByte(CP_ACP, 0, urlW, -1, NULL, 0, NULL, NULL); + if (!(params = heap_alloc(offsetof(struct http_add_url_params, url[len])))) + return ERROR_OUTOFMEMORY; + WideCharToMultiByte(CP_ACP, 0, urlW, -1, params->url, len, NULL, NULL); + params->context = context; + + ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1); + + if (!DeviceIoControl(queue, IOCTL_HTTP_ADD_URL, params, + offsetof(struct http_add_url_params, url[len]), NULL, 0, NULL, &ovl)) + ret = GetLastError(); + CloseHandle(ovl.hEvent); + return ret; +} + /*********************************************************************** * HttpAddUrl (HTTPAPI.@) */ -ULONG WINAPI HttpAddUrl( HANDLE handle, PCWSTR url, PVOID reserved ) +ULONG WINAPI HttpAddUrl(HANDLE queue, const WCHAR *url, void *reserved) { - FIXME( "(%p, %s, %p): stub!\n", handle, debugstr_w(url), reserved ); - return ERROR_CALL_NOT_IMPLEMENTED; + TRACE("queue %p, url %s, reserved %p.\n", queue, debugstr_w(url), reserved); + + return add_url(queue, url, 0); }
/***********************************************************************
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/httpapi/httpapi_main.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/dlls/httpapi/httpapi_main.c b/dlls/httpapi/httpapi_main.c index cc52273967..5f2a307934 100644 --- a/dlls/httpapi/httpapi_main.c +++ b/dlls/httpapi/httpapi_main.c @@ -218,10 +218,30 @@ ULONG WINAPI HttpAddUrl(HANDLE queue, const WCHAR *url, void *reserved) /*********************************************************************** * HttpRemoveUrl (HTTPAPI.@) */ -ULONG WINAPI HttpRemoveUrl(HANDLE queue, const WCHAR *url) +ULONG WINAPI HttpRemoveUrl(HANDLE queue, const WCHAR *urlW) { - FIXME("queue %p, url %s, stub!\n", queue, debugstr_w(url)); - return ERROR_CALL_NOT_IMPLEMENTED; + ULONG ret = ERROR_SUCCESS; + OVERLAPPED ovl = {}; + char *url; + int len; + + TRACE("queue %p, url %s.\n", queue, debugstr_w(urlW)); + + if (!queue) + return ERROR_INVALID_PARAMETER; + + len = WideCharToMultiByte(CP_ACP, 0, urlW, -1, NULL, 0, NULL, NULL); + if (!(url = heap_alloc(len))) + return ERROR_OUTOFMEMORY; + WideCharToMultiByte(CP_ACP, 0, urlW, -1, url, len, NULL, NULL); + + ovl.hEvent = (HANDLE)((ULONG_PTR)CreateEventW(NULL, TRUE, FALSE, NULL) | 1); + + if (!DeviceIoControl(queue, IOCTL_HTTP_REMOVE_URL, url, len, NULL, 0, NULL, &ovl)) + ret = GetLastError(); + CloseHandle(ovl.hEvent); + heap_free(url); + return ret; }
/***********************************************************************
Ultimately we'll want to move the listening socket list out of the request_queue structure, since multiple request queues can share the same address.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/Makefile.in | 2 +- dlls/http.sys/http.c | 129 +++++++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 4 deletions(-)
diff --git a/dlls/http.sys/Makefile.in b/dlls/http.sys/Makefile.in index 449ebaef0a..a170957db3 100644 --- a/dlls/http.sys/Makefile.in +++ b/dlls/http.sys/Makefile.in @@ -1,5 +1,5 @@ MODULE = http.sys -IMPORTS = ntoskrnl +IMPORTS = ntoskrnl ws2_32 EXTRADLLFLAGS = -Wl,--subsystem,native -mno-cygwin
C_SRCS = \ diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index e57cce9e3c..bfec4cfd35 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -41,22 +41,89 @@ WINE_DEFAULT_DEBUG_CHANNEL(http);
DECLARE_CRITICAL_SECTION(http_cs);
+static HANDLE request_thread, request_event; +static BOOL thread_stop; + +struct connection +{ + struct list entry; /* in "connections" below */ + + int socket; +}; + +static struct list connections = LIST_INIT(connections); + struct request_queue { struct list entry; HTTP_URL_CONTEXT context; char *url; + int socket; };
static struct list request_queues = LIST_INIT(request_queues);
+static void accept_connection(int socket) +{ + struct connection *conn; + ULONG true = 1; + int peer; + + if ((peer = accept(socket, NULL, NULL)) == -1) + return; + + if (!(conn = heap_alloc_zero(sizeof(*conn)))) + { + ERR("Failed to allocate memory.\n"); + shutdown(peer, SD_BOTH); + closesocket(peer); + return; + } + WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE); + ioctlsocket(peer, FIONBIO, &true); + conn->socket = peer; + list_add_head(&connections, &conn->entry); +} + +static void close_connection(struct connection *conn) +{ + shutdown(conn->socket, SD_BOTH); + closesocket(conn->socket); + list_remove(&conn->entry); + heap_free(conn); +} + +static DWORD WINAPI request_thread_proc(void *arg) +{ + struct request_queue *queue; + + TRACE("Starting request thread.\n"); + + while (!WaitForSingleObject(request_event, INFINITE)) + { + EnterCriticalSection(&http_cs); + + LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry) + { + if (queue->socket != -1) + accept_connection(queue->socket); + } + + LeaveCriticalSection(&http_cs); + } + + TRACE("Stopping request thread.\n"); + + return 0; +}
static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) { const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer; - unsigned short port; + struct sockaddr_in addr; char *url, *endptr; - int count = 0; + int s, count = 0; + ULONG true = 1; const char *p;
TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context)); @@ -69,7 +136,7 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) else if (strncmp(params->url, "http://", 7) || !strchr(params->url + 7, ':') || params->url[strlen(params->url) - 1] != '/') return STATUS_INVALID_PARAMETER; - if (!(port = strtol(strchr(params->url + 7, ':') + 1, &endptr, 10)) || *endptr != '/') + if (!(addr.sin_port = htons(strtol(strchr(params->url + 7, ':') + 1, &endptr, 10))) || *endptr != '/') return STATUS_INVALID_PARAMETER;
if (!(url = heap_alloc(strlen(params->url)))) @@ -97,6 +164,37 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) return STATUS_NOT_IMPLEMENTED; }
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + ERR("Failed to create socket, error %u.\n", WSAGetLastError()); + LeaveCriticalSection(&http_cs); + heap_free(url); + return STATUS_UNSUCCESSFUL; + } + + addr.sin_family = AF_INET; + addr.sin_addr.S_un.S_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + ERR("Failed to bind socket, error %u.\n", WSAGetLastError()); + LeaveCriticalSection(&http_cs); + closesocket(s); + heap_free(url); + return STATUS_UNSUCCESSFUL; + } + + if (listen(s, SOMAXCONN) == -1) + { + ERR("Failed to listen to port %u, error %u.\n", addr.sin_port, WSAGetLastError()); + LeaveCriticalSection(&http_cs); + closesocket(s); + heap_free(url); + return STATUS_OBJECT_NAME_COLLISION; + } + + ioctlsocket(s, FIONBIO, &true); + WSAEventSelect(s, request_event, FD_ACCEPT); + queue->socket = s; queue->url = url; queue->context = params->context;
@@ -178,6 +276,11 @@ static void close_queue(struct request_queue *queue) { EnterCriticalSection(&http_cs); list_remove(&queue->entry); + if (queue->socket != -1) + { + shutdown(queue->socket, SD_BOTH); + closesocket(queue->socket); + } LeaveCriticalSection(&http_cs);
heap_free(queue->url); @@ -200,12 +303,26 @@ static NTSTATUS WINAPI dispatch_close(DEVICE_OBJECT *device, IRP *irp) static void WINAPI unload(DRIVER_OBJECT *driver) { struct request_queue *queue, *queue_next; + struct connection *conn, *conn_next; + + thread_stop = TRUE; + SetEvent(request_event); + WaitForSingleObject(request_thread, INFINITE); + CloseHandle(request_thread); + CloseHandle(request_event); + + LIST_FOR_EACH_ENTRY_SAFE(conn, conn_next, &connections, struct connection, entry) + { + close_connection(conn); + }
LIST_FOR_EACH_ENTRY_SAFE(queue, queue_next, &request_queues, struct request_queue, entry) { close_queue(queue); }
+ WSACleanup(); + IoDeleteDevice(device_obj); NtClose(directory_obj); } @@ -216,6 +333,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) static const WCHAR directory_nameW[] = {'\','D','e','v','i','c','e','\','H','t','t','p',0}; OBJECT_ATTRIBUTES attr = {sizeof(attr)}; UNICODE_STRING string; + WSADATA wsadata; NTSTATUS ret;
TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer)); @@ -238,5 +356,10 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_ioctl; driver->DriverUnload = unload;
+ WSAStartup(MAKEWORD(1,1), &wsadata); + + request_event = CreateEventW(NULL, FALSE, FALSE, NULL); + request_thread = CreateThread(NULL, 0, request_thread_proc, NULL, 0, NULL); + return STATUS_SUCCESS; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index bfec4cfd35..5e29db0fa7 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -49,6 +49,9 @@ struct connection struct list entry; /* in "connections" below */
int socket; + + char *buffer; + unsigned int len, size; };
static struct list connections = LIST_INIT(connections); @@ -79,6 +82,15 @@ static void accept_connection(int socket) closesocket(peer); return; } + if (!(conn->buffer = heap_alloc(8192))) + { + ERR("Failed to allocate buffer memory.\n"); + heap_free(conn); + shutdown(peer, SD_BOTH); + closesocket(peer); + return; + } + conn->size = 8192; WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE); ioctlsocket(peer, FIONBIO, &true); conn->socket = peer; @@ -93,8 +105,74 @@ static void close_connection(struct connection *conn) heap_free(conn); }
+/* Upon receiving a request, parse it to ensure that it is a valid HTTP request, + * and mark down some information that we will use later. Returns 1 if we parsed + * a complete request, 0 if incomplete, -1 if invalid. */ +static int parse_request(struct connection *conn) +{ + FIXME("Not implemented.\n"); + return -1; +} + +static void receive_data(struct connection *conn) +{ + int len, ret; + + /* We might be waiting for an IRP, but always call recv() anyway, since we + * might have been woken up by the socket closing. */ + if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) <= 0) + { + if (WSAGetLastError() == WSAEWOULDBLOCK) + return; /* nothing to receive */ + else if (!len) + TRACE("Connection was shut down by peer.\n"); + else + ERR("Got error %u; shutting down connection.\n", WSAGetLastError()); + close_connection(conn); + return; + } + conn->len += len; + + TRACE("Received %u bytes of data.\n", len); + + if (!(ret = parse_request(conn))) + { + ULONG available; + ioctlsocket(conn->socket, FIONREAD, &available); + if (available) + { + TRACE("%u more bytes of data available, trying with larger buffer.\n", available); + if (!(conn->buffer = heap_realloc(conn->buffer, conn->len + available))) + { + ERR("Failed to allocate %u bytes of memory.\n", conn->len + available); + close_connection(conn); + return; + } + conn->size = conn->len + available; + + if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) < 0) + { + ERR("Got error %u; shutting down connection.\n", WSAGetLastError()); + close_connection(conn); + return; + } + TRACE("Received %u bytes of data.\n", len); + conn->len += len; + ret = parse_request(conn); + } + } + if (!ret) + TRACE("Request is incomplete, waiting for more data.\n"); + else if (ret < 0) + { + WARN("Failed to parse request; shutting down connection.\n"); + close_connection(conn); + } +} + static DWORD WINAPI request_thread_proc(void *arg) { + struct connection *conn, *cursor; struct request_queue *queue;
TRACE("Starting request thread.\n"); @@ -109,6 +187,11 @@ static DWORD WINAPI request_thread_proc(void *arg) accept_connection(queue->socket); }
+ LIST_FOR_EACH_ENTRY_SAFE(conn, cursor, &connections, struct connection, entry) + { + receive_data(conn); + } + LeaveCriticalSection(&http_cs); }