diff --git a/3ds/include/utils/pksmbridge_api.h b/3ds/include/utils/pksmbridge_api.h
new file mode 100644
index 000000000..b1ce7c0e2
--- /dev/null
+++ b/3ds/include/utils/pksmbridge_api.h
@@ -0,0 +1,167 @@
+/*
+ * This file is part of PKSM
+ * Copyright (C) 2016-2021 mrhappyasthma, Flagbrew
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+ * * Requiring preservation of specified reasonable legal notices or
+ * author attributions in that material or in the Appropriate Legal
+ * Notices displayed by works containing it.
+ * * Prohibiting misrepresentation of the origin of that material,
+ * or requiring that modified versions of such material be marked in
+ * reasonable ways as different from the original version.
+ */
+
+/**
+ * This file defines the PKSM Bridge protocol API.
+ *
+ * Example data flow:
+ *
+ * Client -> Send `pksmBridgeRequest` to server and await acknowledgement of supported version
+ *
+ * Server: Checks the protocol version from the request to see if it is supported.
+ *
+ * <- Server send `pksmBridgeResponse`. If version is supported, the value matches
+ * one sent from the client. If unsupported, then the value is
+ * PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION.
+ *
+ * Client: Parses the `pksmBridgeResponse` to see if the server acknowledged and will send a
+ * file payload.
+ *
+ * (if the version requested by the client is supported)
+ * <- Server sends the `pksmBridgeFile`
+ *
+ * Client: Conditionally receives the file payload, if the version was supported.
+ */
+#ifndef PKSMBRIDGE_API_H
+#define PKSMBRIDGE_API_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+/** A string literal used to identify PKSM Bridge messages. */
+static const char PKSM_BRIDGE_PROTOCOL_NAME[] = "PKSMBRIDGE";
+
+/** The latest protocol version. */
+static const int PKSM_BRIDGE_LATEST_PROTOCOL_VERSION = 1;
+
+/** A sentinel value indicating an unsupported version number. */
+extern const int PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION;
+
+struct pksmBridgeRequest
+{
+ /**
+ * Used as a sentinel value to identify the request type.
+ * Contains `PKSM_BRIDGE_PROTOCOL_NAME`, without the null terminator.
+ */
+ char protocol_name[10];
+
+ /** The requested protocol version to use. */
+ int8_t protocol_version;
+};
+
+struct pksmBridgeResponse
+{
+ /**
+ * Used as a sentinel value to identify the request type.
+ * Contains `PKSM_BRIDGE_PROTOCOL_NAME`, without the null terminator.
+ */
+ char protocol_name[10];
+
+ /**
+ * If the requested protocol_version is supported by the server, this value will
+ * contain the same value to confirm support. If the requested version is not
+ * supported, it will contain `PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION`.
+ */
+ int8_t protocol_version;
+};
+
+struct pksmBridgeFile
+{
+ /** The size of the checksum. */
+ uint32_t checksumSize;
+
+ /**
+ * The checksum of `contents`. Used to verify data integrity after the transmission.
+ * The v1 protocol uses SHA256.
+ */
+ uint8_t* checksum;
+
+ /** The size of the file in bytes. */
+ uint32_t size;
+
+ /** A pointer to the file contents. The size of these contents is specified in `size`. */
+ uint8_t* contents;
+};
+
+enum pksmBridgeError
+{
+ /** Indicates that no error occurred (i.e. success). */
+ PKSM_BRIDGE_ERROR_NONE = 0,
+ /** The requested PKSM Bridge protocol version is not supported. */
+ PKSM_BRIDGE_ERROR_UNSUPPORTED_PROTCOL_VERSION = -1,
+ /** A connection error occurred and the PKSM Bridge could not be established. */
+ PKSM_BRIDGE_ERROR_CONNECTION_ERROR = -2,
+ /** The protocol encountered an error while reading data. */
+ PKSM_BRIDGE_DATA_READ_FAILURE = -3,
+ /** The protocol encountered an error while writing data. */
+ PKSM_BRIDGE_DATA_WRITE_FAILURE = -4,
+ /**
+ * The file received from the PKSM Bridge contains unexpected bytes.
+ * This likely indicates that a checksum failed.
+ */
+ PKSM_BRIDGE_DATA_FILE_CORRUPTED = -5,
+ /**
+ * A generic error indicating that an unexpected message was received that is not part
+ * of the PKSM Bridge protocol.
+ */
+ PKSM_BRIDGE_ERROR_UNEXPECTED_MESSAGE = -99999,
+};
+
+/**
+ * Create a PKSMBridge request with the specified protocol version.
+ *
+ * @param protocol_version The protocol version to specify in the request.
+ *
+ * @return The created bridge request.
+ */
+struct pksmBridgeRequest createPKSMBridgeRequest(int protocol_version);
+
+/**
+ * Create a PKSMBridge response corresponding to the given request.
+ *
+ * @param request The request that was received over the bridge.
+ * @param supportedProtocolVersionsFunc A pointer to a function that validates the
+ * support protocol versions by the client
+ * implementing the PKSM Bridge protocol. It
+ * takes one argument `version`, which is the
+ * requested version number from the request.
+ * It s a boolean indicating whether or not
+ * the `version` is supported by this client.
+ *
+ * @return The created bridge response.
+ */
+struct pksmBridgeResponse createPKSMBridgeResponseForRequest(
+ struct pksmBridgeRequest request, bool (*supportedProtocolVersionsFunc)(int version));
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // PKSMBRIDGE_API_H
\ No newline at end of file
diff --git a/3ds/include/utils/pksmbridge_tcp.h b/3ds/include/utils/pksmbridge_tcp.h
new file mode 100644
index 000000000..bb623b179
--- /dev/null
+++ b/3ds/include/utils/pksmbridge_tcp.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of PKSM
+ * Copyright (C) 2016-2021 mrhappyasthma, Flagbrew
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+ * * Requiring preservation of specified reasonable legal notices or
+ * author attributions in that material or in the Appropriate Legal
+ * Notices displayed by works containing it.
+ * * Prohibiting misrepresentation of the origin of that material,
+ * or requiring that modified versions of such material be marked in
+ * reasonable ways as different from the original version.
+ */
+
+#ifndef PKSMBRIDGE_TCP_H
+#define PKSMBRIDGE_TCP_H
+
+#include "pksmbridge_api.h"
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+/**
+ * Returns a boolean indicating whether or not the `version` is supported by this PKSM Bridge
+ * TCP implementation.
+ */
+bool checkSupportedPKSMBridgeProtocolVersionForTCP(int version);
+
+/**
+ * Send a file over the PKSM Bridge using TCP.
+ *
+ * @warning This is a blocking operation.
+ *
+ * @param port The TCP port to use for the PKSM Bridge connection.
+ * @param address The IP address to use for the PKSM Bridge connection.
+ * @param file The file to send over the PKSM Bridge connection.
+ *
+ * @return If the file was sent successfully, returns `PKSM_BRIDGE_ERROR_NONE`. If an error
+ * occurred, returns the corresponding `enum pksmBridgeError` error value.
+ */
+enum pksmBridgeError sendFileOverPKSMBridgeViaTCP(
+ uint16_t port, struct in_addr address, struct pksmBridgeFile file);
+
+/**
+ * Receive a file over the PKSM Bridge using TCP.
+ *
+ * @warning This is a blocking operation.
+ *
+ * @note Callers should check for an error in the return value before attempting to access
+ * any of the out parameters. The out parameters are not assigned unless the operation
+ * succeeds.
+ *
+ * @param port The TCP port to use for the PKSM Bridge connection.
+ * @param outAddress (out) Returns the IP address that was used for the PKSM Bridge connection.
+ * @param outFile (out) The file that was received over the PKSM Bridge connection. The client
+ * is responsible for calling `free()` on the data returned via `outFile`.
+ * @param outFileSize (out) The size of the `outFile` in bytes.
+ * @param validateChecksumFunc A function pointer to the function that should be used to evaluate
+ * the file's checksum. It takes one argument `file`, which is the
+ * received file from the PKSM Bridge. It is expected to return a
+ * boolean indicating whether or not the checksum in
+ * `file.checksum` matches one computed from the `file.contents`.
+ *
+ * @return If the file was received successfully, returns `PKSM_BRIDGE_ERROR_NONE`. If an error
+ * occurred, returns the corresponding `enum pksmBridgeError` error value.
+ */
+enum pksmBridgeError receiveFileOverPKSMBridgeViaTCP(uint16_t port, struct in_addr* outAddress,
+ uint8_t** outFile, uint32_t* outFileSize,
+ bool (*validateChecksumFunc)(struct pksmBridgeFile file));
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // PKSMBRIDGE_TCP_H
\ No newline at end of file
diff --git a/3ds/source/utils/pksmbridge.cpp b/3ds/source/utils/pksmbridge.cpp
index 38a87f6e0..33d8b51e9 100644
--- a/3ds/source/utils/pksmbridge.cpp
+++ b/3ds/source/utils/pksmbridge.cpp
@@ -33,8 +33,10 @@
#include "gui.hpp"
#include "i18n_ext.hpp"
#include "loader.hpp"
+#include "pksmbridge_api.h"
+#include "pksmbridge_tcp.h"
+#include "utils/crypto.hpp"
#include
-#include
namespace
{
@@ -47,6 +49,18 @@ namespace
addr.sin_addr.s_addr = gethostid();
return inet_ntoa(addr.sin_addr);
}
+
+ bool verifyPKSMBridgeFileSHA256Checksum(struct pksmBridgeFile file)
+ {
+ uint32_t expectedSHA256ChecksumSize = 32;
+ if (file.checksumSize != expectedSHA256ChecksumSize)
+ {
+ return false;
+ }
+ std::array checksum = pksm::crypto::sha256(file.contents, file.size);
+ int result = memcmp(checksum.data(), file.checksum, file.checksumSize);
+ return (result == 0) ? true : false;
+ }
}
bool isLoadedSaveFromBridge(void)
@@ -64,133 +78,86 @@ bool receiveSaveFromBridge(void)
fmt::format(i18n::localize("WIRELESS_IP"), getHostId())))
{
return false;
- }
-
- int fd;
- struct sockaddr_in servaddr;
- if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
- {
- Gui::error(i18n::localize("SOCKET_CREATE_FAIL"), errno);
- return false;
- }
-
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(PKSM_PORT);
- servaddr.sin_addr.s_addr = INADDR_ANY;
-
- if (bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
- {
- Gui::error(i18n::localize("SOCKET_BIND_FAIL"), errno);
- close(fd);
- return false;
- }
-
- if (listen(fd, 5) < 0)
- {
- Gui::error(i18n::localize("SOCKET_LISTEN_FAIL"), errno);
- close(fd);
- return false;
- }
+ };
+ uint8_t* file = nullptr;
+ uint32_t fileSize;
+ enum pksmBridgeError error = receiveFileOverPKSMBridgeViaTCP(
+ PKSM_PORT, &lastIPAddr, &file, &fileSize, &verifyPKSMBridgeFileSHA256Checksum);
- int fdconn;
- int addrlen = sizeof(servaddr);
- if ((fdconn = accept(fd, (struct sockaddr*)&servaddr, (socklen_t*)&addrlen)) < 0)
+ switch (error)
{
- Gui::error(i18n::localize("SOCKET_ACCEPT_FAIL"), errno);
- close(fd);
- return false;
- }
-
- lastIPAddr = servaddr.sin_addr;
-
- size_t size = 0x180B19;
- std::shared_ptr data = std::shared_ptr(new u8[size]);
-
- size_t total = 0;
- size_t chunk = 1024;
- int n;
- while (total < size)
- {
- size_t torecv = size - total > chunk ? chunk : size - total;
- n = recv(fdconn, &data[total], torecv, 0);
- total += n;
- if (n <= 0)
- {
+ case PKSM_BRIDGE_ERROR_NONE:
break;
- }
- fmt::print(stderr, "Recv {:d} bytes, {:d} still missing\n", total, size - total);
+ case PKSM_BRIDGE_ERROR_UNSUPPORTED_PROTCOL_VERSION:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON"), errno);
+ return false;
+ case PKSM_BRIDGE_ERROR_CONNECTION_ERROR:
+ Gui::error(i18n::localize("SOCKET_CONNECTION_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_READ_FAILURE:
+ Gui::error(i18n::localize("DATA_RECEIVE_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_WRITE_FAILURE:
+ Gui::error(i18n::localize("DATA_SEND_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_FILE_CORRUPTED:
+ Gui::error(i18n::localize("BRIDGE_ERROR_FILE_DATA_CORRUPTED"), errno);
+ return false;
+ case PKSM_BRIDGE_ERROR_UNEXPECTED_MESSAGE:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNEXPECTED_MESSAGE"), errno);
+ return false;
+ default:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNHANDLED"), errno);
+ return false;
}
- close(fdconn);
- close(fd);
-
- if (n == 0 || total == size)
- {
- if (TitleLoader::load(data, total))
- {
- saveFromBridge = true;
- Gui::setScreen(std::make_unique());
- }
- }
- else
+ std::shared_ptr data = std::shared_ptr(file, free);
+ if (!TitleLoader::load(data, fileSize))
{
- Gui::error(i18n::localize("DATA_RECEIVE_FAIL"), errno);
+ Gui::error(i18n::localize("BRIDGE_ERROR_FILE_DATA_CORRUPTED"), 2);
+ return false;
}
+ saveFromBridge = true;
+ Gui::setScreen(std::make_unique());
return true;
}
bool sendSaveToBridge(void)
{
- bool result = false;
- // send via TCP
- int fd;
- struct sockaddr_in servaddr;
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- Gui::error(i18n::localize("SOCKET_CREATE_FAIL"), errno);
- return result;
- }
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(PKSM_PORT);
- servaddr.sin_addr = lastIPAddr;
-
- if (connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
+ struct pksmBridgeFile file;
+ file.size = TitleLoader::save->getLength();
+ file.contents = TitleLoader::save->rawData().get();
+ std::array checksum = pksm::crypto::sha256(file.contents, file.size);
+ file.checksum = checksum.data();
+ file.checksumSize = checksum.size();
+ enum pksmBridgeError error = sendFileOverPKSMBridgeViaTCP(PKSM_PORT, lastIPAddr, file);
+ switch (error)
{
- Gui::error(i18n::localize("SOCKET_CONNECTION_FAIL"), errno);
- close(fd);
- return result;
+ case PKSM_BRIDGE_ERROR_NONE:
+ return true;
+ case PKSM_BRIDGE_ERROR_UNSUPPORTED_PROTCOL_VERSION:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON"), errno);
+ return false;
+ case PKSM_BRIDGE_ERROR_CONNECTION_ERROR:
+ Gui::error(i18n::localize("SOCKET_CONNECTION_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_READ_FAILURE:
+ Gui::error(i18n::localize("DATA_RECEIVE_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_WRITE_FAILURE:
+ Gui::error(i18n::localize("DATA_SEND_FAIL"), errno);
+ return false;
+ case PKSM_BRIDGE_DATA_FILE_CORRUPTED:
+ Gui::error(i18n::localize("BRIDGE_ERROR_FILE_DATA_CORRUPTED"), errno);
+ return false;
+ case PKSM_BRIDGE_ERROR_UNEXPECTED_MESSAGE:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNEXPECTED_MESSAGE"), errno);
+ return false;
+ default:
+ Gui::error(i18n::localize("BRIDGE_ERROR_UNHANDLED"), errno);
+ return false;
}
-
- size_t size = TitleLoader::save->getLength();
- size_t total = 0;
- size_t chunk = 1024;
- int n;
- while (total < size)
- {
- size_t tosend = size - total > chunk ? chunk : size - total;
- n = send(fd, TitleLoader::save->rawData().get() + total, tosend, 0);
- if (n == -1)
- {
- break;
- }
- total += n;
- fmt::print(stderr, "Recv {:d} bytes, {:d} still missing\n", total, size - total);
- }
- if (total == size)
- {
- // Gui::createInfo("Success!", "Data sent back correctly.");
- result = true;
- }
- else
- {
- Gui::error(i18n::localize("DATA_SEND_FAIL"), errno);
- }
-
- close(fd);
- return result;
}
void backupBridgeChanges()
diff --git a/3ds/source/utils/pksmbridge_api.c b/3ds/source/utils/pksmbridge_api.c
new file mode 100644
index 000000000..fdb019351
--- /dev/null
+++ b/3ds/source/utils/pksmbridge_api.c
@@ -0,0 +1,55 @@
+/*
+ * This file is part of PKSM
+ * Copyright (C) 2016-2021 mrhappyasthma, Flagbrew
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+ * * Requiring preservation of specified reasonable legal notices or
+ * author attributions in that material or in the Appropriate Legal
+ * Notices displayed by works containing it.
+ * * Prohibiting misrepresentation of the origin of that material,
+ * or requiring that modified versions of such material be marked in
+ * reasonable ways as different from the original version.
+ */
+
+#include "pksmbridge_api.h"
+#include
+#include
+#include
+
+const int PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION = -1;
+
+struct pksmBridgeRequest createPKSMBridgeRequest(int protocol_version)
+{
+ struct pksmBridgeRequest request;
+ memcpy(request.protocol_name, PKSM_BRIDGE_PROTOCOL_NAME,
+ sizeof(request.protocol_name) / sizeof(request.protocol_name[0]));
+ request.protocol_version = protocol_version;
+ return request;
+}
+
+struct pksmBridgeResponse createPKSMBridgeResponseForRequest(
+ struct pksmBridgeRequest request, bool (*supportedProtocolVersionsFunc)(int version))
+{
+ struct pksmBridgeResponse response;
+ memcpy(response.protocol_name, PKSM_BRIDGE_PROTOCOL_NAME,
+ sizeof(response.protocol_name) / sizeof(response.protocol_name[0]));
+ response.protocol_version = request.protocol_version;
+ if (!supportedProtocolVersionsFunc(request.protocol_version))
+ {
+ response.protocol_version = PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION;
+ }
+ return response;
+}
\ No newline at end of file
diff --git a/3ds/source/utils/pksmbridge_tcp.c b/3ds/source/utils/pksmbridge_tcp.c
new file mode 100644
index 000000000..ca1beddbf
--- /dev/null
+++ b/3ds/source/utils/pksmbridge_tcp.c
@@ -0,0 +1,312 @@
+/*
+ * This file is part of PKSM
+ * Copyright (C) 2016-2021 mrhappyasthma, Flagbrew
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+ * * Requiring preservation of specified reasonable legal notices or
+ * author attributions in that material or in the Appropriate Legal
+ * Notices displayed by works containing it.
+ * * Prohibiting misrepresentation of the origin of that material,
+ * or requiring that modified versions of such material be marked in
+ * reasonable ways as different from the original version.
+ */
+
+#include "pksmbridge_tcp.h"
+#include "pksmbridge_api.h"
+#include
+#include
+#include
+#include
+#include
+
+/** Creates a socket and returns the file descriptor. If an error occurs, returns -1. */
+static int createSocket(void)
+{
+ return socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+}
+
+/** Creates a socket address with the provided port and address. */
+static struct sockaddr_in createSocketAddress(uint16_t port, in_addr_t address)
+{
+ struct sockaddr_in servaddr;
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(port);
+ servaddr.sin_addr.s_addr = address;
+ return servaddr;
+}
+
+/** Sends chunks of `data` from a buffer to a socket. Returns the number of sent bytes. */
+static int sendDataFromBufferToSocket(int sockfd, void* buffer, size_t size)
+{
+ size_t total = 0;
+ size_t chunk = 1024;
+ while (total < size)
+ {
+ size_t tosend = size - total > chunk ? chunk : size - total;
+ int n = send(sockfd, ((char*)buffer) + total, tosend, 0);
+ if (n == -1)
+ {
+ break;
+ }
+ total += n;
+ }
+ return total;
+}
+
+/**
+ * Reads chunks of data from a socket into the provided `buffer`. Returns the
+ * number of read bytes.
+ */
+static int receiveDataFromSocketIntoBuffer(int sockfd, void* buffer, size_t size)
+{
+ size_t total = 0;
+ size_t chunk = 1024;
+ while (total < size)
+ {
+ size_t torecv = size - total > chunk ? chunk : size - total;
+ int n = recv(sockfd, ((char*)buffer) + total, torecv, 0);
+ if (n <= 0)
+ {
+ break;
+ }
+ total += n;
+ }
+ return total;
+}
+
+/** Expects the `protocol_name` field from either pksmBridgeRequest or pksmBridgeResponse. */
+static bool verifyPKSMBridgeHeader(char protocol_name[10])
+{
+ int result = strncmp(protocol_name, PKSM_BRIDGE_PROTOCOL_NAME, 10);
+ return (result == 0) ? true : false;
+}
+
+/** A helper function to send a file over the PKSM Bridge piece-by-piece to avoid additional memory
+ * allocations. */
+static bool sendPKSMBridgeFileToSocket(int sockfd, struct pksmBridgeFile file)
+{
+ uint32_t sentBytes =
+ sendDataFromBufferToSocket(sockfd, &file.checksumSize, sizeof(file.checksumSize));
+ if (sentBytes != sizeof(file.checksumSize))
+ {
+ return false;
+ }
+ sentBytes = sendDataFromBufferToSocket(sockfd, file.checksum, file.checksumSize);
+ if (sentBytes != file.checksumSize)
+ {
+ return false;
+ }
+ sentBytes = sendDataFromBufferToSocket(sockfd, &file.size, sizeof(file.size));
+ if (sentBytes != sizeof(file.size))
+ {
+ return false;
+ }
+ sentBytes = sendDataFromBufferToSocket(sockfd, file.contents, file.size);
+ if (sentBytes != file.size)
+ {
+ return false;
+ }
+ return true;
+}
+
+/** A helper function to receive file bytes over the PKSM Bridge. Callers are expected to `free()`
+ * the `outFile`. */
+static uint32_t receiveFileOverPKSMBridgeFromSocket(
+ int sockfd, uint8_t** outFile, bool (*validateChecksumFunc)(struct pksmBridgeFile file))
+{
+ uint32_t checksumSize;
+ uint32_t bytesRead =
+ receiveDataFromSocketIntoBuffer(sockfd, &checksumSize, sizeof(checksumSize));
+ if (bytesRead != sizeof(checksumSize))
+ {
+ return 0;
+ }
+ uint8_t* checksumBuffer = (uint8_t*)malloc(checksumSize);
+ bytesRead = receiveDataFromSocketIntoBuffer(sockfd, checksumBuffer, checksumSize);
+ if (bytesRead != checksumSize)
+ {
+ free(checksumBuffer);
+ return 0;
+ }
+ uint32_t fileSize;
+ bytesRead = receiveDataFromSocketIntoBuffer(sockfd, &fileSize, sizeof(fileSize));
+ if (bytesRead != sizeof(fileSize))
+ {
+ free(checksumBuffer);
+ return 0;
+ }
+ uint8_t* fileBuffer = (uint8_t*)malloc(fileSize);
+ bytesRead = receiveDataFromSocketIntoBuffer(sockfd, fileBuffer, fileSize);
+ if (bytesRead != fileSize)
+ {
+ free(fileBuffer);
+ free(checksumBuffer);
+ return 0;
+ }
+
+ // Construct a temporary `struct pksmBridgeFile` pointing to the corresponding buffers to pass
+ // to the validation function.
+ struct pksmBridgeFile file = {.checksumSize = checksumSize,
+ .checksum = checksumBuffer,
+ .size = fileSize,
+ .contents = fileBuffer};
+ if (!validateChecksumFunc(file))
+ {
+ return 0;
+ }
+ free(checksumBuffer);
+
+ *outFile = fileBuffer;
+ return fileSize;
+}
+
+bool checkSupportedPKSMBridgeProtocolVersionForTCP(int version)
+{
+ // This logic should be updated if newer protocol versions are introduced.
+ // For now, there is only a single protocol version.
+ return (version == 1);
+}
+
+enum pksmBridgeError sendFileOverPKSMBridgeViaTCP(
+ uint16_t port, struct in_addr address, struct pksmBridgeFile file)
+{
+ int fd = createSocket();
+ if (fd < 0)
+ {
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+
+ struct sockaddr_in servaddr = createSocketAddress(port, address.s_addr);
+ if (connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+
+ // Expect a pksmBridgeRequest, which specifies the protocol version.
+ struct pksmBridgeRequest request;
+ int bytesRead = receiveDataFromSocketIntoBuffer(fd, &request, sizeof(request));
+ if (bytesRead != sizeof(request))
+ {
+ close(fd);
+ return PKSM_BRIDGE_DATA_READ_FAILURE;
+ }
+ if (!verifyPKSMBridgeHeader(request.protocol_name))
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_UNEXPECTED_MESSAGE;
+ }
+
+ // Construct and send the pksmBridgeResponse.
+ struct pksmBridgeResponse response =
+ createPKSMBridgeResponseForRequest(request, &checkSupportedPKSMBridgeProtocolVersionForTCP);
+ int sentBytes = sendDataFromBufferToSocket(fd, &response, sizeof(response));
+ if (sentBytes != sizeof(response))
+ {
+ close(fd);
+ return PKSM_BRIDGE_DATA_WRITE_FAILURE;
+ }
+ if (response.protocol_version == PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION)
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_UNSUPPORTED_PROTCOL_VERSION;
+ }
+
+ // Send the pksmBridgeFile, since the protocol version was confirmed.
+ bool success = sendPKSMBridgeFileToSocket(fd, file);
+ close(fd);
+ return success ? PKSM_BRIDGE_ERROR_NONE : PKSM_BRIDGE_DATA_WRITE_FAILURE;
+}
+
+enum pksmBridgeError receiveFileOverPKSMBridgeViaTCP(uint16_t port, struct in_addr* outAddress,
+ uint8_t** outFile, uint32_t* outFileSize,
+ bool (*validateChecksumFunc)(struct pksmBridgeFile file))
+{
+ int fd = createSocket();
+ if (fd < 0)
+ {
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+
+ struct sockaddr_in servaddr = createSocketAddress(port, INADDR_ANY);
+ if (bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+ if (listen(fd, /*backlog=*/1) < 0)
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+
+ int fdconn;
+ int addrlen = sizeof(servaddr);
+ if ((fdconn = accept(fd, (struct sockaddr*)&servaddr, (socklen_t*)&addrlen)) < 0)
+ {
+ close(fd);
+ return PKSM_BRIDGE_ERROR_CONNECTION_ERROR;
+ }
+ close(fd);
+
+ // Send a pksmBridgeRequest, requesting a specific protocol version.
+ struct pksmBridgeRequest request = createPKSMBridgeRequest(PKSM_BRIDGE_LATEST_PROTOCOL_VERSION);
+ uint32_t sentBytes = sendDataFromBufferToSocket(fdconn, &request, sizeof(request));
+ if (sentBytes != sizeof(request))
+ {
+ close(fdconn);
+ return PKSM_BRIDGE_DATA_WRITE_FAILURE;
+ }
+
+ // Expect a pksmBridgeResponse and see if the protocol version was supported.
+ struct pksmBridgeResponse response;
+ uint32_t readBytes = receiveDataFromSocketIntoBuffer(fdconn, &response, sizeof(response));
+ if (readBytes != sizeof(response))
+ {
+ close(fdconn);
+ return PKSM_BRIDGE_DATA_READ_FAILURE;
+ }
+ if (!verifyPKSMBridgeHeader(response.protocol_name))
+ {
+ close(fdconn);
+ return PKSM_BRIDGE_ERROR_UNEXPECTED_MESSAGE;
+ }
+ if (response.protocol_version == PKSM_BRIDGE_UNSUPPORTED_PROTOCOL_VERSION)
+ {
+ close(fdconn);
+ return PKSM_BRIDGE_ERROR_UNSUPPORTED_PROTCOL_VERSION;
+ }
+
+ // Expect a pksmBridgeFile since the protocol version was confirmed.
+ uint8_t* fileBuffer = NULL;
+ uint32_t fileSize =
+ receiveFileOverPKSMBridgeFromSocket(fdconn, &fileBuffer, validateChecksumFunc);
+ close(fdconn);
+
+ if (fileSize == 0 || fileBuffer == NULL)
+ {
+ return PKSM_BRIDGE_DATA_FILE_CORRUPTED;
+ }
+
+ if (outAddress != NULL)
+ {
+ *outAddress = servaddr.sin_addr;
+ }
+ *outFileSize = fileSize;
+ *outFile = fileBuffer;
+ return PKSM_BRIDGE_ERROR_NONE;
+}
\ No newline at end of file
diff --git a/assets/gui_strings/chs/communication.json b/assets/gui_strings/chs/communication.json
index 5f6bd97a5..c931126d1 100644
--- a/assets/gui_strings/chs/communication.json
+++ b/assets/gui_strings/chs/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "您想要发送更改的文件",
"BRIDGE_SHOULD_SEND_2": "到原始客户端吗?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/cht/communication.json b/assets/gui_strings/cht/communication.json
index 5f6bd97a5..c931126d1 100644
--- a/assets/gui_strings/cht/communication.json
+++ b/assets/gui_strings/cht/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "您想要发送更改的文件",
"BRIDGE_SHOULD_SEND_2": "到原始客户端吗?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/eng/communication.json b/assets/gui_strings/eng/communication.json
index 246d0a54c..43fcf0a1c 100644
--- a/assets/gui_strings/eng/communication.json
+++ b/assets/gui_strings/eng/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Would you like to send the changed file",
"BRIDGE_SHOULD_SEND_2": "to the original client?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/fre/communication.json b/assets/gui_strings/fre/communication.json
index 9f38de214..b2e9d0234 100644
--- a/assets/gui_strings/fre/communication.json
+++ b/assets/gui_strings/fre/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Voulez-vous envoyer le nouveau fichier",
"BRIDGE_SHOULD_SEND_2": "vers votre Switch?",
"CLOUD_BOX": "Nuage {:d}",
diff --git a/assets/gui_strings/ger/communication.json b/assets/gui_strings/ger/communication.json
index 6be63c647..e64af8305 100644
--- a/assets/gui_strings/ger/communication.json
+++ b/assets/gui_strings/ger/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Möchtest Du die veränderte Datei",
"BRIDGE_SHOULD_SEND_2": "zum origin. Client senden?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/ita/communication.json b/assets/gui_strings/ita/communication.json
index 9b6ac49e2..a52440030 100644
--- a/assets/gui_strings/ita/communication.json
+++ b/assets/gui_strings/ita/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Vuoi mandare il salvataggio",
"BRIDGE_SHOULD_SEND_2": "al client originale?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/jpn/communication.json b/assets/gui_strings/jpn/communication.json
index c4bbb2cbe..1fb35904a 100644
--- a/assets/gui_strings/jpn/communication.json
+++ b/assets/gui_strings/jpn/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "変更したファイルを",
"BRIDGE_SHOULD_SEND_2": "元のクライアントに送りますか?",
"CLOUD_BOX": "クラウド {:d}",
diff --git a/assets/gui_strings/kor/communication.json b/assets/gui_strings/kor/communication.json
index 27cd45e92..6fc6bdb7b 100644
--- a/assets/gui_strings/kor/communication.json
+++ b/assets/gui_strings/kor/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "변경된 파일을 기준 클라이언트로",
"BRIDGE_SHOULD_SEND_2": "보내겠습니까?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/nl/communication.json b/assets/gui_strings/nl/communication.json
index bcb8ca01b..063ad2b5e 100644
--- a/assets/gui_strings/nl/communication.json
+++ b/assets/gui_strings/nl/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Wilt u het veranderde bestand naar",
"BRIDGE_SHOULD_SEND_2": "de originele client sturen?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/pt/communication.json b/assets/gui_strings/pt/communication.json
index ee72373b7..5256ace82 100644
--- a/assets/gui_strings/pt/communication.json
+++ b/assets/gui_strings/pt/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Você gostaria de enviar o arquivo modificado",
"BRIDGE_SHOULD_SEND_2": "ao Client original?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/ro/communication.json b/assets/gui_strings/ro/communication.json
index f8f5bfecb..fc3dd2f75 100644
--- a/assets/gui_strings/ro/communication.json
+++ b/assets/gui_strings/ro/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "Vrei să trimiți fişierul schimbat?",
"BRIDGE_SHOULD_SEND_2": "clientului original?",
"CLOUD_BOX": "Cloud {:d}",
diff --git a/assets/gui_strings/spa/communication.json b/assets/gui_strings/spa/communication.json
index 56851cff0..736adf891 100644
--- a/assets/gui_strings/spa/communication.json
+++ b/assets/gui_strings/spa/communication.json
@@ -1,4 +1,8 @@
{
+ "BRIDGE_ERROR_FILE_DATA_CORRUPTED": "Transfer failed. File data corrupted.",
+ "BRIDGE_ERROR_UNEXPECTED_MESSAGE" : "Unsupported PKSM Bridge protocol version.",
+ "BRIDGE_ERROR_UNHANDLED" : "Unhandled PKSM Bridge error occurred: {:d}.",
+ "BRIDGE_ERROR_UNSUPPORTED_PROTOCOL_VERISON" : "Unexpected message received over PKSM Bridge.",
"BRIDGE_SHOULD_SEND_1": "¿Te gustaría enviar el archivo modificado",
"BRIDGE_SHOULD_SEND_2": "al cliente original?",
"CLOUD_BOX": "Nube {:d}",