270 lines
8.1 KiB
C
270 lines
8.1 KiB
C
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include "../include/qemu_a6_usb_protocol.h"
|
|
|
|
#define QA6_DEFAULT_HOST "127.0.0.1"
|
|
#define QA6_DEFAULT_PORT "26050"
|
|
#define QA6_DRIVER_PATH L"\\\\.\\QemuA6Ude0"
|
|
|
|
static volatile LONG Qa6StopRequested;
|
|
|
|
static BOOL WINAPI
|
|
Qa6ConsoleHandler(DWORD event)
|
|
{
|
|
if (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT ||
|
|
event == CTRL_CLOSE_EVENT) {
|
|
InterlockedExchange(&Qa6StopRequested, 1);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
Qa6SendAll(SOCKET socketHandle, const unsigned char *buffer, int length)
|
|
{
|
|
int offset = 0;
|
|
|
|
while (offset < length) {
|
|
int sent = send(socketHandle, (const char *)buffer + offset,
|
|
length - offset, 0);
|
|
if (sent == SOCKET_ERROR || sent == 0) {
|
|
return -1;
|
|
}
|
|
offset += sent;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Qa6ReceiveAll(SOCKET socketHandle, unsigned char *buffer, int length)
|
|
{
|
|
int offset = 0;
|
|
|
|
while (offset < length) {
|
|
int received = recv(socketHandle, (char *)buffer + offset,
|
|
length - offset, 0);
|
|
if (received == SOCKET_ERROR || received == 0) {
|
|
return -1;
|
|
}
|
|
offset += received;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static SOCKET
|
|
Qa6ConnectQemu(const char *host, const char *port)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *addresses = NULL;
|
|
struct addrinfo *address;
|
|
SOCKET socketHandle = INVALID_SOCKET;
|
|
int result;
|
|
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
result = getaddrinfo(host, port, &hints, &addresses);
|
|
if (result != 0) {
|
|
fprintf(stderr, "Resolution de %s:%s impossible: %d\n", host, port,
|
|
result);
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
for (address = addresses; address != NULL; address = address->ai_next) {
|
|
socketHandle = socket(address->ai_family, address->ai_socktype,
|
|
address->ai_protocol);
|
|
if (socketHandle == INVALID_SOCKET) {
|
|
continue;
|
|
}
|
|
if (connect(socketHandle, address->ai_addr,
|
|
(int)address->ai_addrlen) == 0) {
|
|
break;
|
|
}
|
|
closesocket(socketHandle);
|
|
socketHandle = INVALID_SOCKET;
|
|
}
|
|
|
|
freeaddrinfo(addresses);
|
|
return socketHandle;
|
|
}
|
|
|
|
static HANDLE
|
|
Qa6OpenDriver(void)
|
|
{
|
|
return CreateFileW(QA6_DRIVER_PATH, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
|
|
static int
|
|
Qa6ValidateFrame(const unsigned char *frame, DWORD length,
|
|
unsigned short expectedType)
|
|
{
|
|
const Qa6UsbFrameHeader *header;
|
|
|
|
if (length < QA6_USB_HEADER_SIZE) {
|
|
return -1;
|
|
}
|
|
header = (const Qa6UsbFrameHeader *)frame;
|
|
if (header->magic != QA6_USB_MAGIC ||
|
|
header->version != QA6_USB_PROTOCOL_VERSION ||
|
|
header->type != expectedType ||
|
|
header->payload_length > QA6_USB_MAX_PAYLOAD ||
|
|
length != QA6_USB_HEADER_SIZE + header->payload_length) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Qa6RelayLoop(HANDLE driver, SOCKET qemu)
|
|
{
|
|
unsigned char request[QA6_USB_MAX_FRAME_SIZE];
|
|
unsigned char response[QA6_USB_MAX_FRAME_SIZE];
|
|
|
|
while (InterlockedCompareExchange(&Qa6StopRequested, 0, 0) == 0) {
|
|
Qa6UsbFrameHeader *requestHeader;
|
|
Qa6UsbFrameHeader *responseHeader;
|
|
DWORD requestLength = 0;
|
|
DWORD responseLength;
|
|
DWORD written = 0;
|
|
|
|
if (!ReadFile(driver, request, sizeof(request), &requestLength,
|
|
NULL)) {
|
|
DWORD error = GetLastError();
|
|
fprintf(stderr, "Lecture du pilote impossible: erreur %lu\n",
|
|
error);
|
|
return -1;
|
|
}
|
|
if (Qa6ValidateFrame(request, requestLength,
|
|
QA6_USB_MESSAGE_REQUEST) != 0) {
|
|
fprintf(stderr, "Trame invalide recue du pilote (%lu octets)\n",
|
|
requestLength);
|
|
return -1;
|
|
}
|
|
|
|
requestHeader = (Qa6UsbFrameHeader *)request;
|
|
printf("USB EP0 #%lu: bm=%02x req=%02x value=%02x%02x "
|
|
"index=%02x%02x len=%u\n",
|
|
requestHeader->request_id,
|
|
requestHeader->setup[0], requestHeader->setup[1],
|
|
requestHeader->setup[3], requestHeader->setup[2],
|
|
requestHeader->setup[5], requestHeader->setup[4],
|
|
(unsigned)(requestHeader->setup[6] |
|
|
(requestHeader->setup[7] << 8)));
|
|
fflush(stdout);
|
|
|
|
if (Qa6SendAll(qemu, request, (int)requestLength) != 0) {
|
|
fprintf(stderr, "Connexion QEMU interrompue pendant l'envoi.\n");
|
|
return -1;
|
|
}
|
|
if (Qa6ReceiveAll(qemu, response, QA6_USB_HEADER_SIZE) != 0) {
|
|
fprintf(stderr, "Connexion QEMU interrompue pendant la reponse.\n");
|
|
return -1;
|
|
}
|
|
|
|
responseHeader = (Qa6UsbFrameHeader *)response;
|
|
if (responseHeader->payload_length > QA6_USB_MAX_PAYLOAD) {
|
|
fprintf(stderr, "Longueur de reponse QEMU invalide.\n");
|
|
return -1;
|
|
}
|
|
responseLength = QA6_USB_HEADER_SIZE +
|
|
responseHeader->payload_length;
|
|
if (responseHeader->payload_length != 0 &&
|
|
Qa6ReceiveAll(qemu, response + QA6_USB_HEADER_SIZE,
|
|
(int)responseHeader->payload_length) != 0) {
|
|
fprintf(stderr, "Connexion QEMU interrompue dans les donnees.\n");
|
|
return -1;
|
|
}
|
|
if (Qa6ValidateFrame(response, responseLength,
|
|
QA6_USB_MESSAGE_RESPONSE) != 0 ||
|
|
responseHeader->request_id != requestHeader->request_id) {
|
|
fprintf(stderr, "Reponse QEMU invalide pour la requete #%lu.\n",
|
|
requestHeader->request_id);
|
|
return -1;
|
|
}
|
|
|
|
if (!WriteFile(driver, response, responseLength, &written, NULL) ||
|
|
written != responseLength) {
|
|
DWORD error = GetLastError();
|
|
fprintf(stderr, "Ecriture vers le pilote impossible: erreur %lu\n",
|
|
error);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *host = argc > 1 ? argv[1] : QA6_DEFAULT_HOST;
|
|
const char *port = argc > 2 ? argv[2] : QA6_DEFAULT_PORT;
|
|
WSADATA winsockData;
|
|
HANDLE driver = INVALID_HANDLE_VALUE;
|
|
SOCKET qemu = INVALID_SOCKET;
|
|
int exitCode = 1;
|
|
|
|
if (sizeof(Qa6UsbFrameHeader) != 32) {
|
|
fprintf(stderr, "Erreur interne: taille du protocole USB=%zu\n",
|
|
sizeof(Qa6UsbFrameHeader));
|
|
return 2;
|
|
}
|
|
|
|
SetConsoleCtrlHandler(Qa6ConsoleHandler, TRUE);
|
|
if (WSAStartup(MAKEWORD(2, 2), &winsockData) != 0) {
|
|
fprintf(stderr, "Initialisation Winsock impossible.\n");
|
|
return 2;
|
|
}
|
|
|
|
printf("Pont USB QEMU A6 natif Windows\n");
|
|
printf("Pilote : \\\\.\\QemuA6Ude0\n");
|
|
printf("QEMU : %s:%s\n", host, port);
|
|
|
|
while (InterlockedCompareExchange(&Qa6StopRequested, 0, 0) == 0) {
|
|
driver = Qa6OpenDriver();
|
|
if (driver != INVALID_HANDLE_VALUE) {
|
|
break;
|
|
}
|
|
printf("Attente du pilote QemuA6Ude...\n");
|
|
Sleep(1000);
|
|
}
|
|
if (driver == INVALID_HANDLE_VALUE) {
|
|
goto cleanup;
|
|
}
|
|
|
|
while (InterlockedCompareExchange(&Qa6StopRequested, 0, 0) == 0) {
|
|
qemu = Qa6ConnectQemu(host, port);
|
|
if (qemu != INVALID_SOCKET) {
|
|
break;
|
|
}
|
|
printf("Attente de QEMU sur %s:%s...\n", host, port);
|
|
Sleep(1000);
|
|
}
|
|
if (qemu == INVALID_SOCKET) {
|
|
goto cleanup;
|
|
}
|
|
|
|
printf("Connecte. idevicerestore peut utiliser l'iPhone DFU virtuel.\n");
|
|
exitCode = Qa6RelayLoop(driver, qemu) == 0 ? 0 : 1;
|
|
|
|
cleanup:
|
|
if (qemu != INVALID_SOCKET) {
|
|
shutdown(qemu, SD_BOTH);
|
|
closesocket(qemu);
|
|
}
|
|
if (driver != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(driver);
|
|
}
|
|
WSACleanup();
|
|
return exitCode;
|
|
}
|