http.sys: Use a separate file and preprocessor directives to avoid duplication.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a897d858e4
commit
cab14ee04c
|
@ -33,164 +33,6 @@ static DEVICE_OBJECT *device_obj;
|
|||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(http);
|
||||
|
||||
/* We have to return the HTTP_REQUEST structure to userspace exactly as it will
|
||||
* be consumed; httpapi has no opportunity to massage it. Since it contains
|
||||
* pointers, this is somewhat nontrivial. */
|
||||
|
||||
struct http_unknown_header_32
|
||||
{
|
||||
USHORT NameLength;
|
||||
USHORT RawValueLength;
|
||||
ULONG pName; /* char string */
|
||||
ULONG pRawValue; /* char string */
|
||||
};
|
||||
|
||||
struct http_data_chunk_32
|
||||
{
|
||||
HTTP_DATA_CHUNK_TYPE DataChunkType;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG pBuffer; /* char string */
|
||||
ULONG BufferLength;
|
||||
} FromMemory;
|
||||
/* for the struct size */
|
||||
struct
|
||||
{
|
||||
ULARGE_INTEGER StartingOffset;
|
||||
ULARGE_INTEGER Length;
|
||||
HANDLE FileHandle;
|
||||
} FromFileHandle;
|
||||
};
|
||||
};
|
||||
|
||||
struct http_request_32
|
||||
{
|
||||
ULONG Flags;
|
||||
HTTP_CONNECTION_ID ConnectionId;
|
||||
HTTP_REQUEST_ID RequestId;
|
||||
HTTP_URL_CONTEXT UrlContext;
|
||||
HTTP_VERSION Version;
|
||||
HTTP_VERB Verb;
|
||||
USHORT UnknownVerbLength;
|
||||
USHORT RawUrlLength;
|
||||
ULONG pUnknownVerb; /* char string */
|
||||
ULONG pRawUrl; /* char string */
|
||||
struct
|
||||
{
|
||||
USHORT FullUrlLength;
|
||||
USHORT HostLength;
|
||||
USHORT AbsPathLength;
|
||||
USHORT QueryStringLength;
|
||||
ULONG pFullUrl; /* WCHAR string */
|
||||
ULONG pHost; /* pointer to above */
|
||||
ULONG pAbsPath; /* pointer to above */
|
||||
ULONG pQueryString; /* pointer to above */
|
||||
} CookedUrl;
|
||||
struct
|
||||
{
|
||||
ULONG pRemoteAddress; /* SOCKADDR */
|
||||
ULONG pLocalAddress; /* SOCKADDR */
|
||||
} Address;
|
||||
struct
|
||||
{
|
||||
USHORT UnknownHeaderCount;
|
||||
ULONG pUnknownHeaders; /* struct http_unknown_header_32 */
|
||||
USHORT TrailerCount;
|
||||
ULONG pTrailers; /* NULL */
|
||||
struct
|
||||
{
|
||||
USHORT RawValueLength;
|
||||
ULONG pRawValue; /* char string */
|
||||
} KnownHeaders[HttpHeaderRequestMaximum];
|
||||
} Headers;
|
||||
ULONGLONG BytesReceived;
|
||||
USHORT EntityChunkCount;
|
||||
ULONG pEntityChunks; /* struct http_data_chunk_32 */
|
||||
HTTP_RAW_CONNECTION_ID RawConnectionId;
|
||||
ULONG pSslInfo; /* NULL (FIXME) */
|
||||
USHORT RequestInfoCount;
|
||||
ULONG pRequestInfo; /* NULL (FIXME) */
|
||||
};
|
||||
|
||||
struct http_unknown_header_64
|
||||
{
|
||||
USHORT NameLength;
|
||||
USHORT RawValueLength;
|
||||
ULONGLONG pName; /* char string */
|
||||
ULONGLONG pRawValue; /* char string */
|
||||
};
|
||||
|
||||
struct http_data_chunk_64
|
||||
{
|
||||
HTTP_DATA_CHUNK_TYPE DataChunkType;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONGLONG pBuffer; /* char string */
|
||||
ULONG BufferLength;
|
||||
} FromMemory;
|
||||
/* for the struct size */
|
||||
struct
|
||||
{
|
||||
ULARGE_INTEGER StartingOffset;
|
||||
ULARGE_INTEGER Length;
|
||||
HANDLE FileHandle;
|
||||
} FromFileHandle;
|
||||
};
|
||||
};
|
||||
|
||||
struct http_request_64
|
||||
{
|
||||
ULONG Flags;
|
||||
HTTP_CONNECTION_ID ConnectionId;
|
||||
HTTP_REQUEST_ID RequestId;
|
||||
HTTP_URL_CONTEXT UrlContext;
|
||||
HTTP_VERSION Version;
|
||||
HTTP_VERB Verb;
|
||||
USHORT UnknownVerbLength;
|
||||
USHORT RawUrlLength;
|
||||
ULONGLONG pUnknownVerb; /* char string */
|
||||
ULONGLONG pRawUrl; /* char string */
|
||||
struct
|
||||
{
|
||||
USHORT FullUrlLength;
|
||||
USHORT HostLength;
|
||||
USHORT AbsPathLength;
|
||||
USHORT QueryStringLength;
|
||||
ULONGLONG pFullUrl; /* WCHAR string */
|
||||
ULONGLONG pHost; /* pointer to above */
|
||||
ULONGLONG pAbsPath; /* pointer to above */
|
||||
ULONGLONG pQueryString; /* pointer to above */
|
||||
} CookedUrl;
|
||||
struct
|
||||
{
|
||||
ULONGLONG pRemoteAddress; /* SOCKADDR */
|
||||
ULONGLONG pLocalAddress; /* SOCKADDR */
|
||||
} Address;
|
||||
struct
|
||||
{
|
||||
USHORT UnknownHeaderCount;
|
||||
ULONGLONG pUnknownHeaders; /* struct http_unknown_header_32 */
|
||||
USHORT TrailerCount;
|
||||
ULONGLONG pTrailers; /* NULL */
|
||||
struct
|
||||
{
|
||||
USHORT RawValueLength;
|
||||
ULONGLONG pRawValue; /* char string */
|
||||
} KnownHeaders[HttpHeaderRequestMaximum];
|
||||
} Headers;
|
||||
ULONGLONG BytesReceived;
|
||||
USHORT EntityChunkCount;
|
||||
ULONGLONG pEntityChunks; /* struct http_data_chunk_32 */
|
||||
HTTP_RAW_CONNECTION_ID RawConnectionId;
|
||||
ULONGLONG pSslInfo; /* NULL (FIXME) */
|
||||
USHORT RequestInfoCount;
|
||||
ULONGLONG pRequestInfo; /* NULL (FIXME) */
|
||||
};
|
||||
|
||||
#define DECLARE_CRITICAL_SECTION(cs) \
|
||||
static CRITICAL_SECTION cs; \
|
||||
static CRITICAL_SECTION_DEBUG cs##_debug = \
|
||||
|
@ -405,365 +247,44 @@ static void parse_header(const char *name, int *name_len, const char **value, in
|
|||
*value_len = p - *value + 1;
|
||||
}
|
||||
|
||||
#define http_unknown_header http_unknown_header_64
|
||||
#define http_data_chunk http_data_chunk_64
|
||||
#define http_request http_request_64
|
||||
#define complete_irp complete_irp_64
|
||||
#define POINTER ULONGLONG
|
||||
#include "request.h"
|
||||
#undef http_unknown_header
|
||||
#undef http_data_chunk
|
||||
#undef http_request
|
||||
#undef complete_irp
|
||||
#undef POINTER
|
||||
|
||||
#define http_unknown_header http_unknown_header_32
|
||||
#define http_data_chunk http_data_chunk_32
|
||||
#define http_request http_request_32
|
||||
#define complete_irp complete_irp_32
|
||||
#define POINTER ULONG
|
||||
#include "request.h"
|
||||
#undef http_unknown_header
|
||||
#undef http_data_chunk
|
||||
#undef http_request
|
||||
#undef complete_irp
|
||||
#undef POINTER
|
||||
|
||||
static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
|
||||
{
|
||||
static const WCHAR httpW[] = {'h','t','t','p',':','/','/'};
|
||||
const struct http_receive_request_params params
|
||||
= *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer;
|
||||
DWORD irp_size = (params.bits == 32) ? sizeof(struct http_request_32) : sizeof(struct http_request_64);
|
||||
ULONG cooked_len, host_len, abs_path_len, query_len, chunk_len = 0, offset, processed;
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
||||
const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
const char *p, *name, *value, *host, *abs_path, *query;
|
||||
USHORT unk_headers_count = 0, unk_header_idx;
|
||||
int name_len, value_len, len;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
TRACE("Completing IRP %p.\n", irp);
|
||||
|
||||
if (!conn->req_id)
|
||||
conn->req_id = ++req_id_counter;
|
||||
|
||||
/* First calculate the total buffer size needed for this IRP. */
|
||||
|
||||
if (conn->unk_verb_len)
|
||||
irp_size += conn->unk_verb_len + 1;
|
||||
irp_size += conn->url_len + 1;
|
||||
|
||||
/* cooked URL */
|
||||
if (conn->url[0] == '/')
|
||||
{
|
||||
p = host = conn->host;
|
||||
while (isgraph(*p)) ++p;
|
||||
host_len = p - conn->host;
|
||||
abs_path = conn->url;
|
||||
abs_path_len = conn->url_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = conn->url + 7;
|
||||
abs_path = strchr(host, '/');
|
||||
host_len = abs_path - host;
|
||||
abs_path_len = (conn->url + conn->url_len) - abs_path;
|
||||
}
|
||||
if ((query = memchr(abs_path, '?', abs_path_len)))
|
||||
{
|
||||
query_len = (abs_path + abs_path_len) - query;
|
||||
abs_path_len = query - abs_path;
|
||||
}
|
||||
else
|
||||
query_len = 0;
|
||||
cooked_len = (7 /* scheme */ + host_len + abs_path_len + query_len) * sizeof(WCHAR);
|
||||
irp_size += cooked_len + sizeof(WCHAR);
|
||||
|
||||
/* addresses */
|
||||
irp_size += 2 * sizeof(addr);
|
||||
|
||||
/* headers */
|
||||
p = strstr(conn->buffer, "\r\n") + 2;
|
||||
while (memcmp(p, "\r\n", 2))
|
||||
{
|
||||
name = p;
|
||||
parse_header(name, &name_len, &value, &value_len);
|
||||
if (parse_header_name(name, name_len) == HttpHeaderRequestMaximum)
|
||||
{
|
||||
irp_size += name_len + 1;
|
||||
++unk_headers_count;
|
||||
}
|
||||
irp_size += value_len + 1;
|
||||
p = strstr(p, "\r\n") + 2;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if (params.bits == 32)
|
||||
irp_size += unk_headers_count * sizeof(struct http_unknown_header_32);
|
||||
return complete_irp_32(conn, irp);
|
||||
else
|
||||
irp_size += unk_headers_count * sizeof(struct http_unknown_header_64);
|
||||
|
||||
TRACE("Need %u bytes, have %u.\n", irp_size, output_len);
|
||||
irp->IoStatus.Information = irp_size;
|
||||
|
||||
memset(irp->AssociatedIrp.SystemBuffer, 0, output_len);
|
||||
|
||||
if (output_len < irp_size)
|
||||
{
|
||||
if (params.bits == 32)
|
||||
{
|
||||
struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer;
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer;
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
}
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (params.bits == 32)
|
||||
{
|
||||
struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer;
|
||||
struct http_unknown_header_32 *unk_headers = NULL;
|
||||
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
struct http_data_chunk_32 *chunk = NULL;
|
||||
|
||||
offset = sizeof(*req);
|
||||
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
req->UrlContext = conn->queue->context;
|
||||
req->Version = conn->version;
|
||||
req->Verb = conn->verb;
|
||||
req->UnknownVerbLength = conn->unk_verb_len;
|
||||
req->RawUrlLength = conn->url_len;
|
||||
|
||||
if (conn->unk_verb_len)
|
||||
{
|
||||
req->pUnknownVerb = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->buffer, conn->unk_verb_len);
|
||||
offset += conn->unk_verb_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
|
||||
req->pRawUrl = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->url, conn->url_len);
|
||||
offset += conn->url_len;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->CookedUrl.FullUrlLength = cooked_len;
|
||||
req->CookedUrl.HostLength = host_len * sizeof(WCHAR);
|
||||
req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR);
|
||||
req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR);
|
||||
req->CookedUrl.pFullUrl = params.addr + offset;
|
||||
req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR);
|
||||
req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR);
|
||||
if (query)
|
||||
req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR);
|
||||
|
||||
memcpy(buffer + offset, httpW, sizeof(httpW));
|
||||
offset += 7 * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR));
|
||||
offset += host_len * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len,
|
||||
(WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR));
|
||||
offset += (abs_path_len + query_len) * sizeof(WCHAR);
|
||||
buffer[offset++] = 0;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->Address.pRemoteAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getpeername(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Address.pLocalAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getsockname(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Headers.UnknownHeaderCount = unk_headers_count;
|
||||
if (unk_headers_count)
|
||||
{
|
||||
req->Headers.pUnknownHeaders = params.addr + offset;
|
||||
unk_headers = (struct http_unknown_header_32 *)(buffer + offset);
|
||||
offset += unk_headers_count * sizeof(*unk_headers);
|
||||
}
|
||||
|
||||
unk_header_idx = 0;
|
||||
p = strstr(conn->buffer, "\r\n") + 2;
|
||||
while (memcmp(p, "\r\n", 2))
|
||||
{
|
||||
HTTP_HEADER_ID id;
|
||||
|
||||
name = p;
|
||||
parse_header(name, &name_len, &value, &value_len);
|
||||
if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum)
|
||||
{
|
||||
unk_headers[unk_header_idx].NameLength = name_len;
|
||||
unk_headers[unk_header_idx].RawValueLength = value_len;
|
||||
unk_headers[unk_header_idx].pName = params.addr + offset;
|
||||
memcpy(buffer + offset, name, name_len);
|
||||
offset += name_len;
|
||||
buffer[offset++] = 0;
|
||||
unk_headers[unk_header_idx].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
++unk_header_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
req->Headers.KnownHeaders[id].RawValueLength = value_len;
|
||||
req->Headers.KnownHeaders[id].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
p = strstr(p, "\r\n") + 2;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY))
|
||||
chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk)));
|
||||
if (chunk_len)
|
||||
{
|
||||
req->EntityChunkCount = 1;
|
||||
req->pEntityChunks = params.addr + offset;
|
||||
chunk = (struct http_data_chunk_32 *)(buffer + offset);
|
||||
offset += sizeof(*chunk);
|
||||
chunk->DataChunkType = HttpDataChunkFromMemory;
|
||||
chunk->FromMemory.BufferLength = chunk_len;
|
||||
chunk->FromMemory.pBuffer = params.addr + offset;
|
||||
memcpy(buffer + offset, p, chunk_len);
|
||||
offset += chunk_len;
|
||||
|
||||
irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len;
|
||||
}
|
||||
|
||||
if (chunk_len < conn->content_len)
|
||||
req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
|
||||
|
||||
req->BytesReceived = conn->req_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer;
|
||||
struct http_unknown_header_64 *unk_headers = NULL;
|
||||
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
struct http_data_chunk_64 *chunk = NULL;
|
||||
|
||||
offset = sizeof(*req);
|
||||
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
req->UrlContext = conn->queue->context;
|
||||
req->Version = conn->version;
|
||||
req->Verb = conn->verb;
|
||||
req->UnknownVerbLength = conn->unk_verb_len;
|
||||
req->RawUrlLength = conn->url_len;
|
||||
|
||||
if (conn->unk_verb_len)
|
||||
{
|
||||
req->pUnknownVerb = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->buffer, conn->unk_verb_len);
|
||||
offset += conn->unk_verb_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
|
||||
req->pRawUrl = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->url, conn->url_len);
|
||||
offset += conn->url_len;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->CookedUrl.FullUrlLength = cooked_len;
|
||||
req->CookedUrl.HostLength = host_len * sizeof(WCHAR);
|
||||
req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR);
|
||||
req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR);
|
||||
req->CookedUrl.pFullUrl = params.addr + offset;
|
||||
req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR);
|
||||
req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR);
|
||||
if (query)
|
||||
req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR);
|
||||
|
||||
memcpy(buffer + offset, httpW, sizeof(httpW));
|
||||
offset += 7 * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR));
|
||||
offset += host_len * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len,
|
||||
(WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR));
|
||||
offset += (abs_path_len + query_len) * sizeof(WCHAR);
|
||||
buffer[offset++] = 0;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->Address.pRemoteAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getpeername(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Address.pLocalAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getsockname(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Headers.UnknownHeaderCount = unk_headers_count;
|
||||
if (unk_headers_count)
|
||||
{
|
||||
req->Headers.pUnknownHeaders = params.addr + offset;
|
||||
unk_headers = (struct http_unknown_header_64 *)(buffer + offset);
|
||||
offset += unk_headers_count * sizeof(*unk_headers);
|
||||
}
|
||||
|
||||
unk_header_idx = 0;
|
||||
p = strstr(conn->buffer, "\r\n") + 2;
|
||||
while (memcmp(p, "\r\n", 2))
|
||||
{
|
||||
HTTP_HEADER_ID id;
|
||||
|
||||
name = p;
|
||||
parse_header(name, &name_len, &value, &value_len);
|
||||
if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum)
|
||||
{
|
||||
unk_headers[unk_header_idx].NameLength = name_len;
|
||||
unk_headers[unk_header_idx].RawValueLength = value_len;
|
||||
unk_headers[unk_header_idx].pName = params.addr + offset;
|
||||
memcpy(buffer + offset, name, name_len);
|
||||
offset += name_len;
|
||||
buffer[offset++] = 0;
|
||||
unk_headers[unk_header_idx].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
++unk_header_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
req->Headers.KnownHeaders[id].RawValueLength = value_len;
|
||||
req->Headers.KnownHeaders[id].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
p = strstr(p, "\r\n") + 2;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY))
|
||||
chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk)));
|
||||
if (chunk_len)
|
||||
{
|
||||
req->EntityChunkCount = 1;
|
||||
req->pEntityChunks = params.addr + offset;
|
||||
chunk = (struct http_data_chunk_64 *)(buffer + offset);
|
||||
offset += sizeof(*chunk);
|
||||
chunk->DataChunkType = HttpDataChunkFromMemory;
|
||||
chunk->FromMemory.BufferLength = chunk_len;
|
||||
chunk->FromMemory.pBuffer = params.addr + offset;
|
||||
memcpy(buffer + offset, p, chunk_len);
|
||||
offset += chunk_len;
|
||||
|
||||
irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len;
|
||||
}
|
||||
|
||||
if (chunk_len < conn->content_len)
|
||||
req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
|
||||
|
||||
req->BytesReceived = conn->req_len;
|
||||
}
|
||||
|
||||
assert(offset == irp->IoStatus.Information);
|
||||
|
||||
conn->available = FALSE;
|
||||
processed = conn->req_len - (conn->content_len - chunk_len);
|
||||
memmove(conn->buffer, conn->buffer + processed, conn->len - processed);
|
||||
conn->content_len -= chunk_len;
|
||||
conn->len -= processed;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return complete_irp_64(conn, irp);
|
||||
}
|
||||
|
||||
/* Complete an IOCTL_HTTP_RECEIVE_REQUEST IRP if there is one to complete. */
|
||||
|
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* We have to return the HTTP_REQUEST structure to userspace exactly as it will
|
||||
* be consumed; httpapi has no opportunity to massage it. Since it contains
|
||||
* pointers, this is somewhat nontrivial. */
|
||||
|
||||
struct http_unknown_header
|
||||
{
|
||||
USHORT NameLength;
|
||||
USHORT RawValueLength;
|
||||
POINTER pName; /* char string */
|
||||
POINTER pRawValue; /* char string */
|
||||
};
|
||||
|
||||
struct http_data_chunk
|
||||
{
|
||||
HTTP_DATA_CHUNK_TYPE DataChunkType;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
POINTER pBuffer; /* char string */
|
||||
ULONG BufferLength;
|
||||
} FromMemory;
|
||||
/* for the struct size */
|
||||
struct
|
||||
{
|
||||
ULARGE_INTEGER StartingOffset;
|
||||
ULARGE_INTEGER Length;
|
||||
POINTER FileHandle;
|
||||
} FromFileHandle;
|
||||
};
|
||||
};
|
||||
|
||||
struct http_request
|
||||
{
|
||||
ULONG Flags;
|
||||
HTTP_CONNECTION_ID ConnectionId;
|
||||
HTTP_REQUEST_ID RequestId;
|
||||
HTTP_URL_CONTEXT UrlContext;
|
||||
HTTP_VERSION Version;
|
||||
HTTP_VERB Verb;
|
||||
USHORT UnknownVerbLength;
|
||||
USHORT RawUrlLength;
|
||||
POINTER pUnknownVerb; /* char string */
|
||||
POINTER pRawUrl; /* char string */
|
||||
struct
|
||||
{
|
||||
USHORT FullUrlLength;
|
||||
USHORT HostLength;
|
||||
USHORT AbsPathLength;
|
||||
USHORT QueryStringLength;
|
||||
POINTER pFullUrl; /* WCHAR string */
|
||||
POINTER pHost; /* pointer to above */
|
||||
POINTER pAbsPath; /* pointer to above */
|
||||
POINTER pQueryString; /* pointer to above */
|
||||
} CookedUrl;
|
||||
struct
|
||||
{
|
||||
POINTER pRemoteAddress; /* SOCKADDR */
|
||||
POINTER pLocalAddress; /* SOCKADDR */
|
||||
} Address;
|
||||
struct
|
||||
{
|
||||
USHORT UnknownHeaderCount;
|
||||
POINTER pUnknownHeaders; /* struct http_unknown_header */
|
||||
USHORT TrailerCount;
|
||||
POINTER pTrailers; /* NULL */
|
||||
struct
|
||||
{
|
||||
USHORT RawValueLength;
|
||||
POINTER pRawValue; /* char string */
|
||||
} KnownHeaders[HttpHeaderRequestMaximum];
|
||||
} Headers;
|
||||
ULONGLONG BytesReceived;
|
||||
USHORT EntityChunkCount;
|
||||
POINTER pEntityChunks; /* struct http_data_chunk */
|
||||
HTTP_RAW_CONNECTION_ID RawConnectionId;
|
||||
POINTER pSslInfo; /* NULL (FIXME) */
|
||||
USHORT RequestInfoCount;
|
||||
POINTER pRequestInfo; /* NULL (FIXME) */
|
||||
};
|
||||
|
||||
static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
|
||||
{
|
||||
const struct http_receive_request_params params
|
||||
= *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer;
|
||||
ULONG cooked_len, host_len, abs_path_len, query_len, chunk_len = 0, offset, processed;
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
||||
const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
struct http_request *req = irp->AssociatedIrp.SystemBuffer;
|
||||
const char *p, *name, *value, *host, *abs_path, *query;
|
||||
struct http_unknown_header *unk_headers = NULL;
|
||||
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
DWORD irp_size = sizeof(struct http_request);
|
||||
USHORT unk_headers_count = 0, unk_header_idx;
|
||||
struct http_data_chunk *chunk = NULL;
|
||||
int name_len, value_len, len;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
/* First calculate the total buffer size needed for this IRP. */
|
||||
|
||||
if (conn->unk_verb_len)
|
||||
irp_size += conn->unk_verb_len + 1;
|
||||
irp_size += conn->url_len + 1;
|
||||
|
||||
/* cooked URL */
|
||||
if (conn->url[0] == '/')
|
||||
{
|
||||
p = host = conn->host;
|
||||
while (isgraph(*p)) ++p;
|
||||
host_len = p - conn->host;
|
||||
abs_path = conn->url;
|
||||
abs_path_len = conn->url_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = conn->url + 7;
|
||||
abs_path = strchr(host, '/');
|
||||
host_len = abs_path - host;
|
||||
abs_path_len = (conn->url + conn->url_len) - abs_path;
|
||||
}
|
||||
if ((query = memchr(abs_path, '?', abs_path_len)))
|
||||
{
|
||||
query_len = (abs_path + abs_path_len) - query;
|
||||
abs_path_len = query - abs_path;
|
||||
}
|
||||
else
|
||||
query_len = 0;
|
||||
cooked_len = (7 /* scheme */ + host_len + abs_path_len + query_len) * sizeof(WCHAR);
|
||||
irp_size += cooked_len + sizeof(WCHAR);
|
||||
|
||||
/* addresses */
|
||||
irp_size += 2 * sizeof(addr);
|
||||
|
||||
/* headers */
|
||||
p = strstr(conn->buffer, "\r\n") + 2;
|
||||
while (memcmp(p, "\r\n", 2))
|
||||
{
|
||||
name = p;
|
||||
parse_header(name, &name_len, &value, &value_len);
|
||||
if (parse_header_name(name, name_len) == HttpHeaderRequestMaximum)
|
||||
{
|
||||
irp_size += name_len + 1;
|
||||
++unk_headers_count;
|
||||
}
|
||||
irp_size += value_len + 1;
|
||||
p = strstr(p, "\r\n") + 2;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
irp_size += unk_headers_count * sizeof(struct http_unknown_header);
|
||||
|
||||
TRACE("Need %u bytes, have %u.\n", irp_size, output_len);
|
||||
irp->IoStatus.Information = irp_size;
|
||||
|
||||
memset(irp->AssociatedIrp.SystemBuffer, 0, output_len);
|
||||
|
||||
if (output_len < irp_size)
|
||||
{
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
offset = sizeof(*req);
|
||||
|
||||
req->ConnectionId = (ULONG_PTR)conn;
|
||||
req->RequestId = conn->req_id;
|
||||
req->UrlContext = conn->queue->context;
|
||||
req->Version = conn->version;
|
||||
req->Verb = conn->verb;
|
||||
req->UnknownVerbLength = conn->unk_verb_len;
|
||||
req->RawUrlLength = conn->url_len;
|
||||
|
||||
if (conn->unk_verb_len)
|
||||
{
|
||||
req->pUnknownVerb = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->buffer, conn->unk_verb_len);
|
||||
offset += conn->unk_verb_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
|
||||
req->pRawUrl = params.addr + offset;
|
||||
memcpy(buffer + offset, conn->url, conn->url_len);
|
||||
offset += conn->url_len;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->CookedUrl.FullUrlLength = cooked_len;
|
||||
req->CookedUrl.HostLength = host_len * sizeof(WCHAR);
|
||||
req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR);
|
||||
req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR);
|
||||
req->CookedUrl.pFullUrl = params.addr + offset;
|
||||
req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR);
|
||||
req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR);
|
||||
if (query)
|
||||
req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR);
|
||||
|
||||
memcpy(buffer + offset, L"http://", sizeof(L"http://"));
|
||||
offset += 7 * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR));
|
||||
offset += host_len * sizeof(WCHAR);
|
||||
MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len,
|
||||
(WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR));
|
||||
offset += (abs_path_len + query_len) * sizeof(WCHAR);
|
||||
buffer[offset++] = 0;
|
||||
buffer[offset++] = 0;
|
||||
|
||||
req->Address.pRemoteAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getpeername(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Address.pLocalAddress = params.addr + offset;
|
||||
len = sizeof(addr);
|
||||
getsockname(conn->socket, (struct sockaddr *)&addr, &len);
|
||||
memcpy(buffer + offset, &addr, sizeof(addr));
|
||||
offset += sizeof(addr);
|
||||
|
||||
req->Headers.UnknownHeaderCount = unk_headers_count;
|
||||
if (unk_headers_count)
|
||||
{
|
||||
req->Headers.pUnknownHeaders = params.addr + offset;
|
||||
unk_headers = (struct http_unknown_header *)(buffer + offset);
|
||||
offset += unk_headers_count * sizeof(*unk_headers);
|
||||
}
|
||||
|
||||
unk_header_idx = 0;
|
||||
p = strstr(conn->buffer, "\r\n") + 2;
|
||||
while (memcmp(p, "\r\n", 2))
|
||||
{
|
||||
HTTP_HEADER_ID id;
|
||||
|
||||
name = p;
|
||||
parse_header(name, &name_len, &value, &value_len);
|
||||
if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum)
|
||||
{
|
||||
unk_headers[unk_header_idx].NameLength = name_len;
|
||||
unk_headers[unk_header_idx].RawValueLength = value_len;
|
||||
unk_headers[unk_header_idx].pName = params.addr + offset;
|
||||
memcpy(buffer + offset, name, name_len);
|
||||
offset += name_len;
|
||||
buffer[offset++] = 0;
|
||||
unk_headers[unk_header_idx].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
++unk_header_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
req->Headers.KnownHeaders[id].RawValueLength = value_len;
|
||||
req->Headers.KnownHeaders[id].pRawValue = params.addr + offset;
|
||||
memcpy(buffer + offset, value, value_len);
|
||||
offset += value_len;
|
||||
buffer[offset++] = 0;
|
||||
}
|
||||
p = strstr(p, "\r\n") + 2;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY))
|
||||
chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk)));
|
||||
if (chunk_len)
|
||||
{
|
||||
req->EntityChunkCount = 1;
|
||||
req->pEntityChunks = params.addr + offset;
|
||||
chunk = (struct http_data_chunk *)(buffer + offset);
|
||||
offset += sizeof(*chunk);
|
||||
chunk->DataChunkType = HttpDataChunkFromMemory;
|
||||
chunk->FromMemory.BufferLength = chunk_len;
|
||||
chunk->FromMemory.pBuffer = params.addr + offset;
|
||||
memcpy(buffer + offset, p, chunk_len);
|
||||
offset += chunk_len;
|
||||
|
||||
irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len;
|
||||
}
|
||||
|
||||
if (chunk_len < conn->content_len)
|
||||
req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
|
||||
|
||||
req->BytesReceived = conn->req_len;
|
||||
|
||||
assert(offset == irp->IoStatus.Information);
|
||||
|
||||
conn->available = FALSE;
|
||||
processed = conn->req_len - (conn->content_len - chunk_len);
|
||||
memmove(conn->buffer, conn->buffer + processed, conn->len - processed);
|
||||
conn->content_len -= chunk_len;
|
||||
conn->len -= processed;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue