Commit 5f6ae94a authored by Rob Shearman's avatar Rob Shearman Committed by Alexandre Julliard

rpcrt4: Implement RPC over HTTP support.

CodeWeavers did this work for supporting the optional HTTP connection method of Outlook 2003/2007 (must be running with Windows versions set to XP or higher to see this option). This was written before Microsoft publicly released a specification for the RPC over HTTP protocol and so was developed by examining traffic flowing between a Windows client and an IIS server.
parent 834c5b42
......@@ -6,7 +6,7 @@ VPATH = @srcdir@
MODULE = rpcrt4.dll
IMPORTLIB = rpcrt4
IMPORTS = uuid advapi32 kernel32 ntdll
DELAYIMPORTS = secur32 user32
DELAYIMPORTS = wininet secur32 user32
C_SRCS = \
cproxy.c \
......
......@@ -394,6 +394,7 @@ RPC_STATUS RpcAssoc_GetClientConnection(RpcAssoc *assoc,
if (status != RPC_S_OK)
return status;
NewConnection->assoc = assoc;
status = RPCRT4_OpenClientConnection(NewConnection);
if (status != RPC_S_OK)
{
......
......@@ -34,6 +34,7 @@ typedef struct _RpcAssoc
/* id of this association group */
ULONG assoc_group_id;
UUID http_uuid;
CRITICAL_SECTION cs;
......
......@@ -24,6 +24,7 @@
#include "rpcndr.h"
#include "security.h"
#include "wine/list.h"
#include "rpc_defs.h"
typedef struct _RpcAuthInfo
......@@ -74,6 +75,7 @@ typedef struct _RpcConnection
struct list conn_pool_entry;
ULONG assoc_group_id; /* association group returned during binding */
RPC_ASYNC_STATE *async_state;
struct _RpcAssoc *assoc; /* association this connection is part of */
/* server-only */
/* The active interface bound to server. */
......@@ -96,6 +98,7 @@ struct connection_ops {
int (*wait_for_incoming_data)(RpcConnection *conn);
size_t (*get_top_of_tower)(unsigned char *tower_data, const char *networkaddr, const char *endpoint);
RPC_STATUS (*parse_top_of_tower)(const unsigned char *tower_data, size_t tower_size, char **networkaddr, char **endpoint);
RPC_STATUS (*receive_fragment)(RpcConnection *conn, RpcPktHdr **Header, void **Payload);
};
/* don't know what MS's structure looks like */
......
......@@ -120,6 +120,14 @@ typedef struct
} protocols[1];
} RpcPktBindNAckHdr;
/* undocumented packet sent during RPC over HTTP */
typedef struct
{
RpcPktCommonHdr common;
unsigned short flags;
unsigned short num_data_items;
} RpcPktHttpHdr;
/* Union representing all possible packet headers */
typedef union
{
......@@ -130,6 +138,7 @@ typedef union
RpcPktBindHdr bind;
RpcPktBindAckHdr bind_ack;
RpcPktBindNAckHdr bind_nack;
RpcPktHttpHdr http;
} RpcPktHdr;
typedef struct
......@@ -174,6 +183,7 @@ typedef struct
#define PKT_SHUTDOWN 17
#define PKT_CO_CANCEL 18
#define PKT_ORPHANED 19
#define PKT_HTTP 20
#define RESULT_ACCEPT 0
#define RESULT_USER_REJECTION 1
......
......@@ -59,13 +59,13 @@ enum secure_packet_direction
static RPC_STATUS I_RpcReAllocateBuffer(PRPC_MESSAGE pMsg);
static DWORD RPCRT4_GetHeaderSize(const RpcPktHdr *Header)
DWORD RPCRT4_GetHeaderSize(const RpcPktHdr *Header)
{
static const DWORD header_sizes[] = {
sizeof(Header->request), 0, sizeof(Header->response),
sizeof(Header->fault), 0, 0, 0, 0, 0, 0, 0, sizeof(Header->bind),
sizeof(Header->bind_ack), sizeof(Header->bind_nack),
0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, sizeof(Header->http)
};
ULONG ret = 0;
......@@ -76,7 +76,7 @@ static DWORD RPCRT4_GetHeaderSize(const RpcPktHdr *Header)
if (Header->common.flags & RPC_FLG_OBJECT_UUID)
ret += sizeof(UUID);
} else {
TRACE("invalid packet type\n");
WARN("invalid packet type %u\n", Header->common.ptype);
}
return ret;
......@@ -283,6 +283,118 @@ RpcPktHdr *RPCRT4_BuildBindAckHeader(unsigned long DataRepresentation,
return header;
}
RpcPktHdr *RPCRT4_BuildHttpHeader(unsigned long DataRepresentation,
unsigned short flags,
unsigned short num_data_items,
unsigned int payload_size)
{
RpcPktHdr *header;
header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(header->http) + payload_size);
if (header == NULL) {
ERR("failed to allocate memory\n");
return NULL;
}
RPCRT4_BuildCommonHeader(header, PKT_HTTP, DataRepresentation);
/* since the packet isn't current sent using RPCRT4_Send, set the flags
* manually here */
header->common.flags = RPC_FLG_FIRST|RPC_FLG_LAST;
header->common.call_id = 0;
header->common.frag_len = sizeof(header->http) + payload_size;
header->http.flags = flags;
header->http.num_data_items = num_data_items;
return header;
}
#define WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, type, value) \
do { \
*(unsigned int *)(payload) = (type); \
(payload) += 4; \
*(unsigned int *)(payload) = (value); \
(payload) += 4; \
} while (0)
#define WRITE_HTTP_PAYLOAD_FIELD_UUID(payload, type, uuid) \
do { \
*(unsigned int *)(payload) = (type); \
(payload) += 4; \
*(UUID *)(payload) = (uuid); \
(payload) += sizeof(UUID); \
} while (0)
#define WRITE_HTTP_PAYLOAD_FIELD_FLOW_CONTROL(payload, bytes_transmitted, flow_control_increment, uuid) \
do { \
*(unsigned int *)(payload) = 0x00000001; \
(payload) += 4; \
*(unsigned int *)(payload) = (bytes_transmitted); \
(payload) += 4; \
*(unsigned int *)(payload) = (flow_control_increment); \
(payload) += 4; \
*(UUID *)(payload) = (uuid); \
(payload) += sizeof(UUID); \
} while (0)
RpcPktHdr *RPCRT4_BuildHttpConnectHeader(unsigned short flags, int out_pipe,
const UUID *connection_uuid,
const UUID *pipe_uuid,
const UUID *association_uuid)
{
RpcPktHdr *header;
unsigned int size;
char *payload;
size = 8 + 4 + sizeof(UUID) + 4 + sizeof(UUID) + 8;
if (!out_pipe)
size += 8 + 4 + sizeof(UUID);
header = RPCRT4_BuildHttpHeader(NDR_LOCAL_DATA_REPRESENTATION, flags,
out_pipe ? 4 : 6, size);
if (!header) return NULL;
payload = (char *)(&header->http+1);
/* FIXME: what does this part of the payload do? */
WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, 0x00000006, 0x00000001);
WRITE_HTTP_PAYLOAD_FIELD_UUID(payload, 0x00000003, *connection_uuid);
WRITE_HTTP_PAYLOAD_FIELD_UUID(payload, 0x00000003, *pipe_uuid);
if (out_pipe)
/* FIXME: what does this part of the payload do? */
WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, 0x00000000, 0x00010000);
else
{
/* FIXME: what does this part of the payload do? */
WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, 0x00000004, 0x40000000);
/* FIXME: what does this part of the payload do? */
WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, 0x00000005, 0x000493e0);
WRITE_HTTP_PAYLOAD_FIELD_UUID(payload, 0x0000000c, *association_uuid);
}
return header;
}
RpcPktHdr *RPCRT4_BuildHttpFlowControlHeader(BOOL server, ULONG bytes_transmitted,
ULONG flow_control_increment,
const UUID *pipe_uuid)
{
RpcPktHdr *header;
char *payload;
header = RPCRT4_BuildHttpHeader(NDR_LOCAL_DATA_REPRESENTATION, 0x2, 2,
5 * sizeof(ULONG) + sizeof(UUID));
if (!header) return NULL;
payload = (char *)(&header->http+1);
WRITE_HTTP_PAYLOAD_FIELD_UINT32(payload, 0x0000000d, (server ? 0x0 : 0x3));
WRITE_HTTP_PAYLOAD_FIELD_FLOW_CONTROL(payload, bytes_transmitted,
flow_control_increment, *pipe_uuid);
return header;
}
VOID RPCRT4_FreeHeader(RpcPktHdr *Header)
{
HeapFree(GetProcessHeap(), 0, Header);
......@@ -362,6 +474,206 @@ static RPC_STATUS NCA2RPC_STATUS(NCA_STATUS status)
}
}
/* assumes the common header fields have already been validated */
BOOL RPCRT4_IsValidHttpPacket(RpcPktHdr *hdr, unsigned char *data,
unsigned short data_len)
{
unsigned short i;
BYTE *p = data;
for (i = 0; i < hdr->http.num_data_items; i++)
{
ULONG type;
if (data_len < sizeof(ULONG))
return FALSE;
type = *(ULONG *)p;
p += sizeof(ULONG);
data_len -= sizeof(ULONG);
switch (type)
{
case 0x3:
case 0xc:
if (data_len < sizeof(GUID))
return FALSE;
p += sizeof(GUID);
data_len -= sizeof(GUID);
break;
case 0x0:
case 0x2:
case 0x4:
case 0x5:
case 0x6:
case 0xd:
if (data_len < sizeof(ULONG))
return FALSE;
p += sizeof(ULONG);
data_len -= sizeof(ULONG);
break;
case 0x1:
if (data_len < 24)
return FALSE;
p += 24;
data_len -= 24;
break;
default:
FIXME("unimplemented type 0x%x\n", type);
break;
}
}
return TRUE;
}
/* assumes the HTTP packet has been validated */
unsigned char *RPCRT4_NextHttpHeaderField(unsigned char *data)
{
ULONG type;
type = *(ULONG *)data;
data += sizeof(ULONG);
switch (type)
{
case 0x3:
case 0xc:
return data + sizeof(GUID);
case 0x0:
case 0x2:
case 0x4:
case 0x5:
case 0x6:
case 0xd:
return data + sizeof(ULONG);
case 0x1:
return data + 24;
default:
FIXME("unimplemented type 0x%x\n", type);
return data;
}
}
#define READ_HTTP_PAYLOAD_FIELD_TYPE(data) *(ULONG *)(data)
#define GET_HTTP_PAYLOAD_FIELD_DATA(data) ((data) + sizeof(ULONG))
/* assumes the HTTP packet has been validated */
RPC_STATUS RPCRT4_ParseHttpPrepareHeader1(RpcPktHdr *header,
unsigned char *data, ULONG *field1)
{
ULONG type;
if (header->http.flags != 0x0)
{
ERR("invalid flags 0x%x\n", header->http.flags);
return RPC_S_PROTOCOL_ERROR;
}
if (header->http.num_data_items != 1)
{
ERR("invalid number of data items %d\n", header->http.num_data_items);
return RPC_S_PROTOCOL_ERROR;
}
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x00000002)
{
ERR("invalid type 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
*field1 = *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data);
return RPC_S_OK;
}
/* assumes the HTTP packet has been validated */
RPC_STATUS RPCRT4_ParseHttpPrepareHeader2(RpcPktHdr *header,
unsigned char *data, ULONG *field1,
ULONG *bytes_until_next_packet,
ULONG *field3)
{
ULONG type;
if (header->http.flags != 0x0)
{
ERR("invalid flags 0x%x\n", header->http.flags);
return RPC_S_PROTOCOL_ERROR;
}
if (header->http.num_data_items != 3)
{
ERR("invalid number of data items %d\n", header->http.num_data_items);
return RPC_S_PROTOCOL_ERROR;
}
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x00000006)
{
ERR("invalid type for field 1: 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
*field1 = *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data);
data = RPCRT4_NextHttpHeaderField(data);
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x00000000)
{
ERR("invalid type for field 2: 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
*bytes_until_next_packet = *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data);
data = RPCRT4_NextHttpHeaderField(data);
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x00000002)
{
ERR("invalid type for field 3: 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
*field3 = *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data);
return RPC_S_OK;
}
RPC_STATUS RPCRT4_ParseHttpFlowControlHeader(RpcPktHdr *header,
unsigned char *data, BOOL server,
ULONG *bytes_transmitted,
ULONG *flow_control_increment,
UUID *pipe_uuid)
{
ULONG type;
if (header->http.flags != 0x2)
{
ERR("invalid flags 0x%x\n", header->http.flags);
return RPC_S_PROTOCOL_ERROR;
}
if (header->http.num_data_items != 2)
{
ERR("invalid number of data items %d\n", header->http.num_data_items);
return RPC_S_PROTOCOL_ERROR;
}
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x0000000d)
{
ERR("invalid type for field 1: 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
if (*(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data) != (server ? 0x3 : 0x0))
{
ERR("invalid type for 0xd field data: 0x%08x\n", *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data));
return RPC_S_PROTOCOL_ERROR;
}
data = RPCRT4_NextHttpHeaderField(data);
type = READ_HTTP_PAYLOAD_FIELD_TYPE(data);
if (type != 0x00000001)
{
ERR("invalid type for field 2: 0x%08x\n", type);
return RPC_S_PROTOCOL_ERROR;
}
*bytes_transmitted = *(ULONG *)GET_HTTP_PAYLOAD_FIELD_DATA(data);
*flow_control_increment = *(ULONG *)(GET_HTTP_PAYLOAD_FIELD_DATA(data) + 4);
*pipe_uuid = *(UUID *)(GET_HTTP_PAYLOAD_FIELD_DATA(data) + 8);
return RPC_S_OK;
}
static RPC_STATUS RPCRT4_SecurePacket(RpcConnection *Connection,
enum secure_packet_direction dir,
RpcPktHdr *hdr, unsigned int hdr_size,
......@@ -687,7 +999,7 @@ RPC_STATUS RPCRT4_Send(RpcConnection *Connection, RpcPktHdr *Header,
}
/* validates version and frag_len fields */
static RPC_STATUS RPCRT4_ValidateCommonHeader(const RpcPktCommonHdr *hdr)
RPC_STATUS RPCRT4_ValidateCommonHeader(const RpcPktCommonHdr *hdr)
{
DWORD hdr_length;
......@@ -716,11 +1028,11 @@ static RPC_STATUS RPCRT4_ValidateCommonHeader(const RpcPktCommonHdr *hdr)
}
/***********************************************************************
* RPCRT4_receive_fragment (internal)
* RPCRT4_default_receive_fragment (internal)
*
* Receive a fragment from a connection.
*/
static RPC_STATUS RPCRT4_receive_fragment(RpcConnection *Connection, RpcPktHdr **Header, void **Payload)
RPC_STATUS RPCRT4_default_receive_fragment(RpcConnection *Connection, RpcPktHdr **Header, void **Payload)
{
RPC_STATUS status;
DWORD hdr_length;
......@@ -794,6 +1106,14 @@ fail:
return status;
}
static RPC_STATUS RPCRT4_receive_fragment(RpcConnection *Connection, RpcPktHdr **Header, void **Payload)
{
if (Connection->ops->receive_fragment)
return Connection->ops->receive_fragment(Connection, Header, Payload);
else
return RPCRT4_default_receive_fragment(Connection, Header, Payload);
}
/***********************************************************************
* RPCRT4_ReceiveWithAuth (internal)
*
......
......@@ -30,10 +30,21 @@ RpcPktHdr *RPCRT4_BuildResponseHeader(unsigned long DataRepresentation, unsigned
RpcPktHdr *RPCRT4_BuildBindHeader(unsigned long DataRepresentation, unsigned short MaxTransmissionSize, unsigned short MaxReceiveSize, unsigned long AssocGroupId, const RPC_SYNTAX_IDENTIFIER *AbstractId, const RPC_SYNTAX_IDENTIFIER *TransferId);
RpcPktHdr *RPCRT4_BuildBindNackHeader(unsigned long DataRepresentation, unsigned char RpcVersion, unsigned char RpcVersionMinor);
RpcPktHdr *RPCRT4_BuildBindAckHeader(unsigned long DataRepresentation, unsigned short MaxTransmissionSize, unsigned short MaxReceiveSize, unsigned long AssocGroupId, LPCSTR ServerAddress, unsigned long Result, unsigned long Reason, const RPC_SYNTAX_IDENTIFIER *TransferId);
RpcPktHdr *RPCRT4_BuildHttpHeader(unsigned long DataRepresentation, unsigned short flags, unsigned short num_data_items, unsigned int payload_size);
RpcPktHdr *RPCRT4_BuildHttpConnectHeader(unsigned short flags, int out_pipe, const UUID *connection_uuid, const UUID *pipe_uuid, const UUID *association_uuid);
RpcPktHdr *RPCRT4_BuildHttpFlowControlHeader(BOOL server, ULONG bytes_transmitted, ULONG flow_control_increment, const UUID *pipe_uuid);
VOID RPCRT4_FreeHeader(RpcPktHdr *Header);
RPC_STATUS RPCRT4_Send(RpcConnection *Connection, RpcPktHdr *Header, void *Buffer, unsigned int BufferLength);
RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, PRPC_MESSAGE pMsg);
RPC_STATUS RPCRT4_ReceiveWithAuth(RpcConnection *Connection, RpcPktHdr **Header, PRPC_MESSAGE pMsg, unsigned char **auth_data_out, unsigned long *auth_length_out);
DWORD RPCRT4_GetHeaderSize(const RpcPktHdr *Header);
RPC_STATUS RPCRT4_ValidateCommonHeader(const RpcPktCommonHdr *hdr);
BOOL RPCRT4_IsValidHttpPacket(RpcPktHdr *hdr, unsigned char *data, unsigned short data_len);
unsigned char *RPCRT4_NextHttpHeaderField(unsigned char *data);
RPC_STATUS RPCRT4_ParseHttpPrepareHeader1(RpcPktHdr *header, unsigned char *data, ULONG *field1);
RPC_STATUS RPCRT4_ParseHttpPrepareHeader2(RpcPktHdr *header, unsigned char *data, ULONG *field1, ULONG *bytes_until_next_packet, ULONG *field3);
RPC_STATUS RPCRT4_ParseHttpFlowControlHeader(RpcPktHdr *header, unsigned char *data, BOOL server, ULONG *bytes_transmitted, ULONG *flow_control_increment, UUID *pipe_uuid);
NCA_STATUS RPC2NCA_STATUS(RPC_STATUS status);
RPC_STATUS RPCRT4_AuthorizeConnection(RpcConnection* conn, BYTE *challenge, ULONG count);
......
......@@ -71,6 +71,7 @@
#include "winbase.h"
#include "winnls.h"
#include "winerror.h"
#include "wininet.h"
#include "winternl.h"
#include "wine/unicode.h"
......@@ -80,6 +81,7 @@
#include "wine/debug.h"
#include "rpc_binding.h"
#include "rpc_assoc.h"
#include "rpc_message.h"
#include "rpc_server.h"
#include "epm_towers.h"
......@@ -88,6 +90,8 @@
# define SOL_TCP IPPROTO_TCP
#endif
#define DEFAULT_NCACN_HTTP_TIMEOUT (60 * 1000)
WINE_DEFAULT_DEBUG_CHANNEL(rpc);
static RPC_STATUS RPCRT4_SpawnConnection(RpcConnection** Connection, RpcConnection* OldConnection);
......@@ -1126,9 +1130,10 @@ static int rpcrt4_conn_tcp_wait_for_incoming_data(RpcConnection *Connection)
return 0;
}
static size_t rpcrt4_ncacn_ip_tcp_get_top_of_tower(unsigned char *tower_data,
const char *networkaddr,
const char *endpoint)
static size_t rpcrt4_ip_tcp_get_top_of_tower(unsigned char *tower_data,
const char *networkaddr,
unsigned char tcp_protid,
const char *endpoint)
{
twr_tcp_floor_t *tcp_floor;
twr_ipv4_floor_t *ipv4_floor;
......@@ -1148,7 +1153,7 @@ static size_t rpcrt4_ncacn_ip_tcp_get_top_of_tower(unsigned char *tower_data,
ipv4_floor = (twr_ipv4_floor_t *)tower_data;
tcp_floor->count_lhs = sizeof(tcp_floor->protid);
tcp_floor->protid = EPM_PROTOCOL_TCP;
tcp_floor->protid = tcp_protid;
tcp_floor->count_rhs = sizeof(tcp_floor->port);
ipv4_floor->count_lhs = sizeof(ipv4_floor->protid);
......@@ -1193,10 +1198,19 @@ static size_t rpcrt4_ncacn_ip_tcp_get_top_of_tower(unsigned char *tower_data,
return size;
}
static RPC_STATUS rpcrt4_ncacn_ip_tcp_parse_top_of_tower(const unsigned char *tower_data,
size_t tower_size,
char **networkaddr,
char **endpoint)
static size_t rpcrt4_ncacn_ip_tcp_get_top_of_tower(unsigned char *tower_data,
const char *networkaddr,
const char *endpoint)
{
return rpcrt4_ip_tcp_get_top_of_tower(tower_data, networkaddr,
EPM_PROTOCOL_TCP, endpoint);
}
static RPC_STATUS rpcrt4_ip_tcp_parse_top_of_tower(const unsigned char *tower_data,
size_t tower_size,
char **networkaddr,
unsigned char tcp_protid,
char **endpoint)
{
const twr_tcp_floor_t *tcp_floor = (const twr_tcp_floor_t *)tower_data;
const twr_ipv4_floor_t *ipv4_floor;
......@@ -1216,7 +1230,7 @@ static RPC_STATUS rpcrt4_ncacn_ip_tcp_parse_top_of_tower(const unsigned char *to
ipv4_floor = (const twr_ipv4_floor_t *)tower_data;
if ((tcp_floor->count_lhs != sizeof(tcp_floor->protid)) ||
(tcp_floor->protid != EPM_PROTOCOL_TCP) ||
(tcp_floor->protid != tcp_protid) ||
(tcp_floor->count_rhs != sizeof(tcp_floor->port)) ||
(ipv4_floor->count_lhs != sizeof(ipv4_floor->protid)) ||
(ipv4_floor->protid != EPM_PROTOCOL_IP) ||
......@@ -1402,6 +1416,949 @@ static int rpcrt4_protseq_sock_wait_for_new_connection(RpcServerProtseq *protseq
#endif /* HAVE_SOCKETPAIR */
static RPC_STATUS rpcrt4_ncacn_ip_tcp_parse_top_of_tower(const unsigned char *tower_data,
size_t tower_size,
char **networkaddr,
char **endpoint)
{
return rpcrt4_ip_tcp_parse_top_of_tower(tower_data, tower_size,
networkaddr, EPM_PROTOCOL_TCP,
endpoint);
}
/**** ncacn_http support ****/
/* 60 seconds is the period native uses */
#define HTTP_IDLE_TIME 60000
/* reference counted to avoid a race between a cancelled call's connection
* being destroyed and the asynchronous InternetReadFileEx call being
* completed */
typedef struct _RpcHttpAsyncData
{
LONG refs;
HANDLE completion_event;
INTERNET_BUFFERSA inet_buffers;
void *destination_buffer; /* the address that inet_buffers.lpvBuffer will be
* copied into when the call completes */
CRITICAL_SECTION cs;
} RpcHttpAsyncData;
static ULONG RpcHttpAsyncData_AddRef(RpcHttpAsyncData *data)
{
return InterlockedIncrement(&data->refs);
}
static ULONG RpcHttpAsyncData_Release(RpcHttpAsyncData *data)
{
ULONG refs = InterlockedDecrement(&data->refs);
if (!refs)
{
TRACE("destroying async data %p\n", data);
CloseHandle(data->completion_event);
HeapFree(GetProcessHeap(), 0, data->inet_buffers.lpvBuffer);
DeleteCriticalSection(&data->cs);
HeapFree(GetProcessHeap(), 0, data);
}
return refs;
}
typedef struct _RpcConnection_http
{
RpcConnection common;
HINTERNET app_info;
HINTERNET session;
HINTERNET in_request;
HINTERNET out_request;
HANDLE timer_cancelled;
HANDLE cancel_event;
DWORD last_sent_time;
ULONG bytes_received;
ULONG flow_control_mark; /* send a control packet to the server when this many bytes received */
ULONG flow_control_increment; /* number of bytes to increment flow_control_mark by */
UUID connection_uuid;
UUID in_pipe_uuid;
UUID out_pipe_uuid;
RpcHttpAsyncData *async_data;
} RpcConnection_http;
static RpcConnection *rpcrt4_ncacn_http_alloc(void)
{
RpcConnection_http *httpc;
httpc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*httpc));
if (!httpc) return NULL;
httpc->async_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RpcHttpAsyncData));
if (!httpc->async_data)
{
HeapFree(GetProcessHeap(), 0, httpc);
return NULL;
}
TRACE("async data = %p\n", httpc->async_data);
httpc->cancel_event = CreateEventW(NULL, FALSE, FALSE, NULL);
httpc->async_data->refs = 1;
httpc->async_data->inet_buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
httpc->async_data->inet_buffers.lpvBuffer = NULL;
httpc->async_data->destination_buffer = NULL;
InitializeCriticalSection(&httpc->async_data->cs);
return &httpc->common;
}
typedef struct _HttpTimerThreadData
{
PVOID timer_param;
DWORD *last_sent_time;
HANDLE timer_cancelled;
} HttpTimerThreadData;
static VOID CALLBACK rpcrt4_http_keep_connection_active_timer_proc(PVOID param, BOOLEAN dummy)
{
HINTERNET in_request = param;
RpcPktHdr *idle_pkt;
idle_pkt = RPCRT4_BuildHttpHeader(NDR_LOCAL_DATA_REPRESENTATION, 0x0001,
0, 0);
if (idle_pkt)
{
DWORD bytes_written;
InternetWriteFile(in_request, idle_pkt, idle_pkt->common.frag_len, &bytes_written);
RPCRT4_FreeHeader(idle_pkt);
}
}
static inline DWORD rpcrt4_http_timer_calc_timeout(DWORD *last_sent_time)
{
DWORD cur_time = GetTickCount();
DWORD cached_last_sent_time = *last_sent_time;
return HTTP_IDLE_TIME - (cur_time - cached_last_sent_time > HTTP_IDLE_TIME ? 0 : cur_time - cached_last_sent_time);
}
static DWORD CALLBACK rpcrt4_http_timer_thread(PVOID param)
{
HttpTimerThreadData *data_in = param;
HttpTimerThreadData data;
DWORD timeout;
data = *data_in;
HeapFree(GetProcessHeap(), 0, data_in);
for (timeout = HTTP_IDLE_TIME;
WaitForSingleObject(data.timer_cancelled, timeout) == WAIT_TIMEOUT;
timeout = rpcrt4_http_timer_calc_timeout(data.last_sent_time))
{
/* are we too soon after last send? */
if (GetTickCount() - HTTP_IDLE_TIME < *data.last_sent_time)
continue;
rpcrt4_http_keep_connection_active_timer_proc(data.timer_param, TRUE);
}
CloseHandle(data.timer_cancelled);
return 0;
}
static VOID WINAPI rpcrt4_http_internet_callback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
RpcHttpAsyncData *async_data = (RpcHttpAsyncData *)dwContext;
switch (dwInternetStatus)
{
case INTERNET_STATUS_REQUEST_COMPLETE:
TRACE("INTERNET_STATUS_REQUEST_COMPLETED\n");
if (async_data)
{
if (async_data->inet_buffers.lpvBuffer)
{
EnterCriticalSection(&async_data->cs);
if (async_data->destination_buffer)
{
memcpy(async_data->destination_buffer,
async_data->inet_buffers.lpvBuffer,
async_data->inet_buffers.dwBufferLength);
async_data->destination_buffer = NULL;
}
LeaveCriticalSection(&async_data->cs);
}
HeapFree(GetProcessHeap(), 0, async_data->inet_buffers.lpvBuffer);
async_data->inet_buffers.lpvBuffer = NULL;
SetEvent(async_data->completion_event);
RpcHttpAsyncData_Release(async_data);
}
break;
}
}
static RPC_STATUS rpcrt4_http_check_response(HINTERNET hor)
{
BOOL ret;
DWORD status_code;
DWORD size;
DWORD index;
WCHAR status_text[32];
TRACE("\n");
index = 0;
size = sizeof(status_code);
ret = HttpQueryInfoW(hor, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &status_code, &size, &index);
if (!ret)
return GetLastError();
if (status_code < 400)
return RPC_S_OK;
index = 0;
size = sizeof(status_text);
ret = HttpQueryInfoW(hor, HTTP_QUERY_STATUS_TEXT, status_text, &size, &index);
if (!ret)
return GetLastError();
ERR("server returned: %d %s\n", status_code, debugstr_w(status_text));
if (status_code == HTTP_STATUS_DENIED)
return ERROR_ACCESS_DENIED;
return RPC_S_SERVER_UNAVAILABLE;
}
static RPC_STATUS rpcrt4_http_internet_connect(RpcConnection_http *httpc)
{
static const WCHAR wszUserAgent[] = {'M','S','R','P','C',0};
LPWSTR proxy = NULL;
LPWSTR user = NULL;
LPWSTR password = NULL;
LPWSTR servername = NULL;
const WCHAR *option;
INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER; /* use default port */
if (httpc->common.QOS &&
(httpc->common.QOS->qos->AdditionalSecurityInfoType == RPC_C_AUTHN_INFO_TYPE_HTTP))
{
const RPC_HTTP_TRANSPORT_CREDENTIALS_W *http_cred = httpc->common.QOS->qos->u.HttpCredentials;
if (http_cred->TransportCredentials)
{
WCHAR *p;
const SEC_WINNT_AUTH_IDENTITY_W *cred = http_cred->TransportCredentials;
ULONG len = cred->DomainLength + 1 + cred->UserLength;
user = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
if (!user)
return RPC_S_OUT_OF_RESOURCES;
p = user;
if (cred->DomainLength)
{
memcpy(p, cred->Domain, cred->DomainLength * sizeof(WCHAR));
p += cred->DomainLength;
*p = '\\';
p++;
}
memcpy(p, cred->User, cred->UserLength * sizeof(WCHAR));
p[cred->UserLength] = 0;
password = RPCRT4_strndupW(cred->Password, cred->PasswordLength);
}
}
for (option = httpc->common.NetworkOptions; option;
option = (strchrW(option, ',') ? strchrW(option, ',')+1 : NULL))
{
static const WCHAR wszRpcProxy[] = {'R','p','c','P','r','o','x','y','=',0};
static const WCHAR wszHttpProxy[] = {'H','t','t','p','P','r','o','x','y','=',0};
if (!strncmpiW(option, wszRpcProxy, sizeof(wszRpcProxy)/sizeof(wszRpcProxy[0])-1))
{
const WCHAR *value_start = option + sizeof(wszRpcProxy)/sizeof(wszRpcProxy[0])-1;
const WCHAR *value_end;
const WCHAR *p;
value_end = strchrW(option, ',');
if (!value_end)
value_end = value_start + strlenW(value_start);
for (p = value_start; p < value_end; p++)
if (*p == ':')
{
port = atoiW(p+1);
value_end = p;
break;
}
TRACE("RpcProxy value is %s\n", debugstr_wn(value_start, value_end-value_start));
servername = RPCRT4_strndupW(value_start, value_end-value_start);
}
else if (!strncmpiW(option, wszHttpProxy, sizeof(wszHttpProxy)/sizeof(wszHttpProxy[0])-1))
{
const WCHAR *value_start = option + sizeof(wszHttpProxy)/sizeof(wszHttpProxy[0])-1;
const WCHAR *value_end;
value_end = strchrW(option, ',');
if (!value_end)
value_end = value_start + strlenW(value_start);
TRACE("HttpProxy value is %s\n", debugstr_wn(value_start, value_end-value_start));
proxy = RPCRT4_strndupW(value_start, value_end-value_start);
}
else
FIXME("unhandled option %s\n", debugstr_w(option));
}
httpc->app_info = InternetOpenW(wszUserAgent, proxy ? INTERNET_OPEN_TYPE_PROXY : INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, INTERNET_FLAG_ASYNC);
if (!httpc->app_info)
{
HeapFree(GetProcessHeap(), 0, password);
HeapFree(GetProcessHeap(), 0, user);
ERR("InternetOpenW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
InternetSetStatusCallbackW(httpc->app_info, rpcrt4_http_internet_callback);
/* if no RpcProxy option specified, set the HTTP server address to the
* RPC server address */
if (!servername)
{
servername = HeapAlloc(GetProcessHeap(), 0, (strlen(httpc->common.NetworkAddr) + 1)*sizeof(WCHAR));
if (!servername)
{
HeapFree(GetProcessHeap(), 0, password);
HeapFree(GetProcessHeap(), 0, user);
return RPC_S_OUT_OF_RESOURCES;
}
MultiByteToWideChar(CP_ACP, 0, httpc->common.NetworkAddr, -1, servername, strlen(httpc->common.NetworkAddr) + 1);
}
httpc->session = InternetConnectW(httpc->app_info, servername, port, user, password,
INTERNET_SERVICE_HTTP, 0, 0);
HeapFree(GetProcessHeap(), 0, password);
HeapFree(GetProcessHeap(), 0, user);
HeapFree(GetProcessHeap(), 0, servername);
if (!httpc->session)
{
ERR("InternetConnectW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
return RPC_S_OK;
}
/* prepare the in pipe for use by RPC packets */
static RPC_STATUS rpcrt4_http_prepare_in_pipe(HINTERNET in_request, RpcHttpAsyncData *async_data,
const UUID *connection_uuid,
const UUID *in_pipe_uuid,
const UUID *association_uuid)
{
BYTE packet[44];
BOOL ret;
RPC_STATUS status;
RpcPktHdr *hdr;
INTERNET_BUFFERSW buffers_in;
DWORD bytes_read, bytes_written;
/* prepare in pipe */
ResetEvent(async_data->completion_event);
RpcHttpAsyncData_AddRef(async_data);
ret = HttpSendRequestW(in_request, NULL, 0, NULL, 0);
if (!ret)
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(async_data->completion_event, INFINITE);
else
{
RpcHttpAsyncData_Release(async_data);
ERR("HttpSendRequestW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
}
status = rpcrt4_http_check_response(in_request);
if (status != RPC_S_OK) return status;
InternetReadFile(in_request, packet, 20, &bytes_read);
/* FIXME: do something with retrieved data */
memset(&buffers_in, 0, sizeof(buffers_in));
buffers_in.dwStructSize = sizeof(buffers_in);
/* FIXME: get this from the registry */
buffers_in.dwBufferTotal = 1024 * 1024 * 1024; /* 1Gb */
ResetEvent(async_data->completion_event);
RpcHttpAsyncData_AddRef(async_data);
ret = HttpSendRequestExW(in_request, &buffers_in, NULL, 0, 0);
if (!ret)
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(async_data->completion_event, INFINITE);
else
{
RpcHttpAsyncData_Release(async_data);
ERR("HttpSendRequestExW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
}
TRACE("sending HTTP connect header to server\n");
hdr = RPCRT4_BuildHttpConnectHeader(0, FALSE, connection_uuid, in_pipe_uuid, association_uuid);
if (!hdr) return RPC_S_OUT_OF_RESOURCES;
ret = InternetWriteFile(in_request, hdr, hdr->common.frag_len, &bytes_written);
RPCRT4_FreeHeader(hdr);
if (!ret)
{
ERR("InternetWriteFile failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
return RPC_S_OK;
}
static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcPktHdr *hdr, BYTE **data)
{
BOOL ret;
DWORD bytes_read;
unsigned short data_len;
ret = InternetReadFile(request, hdr, sizeof(hdr->common), &bytes_read);
if (!ret)
return RPC_S_SERVER_UNAVAILABLE;
if (hdr->common.ptype != PKT_HTTP || hdr->common.frag_len < sizeof(hdr->http))
{
ERR("wrong packet type received %d or wrong frag_len %d\n",
hdr->common.ptype, hdr->common.frag_len);
return RPC_S_PROTOCOL_ERROR;
}
ret = InternetReadFile(request, &hdr->common + 1, sizeof(hdr->http) - sizeof(hdr->common), &bytes_read);
if (!ret)
return RPC_S_SERVER_UNAVAILABLE;
data_len = hdr->common.frag_len - sizeof(hdr->http);
if (data_len)
{
*data = HeapAlloc(GetProcessHeap(), 0, data_len);
if (!*data)
return RPC_S_OUT_OF_RESOURCES;
ret = InternetReadFile(request, *data, data_len, &bytes_read);
if (!ret)
{
HeapFree(GetProcessHeap(), 0, *data);
return RPC_S_SERVER_UNAVAILABLE;
}
}
else
*data = NULL;
if (!RPCRT4_IsValidHttpPacket(hdr, *data, data_len))
{
ERR("invalid http packet\n");
return RPC_S_PROTOCOL_ERROR;
}
return RPC_S_OK;
}
/* prepare the out pipe for use by RPC packets */
static RPC_STATUS rpcrt4_http_prepare_out_pipe(HINTERNET out_request,
RpcHttpAsyncData *async_data,
const UUID *connection_uuid,
const UUID *out_pipe_uuid,
ULONG *flow_control_increment)
{
BYTE packet[20];
BOOL ret;
RPC_STATUS status;
RpcPktHdr *hdr;
DWORD bytes_read;
BYTE *data_from_server;
RpcPktHdr pkt_from_server;
ULONG field1, field3;
ResetEvent(async_data->completion_event);
RpcHttpAsyncData_AddRef(async_data);
ret = HttpSendRequestW(out_request, NULL, 0, NULL, 0);
if (!ret)
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(async_data->completion_event, INFINITE);
else
{
RpcHttpAsyncData_Release(async_data);
ERR("HttpSendRequestW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
}
status = rpcrt4_http_check_response(out_request);
if (status != RPC_S_OK) return status;
InternetReadFile(out_request, packet, 20, &bytes_read);
/* FIXME: do something with retrieved data */
hdr = RPCRT4_BuildHttpConnectHeader(0, TRUE, connection_uuid, out_pipe_uuid, NULL);
if (!hdr) return RPC_S_OUT_OF_RESOURCES;
ResetEvent(async_data->completion_event);
RpcHttpAsyncData_AddRef(async_data);
ret = HttpSendRequestW(out_request, NULL, 0, hdr, hdr->common.frag_len);
if (!ret)
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(async_data->completion_event, INFINITE);
else
{
RpcHttpAsyncData_Release(async_data);
ERR("HttpSendRequestW failed with error %d\n", GetLastError());
RPCRT4_FreeHeader(hdr);
return RPC_S_SERVER_UNAVAILABLE;
}
}
RPCRT4_FreeHeader(hdr);
status = rpcrt4_http_check_response(out_request);
if (status != RPC_S_OK) return status;
status = rpcrt4_http_read_http_packet(out_request, &pkt_from_server,
&data_from_server);
if (status != RPC_S_OK) return status;
status = RPCRT4_ParseHttpPrepareHeader1(&pkt_from_server, data_from_server,
&field1);
HeapFree(GetProcessHeap(), 0, data_from_server);
if (status != RPC_S_OK) return status;
TRACE("received (%d) from first prepare header\n", field1);
status = rpcrt4_http_read_http_packet(out_request, &pkt_from_server,
&data_from_server);
if (status != RPC_S_OK) return status;
status = RPCRT4_ParseHttpPrepareHeader2(&pkt_from_server, data_from_server,
&field1, flow_control_increment,
&field3);
HeapFree(GetProcessHeap(), 0, data_from_server);
if (status != RPC_S_OK) return status;
TRACE("received (0x%08x 0x%08x %d) from second prepare header\n", field1, *flow_control_increment, field3);
return RPC_S_OK;
}
static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
{
RpcConnection_http *httpc = (RpcConnection_http *)Connection;
static const WCHAR wszVerbIn[] = {'R','P','C','_','I','N','_','D','A','T','A',0};
static const WCHAR wszVerbOut[] = {'R','P','C','_','O','U','T','_','D','A','T','A',0};
static const WCHAR wszRpcProxyPrefix[] = {'/','r','p','c','/','r','p','c','p','r','o','x','y','.','d','l','l','?',0};
static const WCHAR wszColon[] = {':',0};
static const WCHAR wszAcceptType[] = {'a','p','p','l','i','c','a','t','i','o','n','/','r','p','c',0};
LPCWSTR wszAcceptTypes[] = { wszAcceptType, NULL };
WCHAR *url;
RPC_STATUS status;
BOOL secure;
HttpTimerThreadData *timer_data;
HANDLE thread;
TRACE("(%s, %s)\n", Connection->NetworkAddr, Connection->Endpoint);
if (Connection->server)
{
ERR("ncacn_http servers not supported yet\n");
return RPC_S_SERVER_UNAVAILABLE;
}
if (httpc->in_request)
return RPC_S_OK;
httpc->async_data->completion_event = CreateEventW(NULL, FALSE, FALSE, NULL);
status = UuidCreate(&httpc->connection_uuid);
status = UuidCreate(&httpc->in_pipe_uuid);
status = UuidCreate(&httpc->out_pipe_uuid);
status = rpcrt4_http_internet_connect(httpc);
if (status != RPC_S_OK)
return status;
url = HeapAlloc(GetProcessHeap(), 0, sizeof(wszRpcProxyPrefix) + (strlen(Connection->NetworkAddr) + 1 + strlen(Connection->Endpoint))*sizeof(WCHAR));
if (!url)
return RPC_S_OUT_OF_MEMORY;
memcpy(url, wszRpcProxyPrefix, sizeof(wszRpcProxyPrefix));
MultiByteToWideChar(CP_ACP, 0, Connection->NetworkAddr, -1, url+sizeof(wszRpcProxyPrefix)/sizeof(wszRpcProxyPrefix[0])-1, strlen(Connection->NetworkAddr)+1);
strcatW(url, wszColon);
MultiByteToWideChar(CP_ACP, 0, Connection->Endpoint, -1, url+strlenW(url), strlen(Connection->Endpoint)+1);
secure = httpc->common.QOS &&
(httpc->common.QOS->qos->AdditionalSecurityInfoType == RPC_C_AUTHN_INFO_TYPE_HTTP) &&
(httpc->common.QOS->qos->u.HttpCredentials->Flags & RPC_C_HTTP_FLAG_USE_SSL);
httpc->in_request = HttpOpenRequestW(httpc->session, wszVerbIn, url, NULL, NULL,
wszAcceptTypes,
(secure ? INTERNET_FLAG_SECURE : 0)|INTERNET_FLAG_KEEP_CONNECTION|INTERNET_FLAG_PRAGMA_NOCACHE,
(DWORD_PTR)httpc->async_data);
if (!httpc->in_request)
{
ERR("HttpOpenRequestW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
httpc->out_request = HttpOpenRequestW(httpc->session, wszVerbOut, url, NULL, NULL,
wszAcceptTypes,
(secure ? INTERNET_FLAG_SECURE : 0)|INTERNET_FLAG_KEEP_CONNECTION|INTERNET_FLAG_PRAGMA_NOCACHE,
(DWORD_PTR)httpc->async_data);
if (!httpc->out_request)
{
ERR("HttpOpenRequestW failed with error %d\n", GetLastError());
return RPC_S_SERVER_UNAVAILABLE;
}
status = rpcrt4_http_prepare_in_pipe(httpc->in_request,
httpc->async_data,
&httpc->connection_uuid,
&httpc->in_pipe_uuid,
&Connection->assoc->http_uuid);
if (status != RPC_S_OK)
return status;
status = rpcrt4_http_prepare_out_pipe(httpc->out_request,
httpc->async_data,
&httpc->connection_uuid,
&httpc->out_pipe_uuid,
&httpc->flow_control_increment);
if (status != RPC_S_OK)
return status;
httpc->flow_control_mark = httpc->flow_control_increment / 2;
httpc->last_sent_time = GetTickCount();
httpc->timer_cancelled = CreateEventW(NULL, FALSE, FALSE, NULL);
timer_data = HeapAlloc(GetProcessHeap(), 0, sizeof(*timer_data));
if (!timer_data)
return ERROR_OUTOFMEMORY;
timer_data->timer_param = httpc->in_request;
timer_data->last_sent_time = &httpc->last_sent_time;
timer_data->timer_cancelled = httpc->timer_cancelled;
/* FIXME: should use CreateTimerQueueTimer when implemented */
thread = CreateThread(NULL, 0, rpcrt4_http_timer_thread, timer_data, 0, NULL);
if (!thread)
{
HeapFree(GetProcessHeap(), 0, timer_data);
return GetLastError();
}
CloseHandle(thread);
return RPC_S_OK;
}
static RPC_STATUS rpcrt4_ncacn_http_handoff(RpcConnection *old_conn, RpcConnection *new_conn)
{
assert(0);
return RPC_S_SERVER_UNAVAILABLE;
}
static int rpcrt4_ncacn_http_read(RpcConnection *Connection,
void *buffer, unsigned int count)
{
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
char *buf = buffer;
BOOL ret = TRUE;
unsigned int bytes_left = count;
ResetEvent(httpc->async_data->completion_event);
while (bytes_left)
{
RpcHttpAsyncData_AddRef(httpc->async_data);
httpc->async_data->inet_buffers.dwBufferLength = bytes_left;
httpc->async_data->inet_buffers.lpvBuffer = HeapAlloc(GetProcessHeap(), 0, bytes_left);
httpc->async_data->destination_buffer = buf;
ret = InternetReadFileExA(httpc->out_request, &httpc->async_data->inet_buffers, IRF_ASYNC, 0);
if (ret)
{
/* INTERNET_STATUS_REQUEST_COMPLETED won't be sent, so release our
* async ref now */
RpcHttpAsyncData_Release(httpc->async_data);
memcpy(buf, httpc->async_data->inet_buffers.lpvBuffer,
httpc->async_data->inet_buffers.dwBufferLength);
HeapFree(GetProcessHeap(), 0, httpc->async_data->inet_buffers.lpvBuffer);
httpc->async_data->inet_buffers.lpvBuffer = NULL;
httpc->async_data->destination_buffer = NULL;
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
HANDLE handles[2] = { httpc->async_data->completion_event, httpc->cancel_event };
DWORD result = WaitForMultipleObjects(2, handles, FALSE, DEFAULT_NCACN_HTTP_TIMEOUT);
if (result == WAIT_OBJECT_0)
ret = TRUE;
else
{
TRACE("call cancelled\n");
EnterCriticalSection(&httpc->async_data->cs);
httpc->async_data->destination_buffer = NULL;
LeaveCriticalSection(&httpc->async_data->cs);
break;
}
}
else
{
HeapFree(GetProcessHeap(), 0, httpc->async_data->inet_buffers.lpvBuffer);
httpc->async_data->inet_buffers.lpvBuffer = NULL;
httpc->async_data->destination_buffer = NULL;
RpcHttpAsyncData_Release(httpc->async_data);
break;
}
}
if (!httpc->async_data->inet_buffers.dwBufferLength)
break;
bytes_left -= httpc->async_data->inet_buffers.dwBufferLength;
buf += httpc->async_data->inet_buffers.dwBufferLength;
}
TRACE("%p %p %u -> %s\n", httpc->out_request, buffer, count, ret ? "TRUE" : "FALSE");
return ret ? count : -1;
}
static RPC_STATUS rpcrt4_ncacn_http_receive_fragment(RpcConnection *Connection, RpcPktHdr **Header, void **Payload)
{
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
RPC_STATUS status;
DWORD hdr_length;
LONG dwRead;
RpcPktCommonHdr common_hdr;
*Header = NULL;
TRACE("(%p, %p, %p)\n", Connection, Header, Payload);
again:
/* read packet common header */
dwRead = rpcrt4_ncacn_http_read(Connection, &common_hdr, sizeof(common_hdr));
if (dwRead != sizeof(common_hdr)) {
WARN("Short read of header, %d bytes\n", dwRead);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
if (!memcmp(&common_hdr, "HTTP/1.1", sizeof("HTTP/1.1")) ||
!memcmp(&common_hdr, "HTTP/1.0", sizeof("HTTP/1.0")))
{
FIXME("server returned %s\n", debugstr_a((const char *)&common_hdr));
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
status = RPCRT4_ValidateCommonHeader(&common_hdr);
if (status != RPC_S_OK) goto fail;
hdr_length = RPCRT4_GetHeaderSize((RpcPktHdr*)&common_hdr);
if (hdr_length == 0) {
WARN("header length == 0\n");
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
*Header = HeapAlloc(GetProcessHeap(), 0, hdr_length);
if (!*Header)
{
status = RPC_S_OUT_OF_RESOURCES;
goto fail;
}
memcpy(*Header, &common_hdr, sizeof(common_hdr));
/* read the rest of packet header */
dwRead = rpcrt4_ncacn_http_read(Connection, &(*Header)->common + 1, hdr_length - sizeof(common_hdr));
if (dwRead != hdr_length - sizeof(common_hdr)) {
WARN("bad header length, %d bytes, hdr_length %d\n", dwRead, hdr_length);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
if (common_hdr.frag_len - hdr_length)
{
*Payload = HeapAlloc(GetProcessHeap(), 0, common_hdr.frag_len - hdr_length);
if (!*Payload)
{
status = RPC_S_OUT_OF_RESOURCES;
goto fail;
}
dwRead = rpcrt4_ncacn_http_read(Connection, *Payload, common_hdr.frag_len - hdr_length);
if (dwRead != common_hdr.frag_len - hdr_length)
{
WARN("bad data length, %d/%d\n", dwRead, common_hdr.frag_len - hdr_length);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
}
else
*Payload = NULL;
if ((*Header)->common.ptype == PKT_HTTP)
{
if (!RPCRT4_IsValidHttpPacket(*Header, *Payload, common_hdr.frag_len - hdr_length))
{
ERR("invalid http packet of length %d bytes\n", (*Header)->common.frag_len);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
if ((*Header)->http.flags == 0x0001)
{
TRACE("http idle packet, waiting for real packet\n");
if ((*Header)->http.num_data_items != 0)
{
ERR("HTTP idle packet should have no data items instead of %d\n", (*Header)->http.num_data_items);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
}
else if ((*Header)->http.flags == 0x0002)
{
ULONG bytes_transmitted;
ULONG flow_control_increment;
UUID pipe_uuid;
status = RPCRT4_ParseHttpFlowControlHeader(*Header, *Payload,
Connection->server,
&bytes_transmitted,
&flow_control_increment,
&pipe_uuid);
if (status != RPC_S_OK)
goto fail;
TRACE("received http flow control header (0x%x, 0x%x, %s)\n",
bytes_transmitted, flow_control_increment, debugstr_guid(&pipe_uuid));
/* FIXME: do something with parsed data */
}
else
{
FIXME("unrecognised http packet with flags 0x%04x\n", (*Header)->http.flags);
status = RPC_S_PROTOCOL_ERROR;
goto fail;
}
RPCRT4_FreeHeader(*Header);
*Header = NULL;
HeapFree(GetProcessHeap(), 0, *Payload);
*Payload = NULL;
goto again;
}
/* success */
status = RPC_S_OK;
httpc->bytes_received += common_hdr.frag_len;
TRACE("httpc->bytes_received = 0x%x\n", httpc->bytes_received);
if (httpc->bytes_received > httpc->flow_control_mark)
{
RpcPktHdr *hdr = RPCRT4_BuildHttpFlowControlHeader(httpc->common.server,
httpc->bytes_received,
httpc->flow_control_increment,
&httpc->out_pipe_uuid);
if (hdr)
{
DWORD bytes_written;
BOOL ret2;
TRACE("sending flow control packet at 0x%x\n", httpc->bytes_received);
ret2 = InternetWriteFile(httpc->in_request, hdr, hdr->common.frag_len, &bytes_written);
RPCRT4_FreeHeader(hdr);
if (ret2)
httpc->flow_control_mark = httpc->bytes_received + httpc->flow_control_increment / 2;
}
}
fail:
if (status != RPC_S_OK) {
RPCRT4_FreeHeader(*Header);
*Header = NULL;
HeapFree(GetProcessHeap(), 0, *Payload);
*Payload = NULL;
}
return status;
}
static int rpcrt4_ncacn_http_write(RpcConnection *Connection,
const void *buffer, unsigned int count)
{
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
DWORD bytes_written;
BOOL ret;
httpc->last_sent_time = ~0UL; /* disable idle packet sending */
ret = InternetWriteFile(httpc->in_request, buffer, count, &bytes_written);
httpc->last_sent_time = GetTickCount();
TRACE("%p %p %u -> %s\n", httpc->in_request, buffer, count, ret ? "TRUE" : "FALSE");
return ret ? bytes_written : -1;
}
static int rpcrt4_ncacn_http_close(RpcConnection *Connection)
{
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
TRACE("\n");
SetEvent(httpc->timer_cancelled);
if (httpc->in_request)
InternetCloseHandle(httpc->in_request);
httpc->in_request = NULL;
if (httpc->out_request)
InternetCloseHandle(httpc->out_request);
httpc->out_request = NULL;
if (httpc->app_info)
InternetCloseHandle(httpc->app_info);
httpc->app_info = NULL;
if (httpc->session)
InternetCloseHandle(httpc->session);
httpc->session = NULL;
RpcHttpAsyncData_Release(httpc->async_data);
if (httpc->cancel_event)
CloseHandle(httpc->cancel_event);
return 0;
}
static void rpcrt4_ncacn_http_cancel_call(RpcConnection *Connection)
{
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
SetEvent(httpc->cancel_event);
}
static int rpcrt4_ncacn_http_wait_for_incoming_data(RpcConnection *Connection)
{
BOOL ret;
RpcConnection_http *httpc = (RpcConnection_http *) Connection;
RpcHttpAsyncData_AddRef(httpc->async_data);
ret = InternetQueryDataAvailable(httpc->out_request,
&httpc->async_data->inet_buffers.dwBufferLength, IRF_ASYNC, 0);
if (ret)
{
/* INTERNET_STATUS_REQUEST_COMPLETED won't be sent, so release our
* async ref now */
RpcHttpAsyncData_Release(httpc->async_data);
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
HANDLE handles[2] = { httpc->async_data->completion_event, httpc->cancel_event };
DWORD result = WaitForMultipleObjects(2, handles, FALSE, DEFAULT_NCACN_HTTP_TIMEOUT);
if (result != WAIT_OBJECT_0)
{
TRACE("call cancelled\n");
return -1;
}
}
else
{
RpcHttpAsyncData_Release(httpc->async_data);
return -1;
}
}
/* success */
return 0;
}
static size_t rpcrt4_ncacn_http_get_top_of_tower(unsigned char *tower_data,
const char *networkaddr,
const char *endpoint)
{
return rpcrt4_ip_tcp_get_top_of_tower(tower_data, networkaddr,
EPM_PROTOCOL_HTTP, endpoint);
}
static RPC_STATUS rpcrt4_ncacn_http_parse_top_of_tower(const unsigned char *tower_data,
size_t tower_size,
char **networkaddr,
char **endpoint)
{
return rpcrt4_ip_tcp_parse_top_of_tower(tower_data, tower_size,
networkaddr, EPM_PROTOCOL_HTTP,
endpoint);
}
static const struct connection_ops conn_protseq_list[] = {
{ "ncacn_np",
{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_SMB },
......@@ -1415,6 +2372,7 @@ static const struct connection_ops conn_protseq_list[] = {
rpcrt4_conn_np_wait_for_incoming_data,
rpcrt4_ncacn_np_get_top_of_tower,
rpcrt4_ncacn_np_parse_top_of_tower,
NULL,
},
{ "ncalrpc",
{ EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_PIPE },
......@@ -1428,6 +2386,7 @@ static const struct connection_ops conn_protseq_list[] = {
rpcrt4_conn_np_wait_for_incoming_data,
rpcrt4_ncalrpc_get_top_of_tower,
rpcrt4_ncalrpc_parse_top_of_tower,
NULL,
},
#ifdef HAVE_SOCKETPAIR
{ "ncacn_ip_tcp",
......@@ -1442,8 +2401,23 @@ static const struct connection_ops conn_protseq_list[] = {
rpcrt4_conn_tcp_wait_for_incoming_data,
rpcrt4_ncacn_ip_tcp_get_top_of_tower,
rpcrt4_ncacn_ip_tcp_parse_top_of_tower,
}
NULL,
},
#endif
{ "ncacn_http",
{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_HTTP },
rpcrt4_ncacn_http_alloc,
rpcrt4_ncacn_http_open,
rpcrt4_ncacn_http_handoff,
rpcrt4_ncacn_http_read,
rpcrt4_ncacn_http_write,
rpcrt4_ncacn_http_close,
rpcrt4_ncacn_http_cancel_call,
rpcrt4_ncacn_http_wait_for_incoming_data,
rpcrt4_ncacn_http_get_top_of_tower,
rpcrt4_ncacn_http_parse_top_of_tower,
rpcrt4_ncacn_http_receive_fragment,
},
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment