/* * Return a matching hv_vmbus_device_id pointer. * If there is no match, return NULL. */ static const struct hv_vmbus_device_id *hv_vmbus_get_id( const struct hv_vmbus_device_id *id, const __u8 *guid) { for (; !is_null_guid(id->guid); id++) if (!memcmp(&id->guid, guid, sizeof(uuid_le))) return id; return NULL; }
/*! * @brief Receive a new packet on the given remote endpoint. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to a pointer that will receive the \c Packet data. * @return An indication of the result of processing the transmission request. */ static DWORD packet_receive_named_pipe(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; PacketHeader header = { 0 }; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR packetBuffer = NULL; PUCHAR payload = NULL; ULONG payloadLength; NamedPipeTransportContext* ctx = (NamedPipeTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); dprintf("[PIPE PACKET RECEIVE] reading in the header from pipe handle: %p", ctx->pipe); // Read the packet length while (inHeader) { if (!ReadFile(ctx->pipe, ((PUCHAR)&header + headerBytes), sizeof(PacketHeader)-headerBytes, &bytesRead, NULL)) { SetLastError(ERROR_NOT_FOUND); goto out; } headerBytes += bytesRead; if (headerBytes != sizeof(PacketHeader)) { vdprintf("[PIPE] More bytes required"); continue; } inHeader = FALSE; } if (headerBytes != sizeof(PacketHeader)) { dprintf("[PIPE] we didn't get enough header bytes"); goto out; } vdprintf("[PIPE] the XOR key is: %02x%02x%02x%02x", header.xor_key[0], header.xor_key[1], header.xor_key[2], header.xor_key[3]); #ifdef DEBUGTRACE PUCHAR h = (PUCHAR)&header; dprintf("[PIPE] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]", h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]); #endif // At this point, we might have read in a valid TLV packet, or we might have read in the first chunk of data // from a staged listener after a reconnect. We can figure this out rather lazily by assuming the following: // XOR keys are always 4 bytes that are non-zero. If the higher order byte of the xor key is zero, then it // isn't an XOR Key, instead it's the 4-byte length of the metsrv binary (because metsrv isn't THAT big). if (header.xor_key[3] == 0) { // looks like we have a metsrv instance, time to ignore it. int length = *(int*)&header.xor_key[0]; dprintf("[PIPE] discovered a length header, assuming it's metsrv of length %d", length); int bytesToRead = length - sizeof(PacketHeader) + sizeof(DWORD); char* buffer = (char*)malloc(bytesToRead); read_raw_bytes_to_buffer(ctx, buffer, bytesToRead, &bytesRead); free(buffer); // did something go wrong. if (bytesToRead != bytesRead) { dprintf("[PIPE] Failed to read all bytes when flushing the buffer: %u vs %u", bytesToRead, bytesRead); goto out; } // indicate success, but don't return a packet for processing SetLastError(ERROR_SUCCESS); *packet = NULL; } else { vdprintf("[PIPE] XOR key looks fine, moving on"); // xor the header data xor_bytes(header.xor_key, (PUCHAR)&header + sizeof(header.xor_key), sizeof(PacketHeader) - sizeof(header.xor_key)); #ifdef DEBUGTRACE PUCHAR h = (PUCHAR)&header; dprintf("[PIPE] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]", h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]); #endif // if we don't have a GUID yet, we need to take the one given in the packet if (is_null_guid(remote->orig_config->session.session_guid)) { memcpy(remote->orig_config->session.session_guid, header.session_guid, sizeof(remote->orig_config->session.session_guid)); } payloadLength = ntohl(header.length) - sizeof(TlvHeader); dprintf("[PIPE] Payload length is %u 0x%08x", payloadLength, payloadLength); DWORD packetSize = sizeof(PacketHeader) + payloadLength; dprintf("[PIPE] total buffer size for the packet is %u 0x%08x", packetSize, packetSize); payloadBytesLeft = payloadLength; // Allocate the payload if (!(packetBuffer = (PUCHAR)malloc(packetSize))) { dprintf("[PIPE] Failed to create the packet buffer"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto out; } dprintf("[PIPE] Allocated packet buffer at %p", packetBuffer); // we're done with the header data, so we need to re-encode it, as the packet decryptor is going to // handle the extraction for us. xor_bytes(header.xor_key, (LPBYTE)&header.session_guid[0], sizeof(PacketHeader) - sizeof(header.xor_key)); // Copy the packet header stuff over to the packet memcpy_s(packetBuffer, sizeof(PacketHeader), (LPBYTE)&header, sizeof(PacketHeader)); payload = packetBuffer + sizeof(PacketHeader); // Read the payload res = read_raw_bytes_to_buffer(ctx, payload, payloadLength, &bytesRead); dprintf("[PIPE] wanted %u read %u", payloadLength, bytesRead); // Didn't finish? if (bytesRead != payloadLength) { dprintf("[PIPE] Failed to get all the payload bytes"); SetLastError(res); goto out; } vdprintf("[PIPE] decrypting packet"); SetLastError(decrypt_packet(remote, packet, packetBuffer, packetSize)); free(packetBuffer); packetBuffer = NULL; } out: res = GetLastError(); // Cleanup if (packetBuffer) { free(packetBuffer); } lock_release(remote->lock); return res; }
/*! * @brief Windows-specific function to receive a new packet via one of the HTTP libs (WinInet or WinHTTP). * @param remote Pointer to the \c Remote instance. * @param packet Pointer to a pointer that will receive the \c Packet data. * @return An indication of the result of processing the transmission request. */ static DWORD packet_receive_http(Remote *remote, Packet **packet) { DWORD headerBytes = 0, payloadBytesLeft = 0, res; Packet *localPacket = NULL; PacketHeader header; LONG bytesRead; BOOL inHeader = TRUE; PUCHAR packetBuffer = NULL; ULONG payloadLength; HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx; HINTERNET hReq; BOOL hRes; DWORD retries = 5; lock_acquire(remote->lock); hReq = ctx->create_req(ctx, TRUE, "PACKET RECEIVE"); if (hReq == NULL) { goto out; } vdprintf("[PACKET RECEIVE HTTP] sending GET"); hRes = ctx->send_req(ctx, hReq, NULL, 0); if (!hRes) { dprintf("[PACKET RECEIVE HTTP] Failed send_req: %d %d", GetLastError(), WSAGetLastError()); SetLastError(ERROR_NOT_FOUND); goto out; } vdprintf("[PACKET RECEIVE HTTP] Waiting to see the response ..."); if (ctx->receive_response && !ctx->receive_response(hReq)) { vdprintf("[PACKET RECEIVE] Failed receive: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); goto out; } SetLastError(ctx->validate_response(hReq, ctx)); if (GetLastError() != ERROR_SUCCESS) { goto out; } // Read the packet length retries = 3; vdprintf("[PACKET RECEIVE HTTP] Start looping through the receive calls"); while (inHeader && retries > 0) { retries--; if (!ctx->read_response(hReq, (PUCHAR)&header + headerBytes, sizeof(PacketHeader)-headerBytes, &bytesRead)) { dprintf("[PACKET RECEIVE HTTP] Failed HEADER read_response: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); goto out; } vdprintf("[PACKET RECEIVE NHTTP] Data received: %u bytes", bytesRead); // If the response contains no data, this is fine, it just means the // remote side had nothing to tell us. Indicate this through a // ERROR_EMPTY response code so we can update the timestamp. if (bytesRead == 0) { SetLastError(ERROR_EMPTY); goto out; } headerBytes += bytesRead; if (headerBytes != sizeof(PacketHeader)) { continue; } inHeader = FALSE; } if (headerBytes != sizeof(PacketHeader)) { dprintf("[PACKET RECEIVE HTTP] headerBytes not valid"); SetLastError(ERROR_NOT_FOUND); goto out; } dprintf("[PACKET RECEIVE HTTP] decoding header"); PacketHeader encodedHeader; memcpy(&encodedHeader, &header, sizeof(PacketHeader)); xor_bytes(header.xor_key, (PUCHAR)&header + sizeof(header.xor_key), sizeof(PacketHeader) - sizeof(header.xor_key)); #ifdef DEBUGTRACE PUCHAR h = (PUCHAR)&header; vdprintf("[HTTP] Packet header: [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X] [0x%02X 0x%02X 0x%02X 0x%02X]", h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19], h[20], h[21], h[22], h[23], h[24], h[25], h[26], h[27], h[28], h[29], h[30], h[31]); #endif payloadLength = ntohl(header.length) - sizeof(TlvHeader); vdprintf("[REC HTTP] Payload length is %d", payloadLength); DWORD packetSize = sizeof(PacketHeader) + payloadLength; vdprintf("[REC HTTP] total buffer size for the packet is %d", packetSize); payloadBytesLeft = payloadLength; // Allocate the payload if (!(packetBuffer = (PUCHAR)malloc(packetSize))) { dprintf("[REC HTTP] Failed to create the packet buffer"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto out; } dprintf("[REC HTTP] Allocated packet buffer at %p", packetBuffer); // Copy the packet header stuff over to the packet memcpy_s(packetBuffer, sizeof(PacketHeader), (LPBYTE)&encodedHeader, sizeof(PacketHeader)); LPBYTE payload = packetBuffer + sizeof(PacketHeader); // Read the payload retries = payloadBytesLeft; while (payloadBytesLeft > 0 && retries > 0) { vdprintf("[PACKET RECEIVE HTTP] reading more data from the body..."); retries--; if (!ctx->read_response(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead)) { dprintf("[PACKET RECEIVE] Failed BODY read_response: %d", GetLastError()); SetLastError(ERROR_NOT_FOUND); goto out; } if (!bytesRead) { vdprintf("[PACKET RECEIVE HTTP] no bytes read, bailing out"); SetLastError(ERROR_NOT_FOUND); goto out; } vdprintf("[PACKET RECEIVE HTTP] bytes read: %u", bytesRead); payloadBytesLeft -= bytesRead; } // Didn't finish? if (payloadBytesLeft) { goto out; } #ifdef DEBUGTRACE h = (PUCHAR)&header.session_guid[0]; dprintf("[HTTP] Packet Session GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]); #endif if (is_null_guid(header.session_guid) || memcmp(remote->orig_config->session.session_guid, header.session_guid, sizeof(header.session_guid)) == 0) { dprintf("[HTTP] Session GUIDs match (or packet guid is null), decrypting packet"); SetLastError(decrypt_packet(remote, packet, packetBuffer, packetSize)); } else { dprintf("[HTTP] Session GUIDs don't match, looking for a pivot"); PivotContext* pivotCtx = pivot_tree_find(remote->pivot_sessions, header.session_guid); if (pivotCtx != NULL) { dprintf("[HTTP] Pivot found, dispatching packet on a thread (to avoid main thread blocking)"); SetLastError(pivot_packet_dispatch(pivotCtx, packetBuffer, packetSize)); // mark this packet buffer as NULL as the thread will clean it up packetBuffer = NULL; *packet = NULL; } else { dprintf("[HTTP] Session GUIDs don't match, can't find pivot!"); } } out: res = GetLastError(); dprintf("[HTTP] Cleaning up"); SAFE_FREE(packetBuffer); // Cleanup on failure if (res != ERROR_SUCCESS) { SAFE_FREE(localPacket); } if (hReq) { ctx->close_req(hReq); } lock_release(remote->lock); dprintf("[HTTP] Packet receive finished"); return res; }