[RPCRT4] Import Wine commit 01290cd by Colin and Christoph: Implement RpcBindingServe...
[reactos.git] / reactos / dll / win32 / rpcrt4 / rpc_transport.c
index f60ea9f..ee08f84 100644 (file)
@@ -79,6 +79,7 @@
 
 #define DEFAULT_NCACN_HTTP_TIMEOUT (60 * 1000)
 
+#undef ARRAYSIZE
 #define ARRAYSIZE(a) (sizeof((a)) / sizeof((a)[0]))
 
 WINE_DEFAULT_DEBUG_CHANNEL(rpc);
@@ -244,6 +245,18 @@ static RPC_STATUS rpcrt4_conn_open_pipe(RpcConnection *Connection, LPCSTR pname,
   return RPC_S_OK;
 }
 
+static char *ncalrpc_pipe_name(const char *endpoint)
+{
+  static const char prefix[] = "\\\\.\\pipe\\lrpc\\";
+  char *pipe_name;
+
+  /* protseq=ncalrpc: supposed to use NT LPC ports,
+   * but we'll implement it with named pipes for now */
+  pipe_name = I_RpcAllocate(sizeof(prefix) + strlen(endpoint));
+  strcat(strcpy(pipe_name, prefix), endpoint);
+  return pipe_name;
+}
+
 static RPC_STATUS rpcrt4_ncalrpc_open(RpcConnection* Connection)
 {
   RpcConnection_np *npc = (RpcConnection_np *) Connection;
@@ -303,13 +316,28 @@ static RPC_STATUS rpcrt4_protseq_ncalrpc_open_endpoint(RpcServerProtseq* protseq
   return r;
 }
 
+static char *ncacn_pipe_name(const char *endpoint)
+{
+  static const char prefix[] = "\\\\.";
+  char *pipe_name;
+
+  /* protseq=ncacn_np: named pipes */
+  pipe_name = I_RpcAllocate(sizeof(prefix) + strlen(endpoint));
+  strcat(strcpy(pipe_name, prefix), endpoint);
+  return pipe_name;
+}
+
 static RPC_STATUS rpcrt4_ncacn_np_open(RpcConnection* Connection)
 {
   RpcConnection_np *npc = (RpcConnection_np *) Connection;
   static const char prefix[] = "\\\\";
-  static const char local[] =".";
+  static const char local[] = ".";
+  BOOL bUseLocalName = TRUE;
+  CHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+  DWORD bufLen = sizeof(ComputerName)/sizeof(ComputerName[0]);
   RPC_STATUS r;
   LPSTR pname;
+  LPSTR NetworkAddr;
   INT size;
 
   /* already connected? */
@@ -318,18 +346,46 @@ static RPC_STATUS rpcrt4_ncacn_np_open(RpcConnection* Connection)
 
   /* protseq=ncacn_np: named pipes */
   size = strlen(prefix);
+
   if (Connection->NetworkAddr == NULL || strlen(Connection->NetworkAddr) == 0)
+  {
+    bUseLocalName = TRUE;
     size += strlen(local);
+  }
   else
-    size += strlen(Connection->NetworkAddr);
+  {
+    NetworkAddr = Connection->NetworkAddr;
+    if (NetworkAddr[0] == '\\' && NetworkAddr[1] == '\\')
+        NetworkAddr += 2;
+
+    if (GetComputerNameA(ComputerName, &bufLen))
+    {
+      if (stricmp(ComputerName, NetworkAddr) == 0)
+      {
+        bUseLocalName = TRUE;
+        size += strlen(local);
+      }
+      else
+      {
+        bUseLocalName = FALSE;
+        size += strlen(NetworkAddr);
+      }
+    }
+    else
+    {
+      bUseLocalName = FALSE;
+      size += strlen(NetworkAddr);
+    }
+  }
+
   size += strlen(Connection->Endpoint) + 1;
 
   pname = I_RpcAllocate(size);
   strcpy(pname, prefix);
-  if (Connection->NetworkAddr == NULL || strlen(Connection->NetworkAddr) == 0)
+  if (bUseLocalName)
     strcat(pname, local);
   else
-    strcat(pname, Connection->NetworkAddr);
+    strcat(pname, NetworkAddr);
   strcat(pname, Connection->Endpoint);
   r = rpcrt4_conn_open_pipe(Connection, pname, TRUE);
   I_RpcFree(pname);
@@ -388,6 +444,7 @@ static void rpcrt4_conn_np_handoff(RpcConnection_np *old_npc, RpcConnection_np *
 
 static RPC_STATUS rpcrt4_ncacn_np_handoff(RpcConnection *old_conn, RpcConnection *new_conn)
 {
+  DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
   RPC_STATUS status;
   LPSTR pname;
   static const char prefix[] = "\\\\.";
@@ -399,11 +456,49 @@ static RPC_STATUS rpcrt4_ncacn_np_handoff(RpcConnection *old_conn, RpcConnection
   status = rpcrt4_conn_create_pipe(old_conn, pname);
   I_RpcFree(pname);
 
+  /* Store the local computer name as the NetworkAddr for ncacn_np as long as
+   * we don't support named pipes over the network. */
+  FIXME("Using local computer name as NetworkAddr\n");
+  new_conn->NetworkAddr = HeapAlloc(GetProcessHeap(), 0, len);
+  if (!GetComputerNameA(new_conn->NetworkAddr, &len))
+  {
+    ERR("Failed to retrieve the computer name, error %u\n", GetLastError());
+    return RPC_S_OUT_OF_RESOURCES;
+  }
+
+  return status;
+}
+
+static RPC_STATUS is_pipe_listening(const char *pipe_name)
+{
+  return WaitNamedPipeA(pipe_name, 1) ? RPC_S_OK : RPC_S_NOT_LISTENING;
+}
+
+static RPC_STATUS rpcrt4_ncacn_np_is_server_listening(const char *endpoint)
+{
+  char *pipe_name;
+  RPC_STATUS status;
+
+  pipe_name = ncacn_pipe_name(endpoint);
+  status = is_pipe_listening(pipe_name);
+  I_RpcFree(pipe_name);
+  return status;
+}
+
+static RPC_STATUS rpcrt4_ncalrpc_np_is_server_listening(const char *endpoint)
+{
+  char *pipe_name;
+  RPC_STATUS status;
+
+  pipe_name = ncalrpc_pipe_name(endpoint);
+  status = is_pipe_listening(pipe_name);
+  I_RpcFree(pipe_name);
   return status;
 }
 
 static RPC_STATUS rpcrt4_ncalrpc_handoff(RpcConnection *old_conn, RpcConnection *new_conn)
 {
+  DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
   RPC_STATUS status;
   LPSTR pname;
   static const char prefix[] = "\\\\.\\pipe\\lrpc\\";
@@ -416,7 +511,15 @@ static RPC_STATUS rpcrt4_ncalrpc_handoff(RpcConnection *old_conn, RpcConnection
   strcat(strcpy(pname, prefix), old_conn->Endpoint);
   status = rpcrt4_conn_create_pipe(old_conn, pname);
   I_RpcFree(pname);
-    
+
+  /* Store the local computer name as the NetworkAddr for ncalrpc. */
+  new_conn->NetworkAddr = HeapAlloc(GetProcessHeap(), 0, len);
+  if (!GetComputerNameA(new_conn->NetworkAddr, &len))
+  {
+    ERR("Failed to retrieve the computer name, error %u\n", GetLastError());
+    return RPC_S_OUT_OF_RESOURCES;
+  }
+
   return status;
 }
 
@@ -961,6 +1064,7 @@ static size_t rpcrt4_ip_tcp_get_top_of_tower(unsigned char *tower_data,
     else
     {
         ERR("unexpected protocol family %d\n", ai->ai_family);
+        freeaddrinfo(ai);
         return 0;
     }
 
@@ -1465,10 +1569,20 @@ static RPC_STATUS rpcrt4_conn_tcp_handoff(RpcConnection *old_conn, RpcConnection
     ERR("Failed to accept a TCP connection: error %d\n", ret);
     return RPC_S_OUT_OF_RESOURCES;
   }
+
   nonblocking = 1;
   ioctlsocket(ret, FIONBIO, &nonblocking);
   client->sock = ret;
-  TRACE("Accepted a new TCP connection\n");
+
+  client->common.NetworkAddr = HeapAlloc(GetProcessHeap(), 0, INET6_ADDRSTRLEN);
+  ret = getnameinfo((struct sockaddr*)&address, addrsize, client->common.NetworkAddr, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+  if (ret != 0)
+  {
+    ERR("Failed to retrieve the IP address, error %d\n", ret);
+    return RPC_S_OUT_OF_RESOURCES;
+  }
+
+  TRACE("Accepted a new TCP connection from %s\n", client->common.NetworkAddr);
   return RPC_S_OK;
 }
 
@@ -1484,6 +1598,8 @@ static int rpcrt4_conn_tcp_read(RpcConnection *Connection,
       return -1;
     else if (r > 0)
       bytes_read += r;
+    else if (errno == EINTR)
+      continue;
     else if (errno != EAGAIN)
     {
       WARN("recv() failed: %s\n", strerror(errno));
@@ -1509,6 +1625,8 @@ static int rpcrt4_conn_tcp_write(RpcConnection *Connection,
     int r = send(tcpc->sock, (const char *)buffer + bytes_written, count - bytes_written, 0);
     if (r >= 0)
       bytes_written += r;
+    else if (errno == EINTR)
+      continue;
     else if (errno != EAGAIN)
       return -1;
     else
@@ -1541,6 +1659,12 @@ static void rpcrt4_conn_tcp_cancel_call(RpcConnection *Connection)
     rpcrt4_sock_wait_cancel(tcpc);
 }
 
+static RPC_STATUS rpcrt4_conn_tcp_is_server_listening(const char *endpoint)
+{
+    FIXME("\n");
+    return RPC_S_ACCESS_DENIED;
+}
+
 static int rpcrt4_conn_tcp_wait_for_incoming_data(RpcConnection *Connection)
 {
     RpcConnection_tcp *tcpc = (RpcConnection_tcp *) Connection;
@@ -1869,7 +1993,7 @@ typedef struct _RpcHttpAsyncData
     LONG refs;
     HANDLE completion_event;
     WORD async_result;
-    INTERNET_BUFFERSA inet_buffers;
+    INTERNET_BUFFERSW inet_buffers;
     CRITICAL_SECTION cs;
 } RpcHttpAsyncData;
 
@@ -1976,7 +2100,7 @@ static RpcConnection *rpcrt4_ncacn_http_alloc(void)
     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.dwStructSize = sizeof(INTERNET_BUFFERSW);
     httpc->async_data->inet_buffers.lpvBuffer = NULL;
     InitializeCriticalSection(&httpc->async_data->cs);
     httpc->async_data->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RpcHttpAsyncData.cs");
@@ -2221,9 +2345,47 @@ static RPC_STATUS rpcrt4_http_internet_connect(RpcConnection_http *httpc)
     return RPC_S_OK;
 }
 
+static int rpcrt4_http_async_read(HINTERNET req, RpcHttpAsyncData *async_data, HANDLE cancel_event,
+                                  void *buffer, unsigned int count)
+{
+    char *buf = buffer;
+    BOOL ret;
+    unsigned int bytes_left = count;
+    RPC_STATUS status = RPC_S_OK;
+
+    async_data->inet_buffers.lpvBuffer = HeapAlloc(GetProcessHeap(), 0, count);
+
+    while (bytes_left)
+    {
+        async_data->inet_buffers.dwBufferLength = bytes_left;
+        prepare_async_request(async_data);
+        ret = InternetReadFileExW(req, &async_data->inet_buffers, IRF_ASYNC, 0);
+        status = wait_async_request(async_data, ret, cancel_event);
+        if (status != RPC_S_OK)
+        {
+            if (status == RPC_S_CALL_CANCELLED)
+                TRACE("call cancelled\n");
+            break;
+        }
+
+        if (!async_data->inet_buffers.dwBufferLength)
+            break;
+        memcpy(buf, async_data->inet_buffers.lpvBuffer,
+               async_data->inet_buffers.dwBufferLength);
+
+        bytes_left -= async_data->inet_buffers.dwBufferLength;
+        buf += async_data->inet_buffers.dwBufferLength;
+    }
+
+    HeapFree(GetProcessHeap(), 0, async_data->inet_buffers.lpvBuffer);
+    async_data->inet_buffers.lpvBuffer = NULL;
+
+    TRACE("%p %p %u -> %u\n", req, buffer, count, status);
+    return status == RPC_S_OK ? count : -1;
+}
+
 static RPC_STATUS send_echo_request(HINTERNET req, RpcHttpAsyncData *async_data, HANDLE cancel_event)
 {
-    DWORD bytes_read;
     BYTE buf[20];
     BOOL ret;
     RPC_STATUS status;
@@ -2238,12 +2400,23 @@ static RPC_STATUS send_echo_request(HINTERNET req, RpcHttpAsyncData *async_data,
     status = rpcrt4_http_check_response(req);
     if (status != RPC_S_OK) return status;
 
-    InternetReadFile(req, buf, sizeof(buf), &bytes_read);
+    rpcrt4_http_async_read(req, async_data, cancel_event, buf, sizeof(buf));
     /* FIXME: do something with retrieved data */
 
     return RPC_S_OK;
 }
 
+static RPC_STATUS insert_content_length_header(HINTERNET request, DWORD len)
+{
+    static const WCHAR fmtW[] =
+        {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','u','\r','\n',0};
+    WCHAR header[sizeof(fmtW) / sizeof(fmtW[0]) + 10];
+
+    sprintfW(header, fmtW, len);
+    if ((HttpAddRequestHeadersW(request, header, -1, HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD))) return RPC_S_OK;
+    return RPC_S_SERVER_UNAVAILABLE;
+}
+
 /* prepare the in pipe for use by RPC packets */
 static RPC_STATUS rpcrt4_http_prepare_in_pipe(HINTERNET in_request, RpcHttpAsyncData *async_data, HANDLE cancel_event,
                                               const UUID *connection_uuid, const UUID *in_pipe_uuid,
@@ -2265,6 +2438,9 @@ static RPC_STATUS rpcrt4_http_prepare_in_pipe(HINTERNET in_request, RpcHttpAsync
     buffers_in.dwStructSize = sizeof(buffers_in);
     /* FIXME: get this from the registry */
     buffers_in.dwBufferTotal = 1024 * 1024 * 1024; /* 1Gb */
+    status = insert_content_length_header(in_request, buffers_in.dwBufferTotal);
+    if (status != RPC_S_OK) return status;
+
     prepare_async_request(async_data);
     ret = HttpSendRequestExW(in_request, &buffers_in, NULL, 0, 0);
     status = wait_async_request(async_data, ret, cancel_event);
@@ -2284,14 +2460,13 @@ static RPC_STATUS rpcrt4_http_prepare_in_pipe(HINTERNET in_request, RpcHttpAsync
     return RPC_S_OK;
 }
 
-static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcPktHdr *hdr, BYTE **data)
+static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcHttpAsyncData *async_data,
+                                               HANDLE cancel_event, RpcPktHdr *hdr, BYTE **data)
 {
-    BOOL ret;
-    DWORD bytes_read;
     unsigned short data_len;
+    unsigned int size;
 
-    ret = InternetReadFile(request, hdr, sizeof(hdr->common), &bytes_read);
-    if (!ret)
+    if (rpcrt4_http_async_read(request, async_data, cancel_event, hdr, sizeof(hdr->common)) < 0)
         return RPC_S_SERVER_UNAVAILABLE;
     if (hdr->common.ptype != PKT_HTTP || hdr->common.frag_len < sizeof(hdr->http))
     {
@@ -2300,8 +2475,8 @@ static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcPktHdr *hdr
         return RPC_S_PROTOCOL_ERROR;
     }
 
-    ret = InternetReadFile(request, &hdr->common + 1, sizeof(hdr->http) - sizeof(hdr->common), &bytes_read);
-    if (!ret)
+    size = sizeof(hdr->http) - sizeof(hdr->common);
+    if (rpcrt4_http_async_read(request, async_data, cancel_event, &hdr->common + 1, size) < 0)
         return RPC_S_SERVER_UNAVAILABLE;
 
     data_len = hdr->common.frag_len - sizeof(hdr->http);
@@ -2310,8 +2485,7 @@ static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcPktHdr *hdr
         *data = HeapAlloc(GetProcessHeap(), 0, data_len);
         if (!*data)
             return RPC_S_OUT_OF_RESOURCES;
-        ret = InternetReadFile(request, *data, data_len, &bytes_read);
-        if (!ret)
+        if (rpcrt4_http_async_read(request, async_data, cancel_event, *data, data_len) < 0)
         {
             HeapFree(GetProcessHeap(), 0, *data);
             return RPC_S_SERVER_UNAVAILABLE;
@@ -2323,6 +2497,7 @@ static RPC_STATUS rpcrt4_http_read_http_packet(HINTERNET request, RpcPktHdr *hdr
     if (!RPCRT4_IsValidHttpPacket(hdr, *data, data_len))
     {
         ERR("invalid http packet\n");
+        HeapFree(GetProcessHeap(), 0, *data);
         return RPC_S_PROTOCOL_ERROR;
     }
 
@@ -2341,7 +2516,6 @@ static RPC_STATUS rpcrt4_http_prepare_out_pipe(HINTERNET out_request, RpcHttpAsy
     BYTE *data_from_server;
     RpcPktHdr pkt_from_server;
     ULONG field1, field3;
-    DWORD bytes_read;
     BYTE buf[20];
 
     if (!authorized)
@@ -2351,11 +2525,18 @@ static RPC_STATUS rpcrt4_http_prepare_out_pipe(HINTERNET out_request, RpcHttpAsy
         if (status != RPC_S_OK) return status;
     }
     else
-        InternetReadFile(out_request, buf, sizeof(buf), &bytes_read);
+        rpcrt4_http_async_read(out_request, async_data, cancel_event, buf, sizeof(buf));
 
     hdr = RPCRT4_BuildHttpConnectHeader(TRUE, connection_uuid, out_pipe_uuid, NULL);
     if (!hdr) return RPC_S_OUT_OF_RESOURCES;
 
+    status = insert_content_length_header(out_request, hdr->common.frag_len);
+    if (status != RPC_S_OK)
+    {
+        RPCRT4_FreeHeader(hdr);
+        return status;
+    }
+
     TRACE("sending HTTP connect header to server\n");
     prepare_async_request(async_data);
     ret = HttpSendRequestW(out_request, NULL, 0, hdr, hdr->common.frag_len);
@@ -2366,8 +2547,8 @@ static RPC_STATUS rpcrt4_http_prepare_out_pipe(HINTERNET out_request, RpcHttpAsy
     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);
+    status = rpcrt4_http_read_http_packet(out_request, async_data, cancel_event,
+                                          &pkt_from_server, &data_from_server);
     if (status != RPC_S_OK) return status;
     status = RPCRT4_ParseHttpPrepareHeader1(&pkt_from_server, data_from_server,
                                             &field1);
@@ -2377,8 +2558,8 @@ static RPC_STATUS rpcrt4_http_prepare_out_pipe(HINTERNET out_request, RpcHttpAsy
 
     for (;;)
     {
-        status = rpcrt4_http_read_http_packet(out_request, &pkt_from_server,
-                                              &data_from_server);
+        status = rpcrt4_http_read_http_packet(out_request, async_data, cancel_event,
+                                              &pkt_from_server, &data_from_server);
         if (status != RPC_S_OK) return status;
         if (pkt_from_server.http.flags != 0x0001) break;
 
@@ -2783,7 +2964,7 @@ static RPC_STATUS insert_authorization_header(HINTERNET request, ULONG scheme, c
     return status;
 }
 
-static void drain_content(HINTERNET request)
+static void drain_content(HINTERNET request, RpcHttpAsyncData *async_data, HANDLE cancel_event)
 {
     DWORD count, len = 0, size = sizeof(len);
     char buf[2048];
@@ -2793,7 +2974,7 @@ static void drain_content(HINTERNET request)
     for (;;)
     {
         count = min(sizeof(buf), len);
-        if (!InternetReadFile(request, buf, count, &count) || !count) return;
+        if (rpcrt4_http_async_read(request, async_data, cancel_event, buf, count) <= 0) return;
         len -= count;
     }
 }
@@ -2820,40 +3001,16 @@ static RPC_STATUS authorize_request(RpcConnection_http *httpc, HINTERNET request
 
         status = rpcrt4_http_check_response(request);
         if (status != RPC_S_OK && status != ERROR_ACCESS_DENIED) break;
-        drain_content(request);
+        drain_content(request, httpc->async_data, httpc->cancel_event);
     }
 
     if (info->scheme != RPC_C_HTTP_AUTHN_SCHEME_BASIC)
-        HttpAddRequestHeadersW(request, authW, -1, HTTP_ADDREQ_FLAG_REPLACE);
+        HttpAddRequestHeadersW(request, authW, -1, HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD);
 
     destroy_authinfo(info);
     return status;
 }
 
-static RPC_STATUS insert_cookie_header(HINTERNET request, const WCHAR *value)
-{
-    static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
-    WCHAR *header, *ptr;
-    int len;
-    RPC_STATUS status = RPC_S_SERVER_UNAVAILABLE;
-
-    if (!value) return RPC_S_OK;
-
-    len = strlenW(value);
-    if ((header = HeapAlloc(GetProcessHeap(), 0, sizeof(cookieW) + (len + 3) * sizeof(WCHAR))))
-    {
-        memcpy(header, cookieW, sizeof(cookieW));
-        ptr = header + sizeof(cookieW) / sizeof(cookieW[0]);
-        memcpy(ptr, value, len * sizeof(WCHAR));
-        ptr[len++] = '\r';
-        ptr[len++] = '\n';
-        ptr[len] = 0;
-        if ((HttpAddRequestHeadersW(request, header, -1, HTTP_ADDREQ_FLAG_ADD_IF_NEW))) status = RPC_S_OK;
-        HeapFree(GetProcessHeap(), 0, header);
-    }
-    return status;
-}
-
 static BOOL has_credentials(RpcConnection_http *httpc)
 {
     RPC_HTTP_TRANSPORT_CREDENTIALS_W *creds;
@@ -2879,6 +3036,51 @@ static BOOL is_secure(RpcConnection_http *httpc)
            (httpc->common.QOS->qos->u.HttpCredentials->Flags & RPC_C_HTTP_FLAG_USE_SSL);
 }
 
+static RPC_STATUS set_auth_cookie(RpcConnection_http *httpc, const WCHAR *value)
+{
+    static WCHAR httpW[] = {'h','t','t','p',0};
+    static WCHAR httpsW[] = {'h','t','t','p','s',0};
+    URL_COMPONENTSW uc;
+    DWORD len;
+    WCHAR *url;
+    BOOL ret;
+
+    if (!value) return RPC_S_OK;
+
+    uc.dwStructSize     = sizeof(uc);
+    uc.lpszScheme       = is_secure(httpc) ? httpsW : httpW;
+    uc.dwSchemeLength   = 0;
+    uc.lpszHostName     = httpc->servername;
+    uc.dwHostNameLength = 0;
+    uc.nPort            = 0;
+    uc.lpszUserName     = NULL;
+    uc.dwUserNameLength = 0;
+    uc.lpszPassword     = NULL;
+    uc.dwPasswordLength = 0;
+    uc.lpszUrlPath      = NULL;
+    uc.dwUrlPathLength  = 0;
+    uc.lpszExtraInfo    = NULL;
+    uc.dwExtraInfoLength = 0;
+
+    if (!InternetCreateUrlW(&uc, 0, NULL, &len) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+        return RPC_S_SERVER_UNAVAILABLE;
+
+    if (!(url = HeapAlloc(GetProcessHeap(), 0, len))) return RPC_S_OUT_OF_MEMORY;
+
+    len = len / sizeof(WCHAR) - 1;
+    if (!InternetCreateUrlW(&uc, 0, url, &len))
+    {
+        HeapFree(GetProcessHeap(), 0, url);
+        return RPC_S_SERVER_UNAVAILABLE;
+    }
+
+    ret = InternetSetCookieW(url, NULL, value);
+    HeapFree(GetProcessHeap(), 0, url);
+    if (!ret) return RPC_S_SERVER_UNAVAILABLE;
+
+    return RPC_S_OK;
+}
+
 static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
 {
     RpcConnection_http *httpc = (RpcConnection_http *)Connection;
@@ -2908,9 +3110,9 @@ static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
 
     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);
+    UuidCreate(&httpc->connection_uuid);
+    UuidCreate(&httpc->in_pipe_uuid);
+    UuidCreate(&httpc->out_pipe_uuid);
 
     status = rpcrt4_http_internet_connect(httpc);
     if (status != RPC_S_OK)
@@ -2932,6 +3134,12 @@ static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
     if (secure) flags |= INTERNET_FLAG_SECURE;
     if (credentials) flags |= INTERNET_FLAG_NO_AUTH;
 
+    status = set_auth_cookie(httpc, Connection->CookieAuth);
+    if (status != RPC_S_OK)
+    {
+        HeapFree(GetProcessHeap(), 0, url);
+        return status;
+    }
     httpc->in_request = HttpOpenRequestW(httpc->session, wszVerbIn, url, NULL, NULL, wszAcceptTypes,
                                          flags, (DWORD_PTR)httpc->async_data);
     if (!httpc->in_request)
@@ -2940,12 +3148,7 @@ static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
         HeapFree(GetProcessHeap(), 0, url);
         return RPC_S_SERVER_UNAVAILABLE;
     }
-    status = insert_cookie_header(httpc->in_request, Connection->CookieAuth);
-    if (status != RPC_S_OK)
-    {
-        HeapFree(GetProcessHeap(), 0, url);
-        return status;
-    }
+
     if (credentials)
     {
         status = authorize_request(httpc, httpc->in_request);
@@ -2960,7 +3163,7 @@ static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
             HeapFree(GetProcessHeap(), 0, url);
             return status;
         }
-        drain_content(httpc->in_request);
+        drain_content(httpc->in_request, httpc->async_data, httpc->cancel_event);
     }
 
     httpc->out_request = HttpOpenRequestW(httpc->session, wszVerbOut, url, NULL, NULL, wszAcceptTypes,
@@ -2971,9 +3174,6 @@ static RPC_STATUS rpcrt4_ncacn_http_open(RpcConnection* Connection)
         ERR("HttpOpenRequestW failed with error %d\n", GetLastError());
         return RPC_S_SERVER_UNAVAILABLE;
     }
-    status = insert_cookie_header(httpc->out_request, Connection->CookieAuth);
-    if (status != RPC_S_OK)
-        return status;
 
     if (credentials)
     {
@@ -3026,39 +3226,7 @@ static int rpcrt4_ncacn_http_read(RpcConnection *Connection,
                                 void *buffer, unsigned int count)
 {
   RpcConnection_http *httpc = (RpcConnection_http *) Connection;
-  char *buf = buffer;
-  BOOL ret;
-  unsigned int bytes_left = count;
-  RPC_STATUS status = RPC_S_OK;
-
-  httpc->async_data->inet_buffers.lpvBuffer = HeapAlloc(GetProcessHeap(), 0, count);
-
-  while (bytes_left)
-  {
-    httpc->async_data->inet_buffers.dwBufferLength = bytes_left;
-    prepare_async_request(httpc->async_data);
-    ret = InternetReadFileExA(httpc->out_request, &httpc->async_data->inet_buffers, IRF_ASYNC, 0);
-    status = wait_async_request(httpc->async_data, ret, httpc->cancel_event);
-    if(status != RPC_S_OK) {
-        if(status == RPC_S_CALL_CANCELLED)
-            TRACE("call cancelled\n");
-        break;
-    }
-
-    if(!httpc->async_data->inet_buffers.dwBufferLength)
-        break;
-    memcpy(buf, httpc->async_data->inet_buffers.lpvBuffer,
-           httpc->async_data->inet_buffers.dwBufferLength);
-
-    bytes_left -= httpc->async_data->inet_buffers.dwBufferLength;
-    buf += httpc->async_data->inet_buffers.dwBufferLength;
-  }
-
-  HeapFree(GetProcessHeap(), 0, httpc->async_data->inet_buffers.lpvBuffer);
-  httpc->async_data->inet_buffers.lpvBuffer = NULL;
-
-  TRACE("%p %p %u -> %u\n", httpc->out_request, buffer, count, status);
-  return status == RPC_S_OK ? count : -1;
+  return rpcrt4_http_async_read(httpc->out_request, httpc->async_data, httpc->cancel_event, buffer, count);
 }
 
 static RPC_STATUS rpcrt4_ncacn_http_receive_fragment(RpcConnection *Connection, RpcPktHdr **Header, void **Payload)
@@ -3266,6 +3434,12 @@ static void rpcrt4_ncacn_http_cancel_call(RpcConnection *Connection)
   SetEvent(httpc->cancel_event);
 }
 
+static RPC_STATUS rpcrt4_ncacn_http_is_server_listening(const char *endpoint)
+{
+    FIXME("\n");
+    return RPC_S_ACCESS_DENIED;
+}
+
 static int rpcrt4_ncacn_http_wait_for_incoming_data(RpcConnection *Connection)
 {
   RpcConnection_http *httpc = (RpcConnection_http *) Connection;
@@ -3307,6 +3481,7 @@ static const struct connection_ops conn_protseq_list[] = {
     rpcrt4_conn_np_write,
     rpcrt4_conn_np_close,
     rpcrt4_conn_np_cancel_call,
+    rpcrt4_ncacn_np_is_server_listening,
     rpcrt4_conn_np_wait_for_incoming_data,
     rpcrt4_ncacn_np_get_top_of_tower,
     rpcrt4_ncacn_np_parse_top_of_tower,
@@ -3327,6 +3502,7 @@ static const struct connection_ops conn_protseq_list[] = {
     rpcrt4_conn_np_write,
     rpcrt4_conn_np_close,
     rpcrt4_conn_np_cancel_call,
+    rpcrt4_ncalrpc_np_is_server_listening,
     rpcrt4_conn_np_wait_for_incoming_data,
     rpcrt4_ncalrpc_get_top_of_tower,
     rpcrt4_ncalrpc_parse_top_of_tower,
@@ -3347,6 +3523,7 @@ static const struct connection_ops conn_protseq_list[] = {
     rpcrt4_conn_tcp_write,
     rpcrt4_conn_tcp_close,
     rpcrt4_conn_tcp_cancel_call,
+    rpcrt4_conn_tcp_is_server_listening,
     rpcrt4_conn_tcp_wait_for_incoming_data,
     rpcrt4_ncacn_ip_tcp_get_top_of_tower,
     rpcrt4_ncacn_ip_tcp_parse_top_of_tower,
@@ -3367,6 +3544,7 @@ static const struct connection_ops conn_protseq_list[] = {
     rpcrt4_ncacn_http_write,
     rpcrt4_ncacn_http_close,
     rpcrt4_ncacn_http_cancel_call,
+    rpcrt4_ncacn_http_is_server_listening,
     rpcrt4_ncacn_http_wait_for_incoming_data,
     rpcrt4_ncacn_http_get_top_of_tower,
     rpcrt4_ncacn_http_parse_top_of_tower,
@@ -3540,6 +3718,20 @@ RPC_STATUS RPCRT4_ReleaseConnection(RpcConnection* Connection)
   return RPC_S_OK;
 }
 
+RPC_STATUS RPCRT4_IsServerListening(const char *protseq, const char *endpoint)
+{
+  const struct connection_ops *ops;
+
+  ops = rpcrt4_get_conn_protseq_ops(protseq);
+  if (!ops)
+  {
+    FIXME("not supported for protseq %s\n", protseq);
+    return RPC_S_INVALID_BINDING;
+  }
+
+  return ops->is_server_listening(endpoint);
+}
+
 RPC_STATUS RpcTransport_GetTopOfTower(unsigned char *tower_data,
                                       size_t *tower_size,
                                       const char *protseq,