From 855accae1cefa7de3fb2f3d04edc2b3d81e5dd90 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 18 Aug 2024 10:14:21 -0300 Subject: [PATCH] Adding signatures and documentation for openConnection and closeConnection --- README.md | 6 +- lib/LinkMobile.hpp | 223 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 173 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index c32bbe2..9f58073 100644 --- a/README.md +++ b/README.md @@ -437,8 +437,6 @@ You can also change these compile-time constants: - On fatal errors, the library will transition to a `NEEDS_RESET` state. In that case, you can call `getError()` to know more details on what happened, and then `activate()` to restart. - When calling `deactivate()`, the adapter automatically turns itself off after `3` seconds of inactivity. However, to gracefully turn it off, it's recommended to call `shutdown()` first, wait until the state is `SHUTDOWN`, and then `deactivate()`. -// TODO: Add ISP methods - Name | Return type | Description --- | --- | --- `isActive()` | **bool** | Returns whether the library is active or not. @@ -448,7 +446,9 @@ Name | Return type | Description `call(phoneNumber)` | **bool** | Initiates a P2P connection with a `phoneNumber`. After some time, the state will be `CALL_ESTABLISHED`, or `ACTIVE_SESSION` if the connection fails or ends. In REON/libmobile the phone number can be a number assigned by the relay server, or a 12-digit IPv4 address (for example, `"127000000001"` would be `127.0.0.1`). `callISP(password, loginId)` | **bool** | Calls the ISP number registered in the adapter configuration, or a default number if the adapter hasn't been configured. Then, performs a login operation using the provided `password` and `loginId`. After some time, the state will be `ISP_ACTIVE`. If `loginId` is empty and the adapter has been configured, it will use the one stored in the configuration. Both parameters are null-terminated strings (max `32` characters). `dnsQuery(domainName, result)` | **bool** | Looks up the IPv4 address for a `domainName` (a null-terminated string, max `253` characters). The `result` is a pointer to a `LinkMobile::DNSQuery` struct that will be filled with the result. When the request is completed, the `completed` field will be `true`. If an IP address was found, the `success` field will be `true` and the `ipv4` field can be read as a 4-byte address. -`transfer(dataToSend, result)` | **bool** | Requests a data transfer (up to `254` bytes) within a P2P connection and responds the received data. The `result` is a pointer to a `LinkMobile::DataTransfer` struct that will be filled with the received data. It can also point to `dataToSend` to reuse the struct. When the transfer is completed, the `completed` field will be `true`. +`openConnection(ip, port, type, result)` | **bool** | Opens a TCP/UDP (`type`) connection at the given `ip` (4-byte address) on the given `port`. The `result` is a pointer to a `LinkMobile::OpenConn` struct that will be filled with the result. When the request is completed, the `completed` field will be `true`. If the connection was successful, the `success` field will be `true` and the `connectionId` field can be used when calling the `transfer(...)` method. Only `2` connections can be opened at the same time. +`closeConnection(connectionId, type, result)` | **bool** | Closes an active TCP/UDP (`type`) connection. The `result` is a pointer to a `LinkMobile::CloseConn` struct that will be filled with the result. When the request is completed, the `completed` field will be `true`. If the connection was closed correctly, the `success` field will be `true`. +`transfer(dataToSend, result, [connectionId])` | **bool** | Requests a data transfer (up to `254` bytes) and responds the received data. The transfer can be done with the other node in a P2P connection, or with any open TCP/UDP connection if an ISP session is active. In the case of a TCP/UDP connection, the `connectionId` must be provided. The `result` is a pointer to a `LinkMobile::DataTransfer` struct that will be filled with the received data. It can also point to `dataToSend` to reuse the struct. When the request is completed, the `completed` field will be `true`. If the transfer was successful, the `success` field will be `true`. `hangUp()` | **bool** | Hangs up the current P2P or ISP call. Closes all connections. `readConfiguration(configurationData)` | **bool** | Retrieves the adapter configuration, and puts it in the `configurationData` struct. If the adapter has an active session, the data is already loaded, so it's instantaneous. `getState()` | **LinkMobile::State** | Returns the current state (one of `LinkMobile::State::NEEDS_RESET`, `LinkMobile::State::PINGING`, `LinkMobile::State::WAITING_TO_START`, `LinkMobile::State::STARTING_SESSION`, `LinkMobile::State::ACTIVATING_SIO32`, `LinkMobile::State::WAITING_32BIT_SWITCH`, `LinkMobile::State::READING_CONFIGURATION`, `LinkMobile::State::SESSION_ACTIVE`, `LinkMobile::State::CALL_REQUESTED`, `LinkMobile::State::CALLING`, `LinkMobile::State::CALL_ESTABLISHED`, `LinkMobile::State::ISP_CALL_REQUESTED`, `LinkMobile::State::ISP_CALLING`, `LinkMobile::State::ISP_LOGIN`, `LinkMobile::State::ISP_ACTIVE`, `LinkMobile::State::SHUTDOWN_REQUESTED`, `LinkMobile::State::ENDING_SESSION`, `LinkMobile::State::WAITING_8BIT_SWITCH`, or `LinkMobile::State::SHUTDOWN`). diff --git a/lib/LinkMobile.hpp b/lib/LinkMobile.hpp index 9fa5b94..ae97488 100644 --- a/lib/LinkMobile.hpp +++ b/lib/LinkMobile.hpp @@ -35,7 +35,18 @@ // linkMobile->dnsQuery("something.com", &dnsQuery); // // (do something until `dnsQuery.completed` is `true`) // // (use `dnsQuery.success` and `dnsQuery.ipv4`) -// - 7) Turn off the adapter: +// - 9) Open connections: +// auto type = LinkMobile::ConnectionType::TCP; +// LinkMobile::OpenConn openConn; +// linkMobile->openConnection(dnsQuery.ipv4, type, openConn); +// // (do something until `openConn.completed` is `true`) +// // (use `openConn.connectionId` as last argument of `transfer(...)`) +// - 10) Close connections: +// auto type = LinkMobile::ConnectionType::TCP; +// LinkMobile::CloseConn closeConn; +// linkMobile->closeConnection(openConn.connectionId, type, closeConn); +// // (do something until `openConn.completed` is `true`) +// - 11) Turn off the adapter: // linkMobile->shutdown(); // -------------------------------------------------------------------------- // (*) libtonc's interrupt handler sometimes ignores interrupts due to a bug. @@ -158,41 +169,8 @@ class LinkMobile { SHUTDOWN }; - enum CommandResult { - PENDING, - SUCCESS, - NOT_WAITING, - INVALID_DEVICE_ID, - INVALID_COMMAND_ACK, - INVALID_MAGIC_BYTES, - WEIRD_DATA_SIZE, - WRONG_CHECKSUM, - ERROR_CODE, - WEIRD_ERROR_CODE - }; - enum Role { NO_P2P_CONNECTION, CALLER, RECEIVER }; - struct Error { - enum Type { - NONE, - ADAPTER_NOT_CONNECTED, - ISP_LOGIN_FAILED, - COMMAND_FAILED, - WEIRD_RESPONSE, - TIMEOUT, - WTF - }; - - Error::Type type = Error::Type::NONE; - State state = State::NEEDS_RESET; - u8 cmdId = 0; - CommandResult cmdResult = CommandResult::PENDING; - u8 cmdErrorCode = 0; - bool cmdIsSending = false; - int reqType = -1; - }; - struct ConfigurationData { char magic[2]; u8 registrationState; @@ -215,16 +193,63 @@ class LinkMobile { char _ispNumber1[16 + 1]; // (parsed from `configurationSlot1`) } __attribute__((packed)); - struct DataTransfer { - u8 data[LINK_MOBILE_MAX_USER_TRANSFER_LENGTH] = {}; - u8 size = 0; - bool completed = false; - }; - struct DNSQuery { - u8 ipv4[4] = {}; bool completed = false; bool success = false; + u8 ipv4[4] = {}; + }; + + enum ConnectionType { TCP, UDP }; + + struct OpenConn { + bool completed = false; + bool success = false; + u8 connectionId = 0; + }; + + struct CloseConn { + bool completed = false; + bool success = false; + }; + + struct DataTransfer { + bool completed = false; + bool success = false; + u8 data[LINK_MOBILE_MAX_USER_TRANSFER_LENGTH] = {}; + u8 size = 0; + }; + + enum CommandResult { + PENDING, + SUCCESS, + NOT_WAITING, + INVALID_DEVICE_ID, + INVALID_COMMAND_ACK, + INVALID_MAGIC_BYTES, + WEIRD_DATA_SIZE, + WRONG_CHECKSUM, + ERROR_CODE, + WEIRD_ERROR_CODE + }; + + struct Error { + enum Type { + NONE, + ADAPTER_NOT_CONNECTED, + ISP_LOGIN_FAILED, + COMMAND_FAILED, + WEIRD_RESPONSE, + TIMEOUT, + WTF + }; + + Error::Type type = Error::Type::NONE; + State state = State::NEEDS_RESET; + u8 cmdId = 0; + CommandResult cmdResult = CommandResult::PENDING; + u8 cmdErrorCode = 0; + bool cmdIsSending = false; + int reqType = -1; }; /** @@ -368,8 +393,8 @@ class LinkMobile { size = LINK_MOBILE_MAX_DOMAIN_NAME_LENGTH; auto request = UserRequest{.type = UserRequest::Type::DNS_QUERY, - .send = {.data = {}}, .dns = result, + .send = {.data = {}}, .commandSent = false}; for (u32 i = 0; i < size; i++) request.send.data[i] = domainName[i]; @@ -380,22 +405,98 @@ class LinkMobile { } /** - * @brief Requests a data transfer within a P2P connection and responds the - * received data. + * @brief Opens a TCP/UDP (`type`) connection at the given `ip` (4-byte + * address) on the given `port`. + * @param ip The 4-byte address. + * @param port The port. + * @param type One of the enum values from `LinkMobile::ConnectionType`. + * @param result A pointer to a `LinkMobile::OpenConn` struct that + * will be filled with the result. When the request is completed, the + * `completed` field will be `true`. If the connection was successful, the + * `success` field will be `true` and the `connectionId` field can be used + * when calling the `transfer(...)` method. + * \warning Only `2` connections can be opened at the same time. + * \warning Non-blocking. Returns `true` immediately, or `false` if there's no + * active ISP session, no available request slots. + */ + bool openConnection(const u8* ip, + u16 port, + ConnectionType type, + OpenConn* result) { + if (state != ISP_ACTIVE || userRequests.isFull()) + return false; + + result->completed = false; + result->success = false; + + auto request = UserRequest{.type = UserRequest::Type::OPEN_CONNECTION, + .open = result, + .connectionType = type, + .commandSent = false}; + for (u32 i = 0; i < 4; i++) + request.ip[i] = ip[i]; + request.port = port; + + pushRequest(request); + return true; + } + // TODO: IMPLEMENT SOCKET METHODS + + /** + * @brief Closes an active TCP/UDP (`type`) connection. + * @param connectionId The ID of the connection. + * @param type One of the enum values from `LinkMobile::ConnectionType`. + * @param result A pointer to a `LinkMobile::CloseConn` struct that + * will be filled with the result. When the request is completed, the + * `completed` field will be `true`. If the connection was closed correctly, + * the `success` field will be `true`. + * \warning Non-blocking. Returns `true` immediately, or `false` if there's no + * active ISP session, no available request slots. + */ + bool closeConnection(u8 connectionId, + ConnectionType type, + CloseConn* result) { + if (state != ISP_ACTIVE || userRequests.isFull()) + return false; + + result->completed = false; + result->success = false; + + auto request = UserRequest{.type = UserRequest::Type::CLOSE_CONNECTION, + .close = result, + .connectionType = type, + .commandSent = false}; + request.connectionId = connectionId; + + pushRequest(request); + return true; + } + + /** + * @brief Requests a data transfer and responds the received data. The + * transfer can be done with the other node in a P2P connection, or with any + * open TCP/UDP connection if an ISP session is active. In the case of a + * TCP/UDP connection, the `connectionId` must be provided. * @param dataToSend The data to send, up to 254 bytes. * @param result A pointer to a `LinkMobile::DataTransfer` struct that * will be filled with the received data. It can also point to `dataToSend` to * reuse the struct. When the transfer is completed, the `completed` field - * will be `true`. - * \warning Non-blocking. Returns `true` immediately, or `false` if there's no - * active call or available request slots. + * will be `true`. If the transfer was successful, the `success` field will be + * `true`. + * \warning Non-blocking. Returns `true` immediately, or `false` if + * there's no active call or available request slots. */ - bool transfer(DataTransfer dataToSend, DataTransfer* result) { - if (state != CALL_ESTABLISHED || userRequests.isFull()) + bool transfer(DataTransfer dataToSend, + DataTransfer* result, + u8 connectionId = 0xff) { + if ((state != CALL_ESTABLISHED && state != ISP_ACTIVE) || + userRequests.isFull()) return false; result->completed = false; + result->success = false; auto request = UserRequest{.type = UserRequest::Type::TRANSFER, + .connectionId = connectionId, .send = {.data = {}, .size = dataToSend.size}, .receive = result, .commandSent = false}; @@ -591,15 +692,30 @@ class LinkMobile { enum AdapterType { BLUE, YELLOW, GREEN, RED, UNKNOWN }; struct UserRequest { - enum Type { CALL, ISP_LOGIN, DNS_QUERY, TRANSFER, HANG_UP, SHUTDOWN }; + enum Type { + CALL, + ISP_LOGIN, + DNS_QUERY, + OPEN_CONNECTION, + CLOSE_CONNECTION, + TRANSFER, + HANG_UP, + SHUTDOWN + }; Type type; char phoneNumber[LINK_MOBILE_MAX_PHONE_NUMBER_LENGTH + 1]; - DataTransfer send; - DataTransfer* receive; - DNSQuery* dns; char loginId[LINK_MOBILE_MAX_LOGIN_ID_LENGTH + 1]; char password[LINK_MOBILE_MAX_PASSWORD_LENGTH + 1]; + DNSQuery* dns; + OpenConn* open; + CloseConn* close; + u8 ip[4]; + u16 port; + ConnectionType connectionType; + u8 connectionId; + DataTransfer send; + DataTransfer* receive; bool commandSent; u32 timeout; bool finished; @@ -1022,6 +1138,7 @@ class LinkMobile { request->receive->data[i] = asyncCommand.cmd.data.bytes[1 + i]; request->receive->size = size; request->receive->completed = true; + request->receive->success = true; request->finished = true; } case ISP_CALLING: {