From 4bf9949f5dac289ecde1d62b79a8dce0592e3750 Mon Sep 17 00:00:00 2001 From: Simon Piriou Date: Sun, 10 Apr 2022 01:02:43 +0200 Subject: [PATCH] adb: Add adb reverse command support for tcp sockets This commit implements support for the following features: * Reverse a socket connection: adb reverse tcp: tcp: * List all reverse socket connections: adb reverse --list * Remove the specified reverse socket connection: adb reverse --remove * Remove all reverse socket connections adb reverse --remove-all --- CMakeLists.txt | 1 + adb.h | 48 +++++- adb_client.c | 75 +++++---- adb_list.c | 41 +++++ adb_list.h | 45 ++++++ hal/hal_uv_priv.h | 6 + hal/hal_uv_socket.c | 72 +++++++++ tcp_service.c | 365 ++++++++++++++++++++++++++++++++++++++++++++ tcp_service.h | 5 + 9 files changed, 620 insertions(+), 38 deletions(-) create mode 100644 adb_list.c create mode 100644 adb_list.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 39a5ebb..90a95a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ set (ADB_SRCS adb_banner.c adb_client.c adb_frame.c + adb_list.c hal/hal_uv.c hal/hal_uv_packet.c hal/hal_uv_client_tcp.c) diff --git a/adb.h b/adb.h index 51f0fe1..0664da0 100644 --- a/adb.h +++ b/adb.h @@ -27,6 +27,8 @@ #include #include +#include "adb_list.h" + #ifndef UNUSED #define UNUSED(x) (void)(x) #endif @@ -81,6 +83,13 @@ void adb_reboot_impl(const char *target); struct adb_client_s; struct adb_service_s; struct apacket_s; +#ifdef CONFIG_ADBD_SOCKET_SERVICE +struct adb_reverse_server_s; + +typedef struct adb_tcp_socket_s adb_tcp_socket_t; +typedef struct adb_tcp_conn_s adb_tcp_conn_t; +typedef struct adb_tcp_server_s adb_tcp_server_t; +#endif /**************************************************************************** * Public types @@ -116,10 +125,8 @@ typedef struct adb_service_ops_s { } adb_service_ops_t; typedef struct adb_service_s { - /* chain pointers for the local/remote list of - ** asockets that this asocket lives in - */ - struct adb_service_s *next; + /* Pointer used to keep track of all the services created by a client */ + adb_list_entry_t entry; const adb_service_ops_t *ops; /* the unique identifier for this service */ @@ -127,6 +134,19 @@ typedef struct adb_service_s { int peer_id; } adb_service_t; +#ifdef CONFIG_ADBD_SOCKET_SERVICE +typedef struct adb_reverse_server_s { + /* Pointer used to keep track of all the reverse servers + * created by a client. + */ + adb_list_entry_t entry; + + /* Reverse servers local and remote tcp ports */ + uint16_t local_port; + uint16_t remote_port; +} adb_reverse_server_t; +#endif + typedef struct adb_client_ops_s { int (*write)(struct adb_client_s *client, apacket *p); void (*kick)(struct adb_client_s *client); @@ -136,11 +156,15 @@ typedef struct adb_client_ops_s { typedef struct adb_client_s { const adb_client_ops_t *ops; int next_service_id; - adb_service_t *services; + adb_list_entry_t services; uint8_t is_connected; #ifdef CONFIG_ADBD_AUTHENTICATION uint8_t token[CONFIG_ADBD_TOKEN_SIZE]; #endif +#ifdef CONFIG_ADBD_SOCKET_SERVICE + /* Keep track of reverse servers attached to a client */ + adb_list_entry_t reverse_servers; +#endif } adb_client_t; typedef struct adb_context_s { @@ -198,8 +222,11 @@ extern const unsigned char *g_adb_public_keys[]; /* TCP services */ #ifdef CONFIG_ADBD_SOCKET_SERVICE -typedef struct adb_tcp_socket_s adb_tcp_socket_t; -typedef struct adb_tcp_conn_s adb_tcp_conn_t; +void adb_register_reverse_server(adb_client_t *client, + adb_reverse_server_t *server); +void adb_reverse_server_close(adb_client_t *client, + adb_reverse_server_t *server); +void adb_hal_destroy_reverse_server(adb_reverse_server_t *server); int adb_hal_socket_connect(struct adb_client_s *client, adb_tcp_socket_t *socket, int port, adb_tcp_conn_t *conn, @@ -208,6 +235,13 @@ int adb_hal_socket_connect(struct adb_client_s *client, adb_tcp_socket_t *socket void adb_hal_socket_close(adb_tcp_socket_t *socket, void (*close_cb)(adb_tcp_socket_t*)); +int adb_hal_server_listen(struct adb_client_s *client, adb_tcp_server_t *server, + int port, + void (*on_connect_cb)(adb_tcp_server_t*, adb_tcp_socket_t*)); + +void adb_hal_server_close(adb_tcp_server_t *server, + void (*close_cb)(adb_tcp_server_t*)); + int adb_hal_socket_write(adb_tcp_socket_t *socket, struct apacket_s *p, void (*cb)(struct adb_client_s*, adb_tcp_socket_t*, struct apacket_s*, bool fail)); diff --git a/adb_client.c b/adb_client.c index 95a071b..f7f47d6 100644 --- a/adb_client.c +++ b/adb_client.c @@ -169,6 +169,7 @@ static void handle_close_frame(adb_client_t *client, apacket *p) { svc = adb_client_find_service(client, p->msg.arg1, p->msg.arg0); if (svc != NULL) { + /* Service cannot send anything after CLOSE packet */ adb_service_close(client, svc, NULL); } adb_hal_apacket_release(client, p); @@ -334,9 +335,9 @@ void adb_send_data_frame(adb_client_t *client, apacket *p) void adb_register_service(adb_service_t *svc, adb_client_t *client) { svc->id = client->next_service_id++; - svc->next = client->services; + adb_log("id=%d, peer=%d\n", svc->id, svc->peer_id); - client->services = svc; + adb_list_insert(&client->services, &svc->entry); } static adb_service_t *adb_service_open(adb_client_t *client, const char *name, apacket *p) @@ -363,6 +364,10 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a svc = tcp_forward_service(client, name, p); break; } + if (!strncmp(name, "reverse:", 8)) { + svc = tcp_reverse_service(client, name, p); + break; + } #endif #if defined(CONFIG_ADBD_SHELL_SERVICE) || defined(CONFIG_ADBD_LOGCAT_SERVICE) @@ -391,7 +396,7 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a } while (0); if (svc == NULL) { - adb_log("fail to init service %s\n", name); + /* Either service init failed or service completed in init function */ return NULL; } @@ -401,58 +406,58 @@ static adb_service_t *adb_service_open(adb_client_t *client, const char *name, a } void adb_service_close(adb_client_t *client, adb_service_t *svc, apacket *p) { - adb_service_t *cur_svc = client->services; - - if (cur_svc == svc) { - client->services = svc->next; - goto exit_free_service; - } - - while (cur_svc->next) { - if (cur_svc->next == svc) { - cur_svc->next = svc->next; - goto exit_free_service; - } - } - - adb_log("service %p not found\n", svc); - return; + adb_list_remove(&client->services, &svc->entry); -exit_free_service: if (p) { send_close_frame(client, p, svc->id, svc->peer_id); } + svc->ops->on_close(svc); } +#ifdef CONFIG_ADBD_SOCKET_SERVICE +void adb_register_reverse_server(adb_client_t *client, + adb_reverse_server_t *server) { + adb_log("register tcp:%d <-> tcp:%d\n", + server->local_port, server->remote_port); + adb_list_insert(&client->reverse_servers, &server->entry); +} + +void adb_reverse_server_close(adb_client_t *client, + adb_reverse_server_t *server) { + adb_list_remove(&client->reverse_servers, &server->entry); + adb_hal_destroy_reverse_server(server); +} +#endif + static adb_service_t* adb_client_find_service(adb_client_t *client, int id, int peer_id) { adb_service_t *svc; - svc = client->services; - while (svc != NULL) { + adb_list_foreach(&client->services, svc, adb_service_t, entry) { if (svc->id == id && (peer_id == 0 || svc->peer_id == peer_id)) { return svc; } - svc = svc->next; } return NULL; } void adb_client_kick_services(adb_client_t *client) { - adb_service_t *service = client->services; - while (service != NULL) { + adb_service_t *service; + adb_list_foreach(&client->services, service, adb_service_t, entry) { if (service->ops->on_kick) { service->ops->on_kick(service); } - service = service->next; } } static void adb_init_client(adb_client_t *client) { /* setup adb_client */ client->next_service_id = 1; - client->services = NULL; + adb_list_init(&client->services); +#ifdef CONFIG_ADBD_SOCKET_SERVICE + adb_list_init(&client->reverse_servers); +#endif client->is_connected = 0; } @@ -467,13 +472,21 @@ adb_client_t *adb_create_client(size_t size) { } void adb_destroy_client(adb_client_t *client) { - adb_service_t *service = client->services; - while (service != NULL) { + while (!adb_list_empty(&client->services)) { + adb_service_t *service = container_of(adb_list_next(&client->services), + adb_service_t, entry); adb_log("stop service %d <-> %d\n", service->id, service->peer_id); - // FIXME send close frame ? adb_service_close(client, service, NULL); - service = service->next; } +#ifdef CONFIG_ADBD_SOCKET_SERVICE + while (!adb_list_empty(&client->reverse_servers)) { + adb_reverse_server_t *server = + container_of(adb_list_next(&client->reverse_servers), + adb_reverse_server_t, entry); + + adb_reverse_server_close(client, server); + } +#endif adb_hal_destroy_client(client); } diff --git a/adb_list.c b/adb_list.c new file mode 100644 index 0000000..c44cee8 --- /dev/null +++ b/adb_list.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Simon Piriou. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "adb_list.h" + +void adb_list_remove(adb_list_entry_t *head, adb_list_entry_t *entry) +{ + adb_list_entry_t *current = head->next; + if (head->next == NULL) { + /* List empty */ + return; + } + + if (current == entry) { + head->next = entry->next; + return; + } + + while (current->next) { + if (current->next == entry) { + current->next = entry->next; + return; + } + } + + /* Entry not found */ +} diff --git a/adb_list.h b/adb_list.h new file mode 100644 index 0000000..6f11651 --- /dev/null +++ b/adb_list.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Simon Piriou. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __ADB_LIST_H +#define __ADB_LIST_H + +#include + +typedef struct adb_list_entry_s { + struct adb_list_entry_s *next; +} adb_list_entry_t; + +#define adb_list_init(entry) (entry)->next = NULL +#define adb_list_empty(head) ((head)->next == NULL) +#define adb_list_next(entry) (entry)->next + +#define adb_list_foreach(head, entry, type, member) \ + for((entry) = container_of((head)->next, type, member);\ + (entry); \ + (entry) = container_of((entry)->member.next, type, member)) + + +static inline void adb_list_insert(adb_list_entry_t *head, + adb_list_entry_t *entry) { + entry->next = head->next; + head->next = entry; +} + +void adb_list_remove(adb_list_entry_t *head, adb_list_entry_t *entry); + +#endif diff --git a/hal/hal_uv_priv.h b/hal/hal_uv_priv.h index dd0aff2..f9a2842 100644 --- a/hal/hal_uv_priv.h +++ b/hal/hal_uv_priv.h @@ -64,6 +64,12 @@ struct adb_tcp_conn_s { uv_connect_t connect_req; void (*on_connect_cb)(adb_tcp_socket_t*, int); }; + +typedef struct adb_tcp_server_s { + uv_tcp_t handle; + void (*close_cb)(struct adb_tcp_server_s*); + void (*on_connect_cb)(struct adb_tcp_server_s*, adb_tcp_socket_t*); +} adb_tcp_server_t; #endif /**************************************************************************** diff --git a/hal/hal_uv_socket.c b/hal/hal_uv_socket.c index 6dedbce..da78dad 100644 --- a/hal/hal_uv_socket.c +++ b/hal/hal_uv_socket.c @@ -87,6 +87,16 @@ void adb_hal_socket_close(adb_tcp_socket_t *socket, void (*close_cb)(adb_tcp_soc uv_close((uv_handle_t*)&socket->handle, socket_close_cb); } +static void server_close_cb(uv_handle_t* handle) { + adb_tcp_server_t *server = container_of(handle, adb_tcp_server_t, handle); + server->close_cb(server); +} + +void adb_hal_server_close(adb_tcp_server_t *server, void (*close_cb)(adb_tcp_server_t*)) { + server->close_cb = close_cb; + uv_close((uv_handle_t*)&server->handle, server_close_cb); +} + int adb_hal_socket_start(adb_tcp_socket_t *socket, void (*on_data_cb)(adb_tcp_socket_t*, apacket*)) { @@ -160,3 +170,65 @@ int adb_hal_socket_connect(adb_client_t *client, adb_tcp_socket_t *socket, (const struct sockaddr*) &addr, connect_cb); } + +static void tcp_server_on_connection(uv_stream_t* server, int status) { + int ret; + adb_tcp_server_t *socket = + (adb_tcp_server_t*)container_of(server, adb_tcp_server_t, handle); + adb_client_uv_t *client = (adb_client_uv_t*)server->data; + // adb_rstream_service_t* rsvc; + adb_tcp_socket_t *stream_socket; + + adb_log("entry %d\n", status); + + if (status != 0) { + adb_log("Connect error %s\n", uv_err_name(status)); + } + assert(status == 0); + + server = server; + stream_socket = tcp_allocate_rstream_socket(&client->client); + if (stream_socket == NULL) { + fatal("out of memory"); + } + + assert(0 == uv_tcp_init(server->loop, &stream_socket->handle)); + stream_socket->handle.data = client; + + ret = uv_accept(server, (uv_stream_t*)&stream_socket->handle); + adb_log("ACCEPT %d\n", ret); + if (ret) { + // FIXME + tcp_release_rstream_socket(stream_socket); + // adb_service_close(&client->client, &rsvc->service, NULL); + return; + } + + socket->on_connect_cb(socket, stream_socket); +} + +int adb_hal_server_listen(adb_client_t *client, adb_tcp_server_t *server, int port, + void (*on_connect_cb)(adb_tcp_server_t*, adb_tcp_socket_t*)) { + int ret; + struct sockaddr_in addr; + uv_handle_t *handle = (uv_handle_t*)(((adb_client_uv_t*)client)+1); + + assert(0 == uv_ip4_addr("0.0.0.0", port, &addr)); + assert(0 == uv_tcp_init(handle->loop, &server->handle)); + if (uv_tcp_bind(&server->handle, (const struct sockaddr*) &addr, 0) != 0) { + adb_log("bind to 0.0.0.0:%d failed\n", port); + return -1; + } + + server->handle.data = client; + server->on_connect_cb = on_connect_cb; + + ret = uv_listen((uv_stream_t*)&server->handle, + SOMAXCONN, tcp_server_on_connection); + if (ret) { + adb_log("listen failed %d\n", ret); + return -1; + } + + return 0; +} diff --git a/tcp_service.c b/tcp_service.c index 6dd1f02..1074233 100644 --- a/tcp_service.c +++ b/tcp_service.c @@ -22,15 +22,20 @@ #include #include #include +#include #include "adb.h" #include "tcp_service.h" #include "hal/hal_uv_priv.h" +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define REVERSE_LIST_FMT "host tcp:%d tcp:%d\n" + enum stream_state { F_ERROR_CLOSE = 0, F_NOT_CONNECTED, F_NOTIFY_CLIENT, // forward only (we must send OKAY packet) + F_WAIT_OPEN_ACK, // reverse only (xe must send OPEN packet) F_CONNECTED, // Connected and ready to receive packets F_WAIT_ACK, // Connected but it cannot receive packets, waiting for ack }; @@ -43,11 +48,63 @@ typedef struct adb_stream_service_s { enum stream_state state; } adb_stream_service_t; +/* adb forward service */ + typedef struct atcp_fstream_service_s { adb_stream_service_t stream_service; adb_tcp_conn_t conn; } atcp_fstream_service_t; +/* adb reverse services */ + +struct atcp_reverse_server_s; + +typedef struct atcp_rstream_service_s { + adb_stream_service_t stream_service; + struct atcp_reverse_server_s *parent; +} atcp_rstream_service_t; + +typedef struct atcp_reverse_server_s { + adb_reverse_server_t server; + adb_tcp_server_t socket; +} atcp_reverse_server_t; + + + + + + + +static void tcp_error_printf(apacket *p, const char *fmt, ...) +{ + int len; + va_list ap; + char c; + va_start(ap, fmt); + + len = vsnprintf((char*)p->data+8, CONFIG_ADBD_PAYLOAD_SIZE-1-8, fmt, ap); + if (len >= CONFIG_ADBD_PAYLOAD_SIZE-1-8) { + assert(0); + } + + c = p->data[8]; + snprintf((char*)p->data, 8+1, "FAIL%04x", len); + p->data[8] = c; + p->write_len = len + 8; +} + + + + + + + + + + + + + static int atcp_stream_on_ack(adb_service_t *service, apacket *p); static int atcp_stream_on_write(adb_service_t *service, apacket *p); static void atcp_stream_on_kick(adb_service_t *service); @@ -60,6 +117,23 @@ static const adb_service_ops_t atcp_stream_ops = { .on_close = atcp_stream_on_close }; +static void atcp_server_release(adb_tcp_server_t *server) { + atcp_reverse_server_t *svc = + container_of(server, atcp_reverse_server_t, socket); + adb_log("releasing reverse %d -> %d\n", + svc->server.local_port, svc->server.remote_port); + free(svc); +} + +void adb_hal_destroy_reverse_server(struct adb_reverse_server_s *server) { + atcp_reverse_server_t *svc = container_of(server, atcp_reverse_server_t, server); + adb_hal_server_close(&svc->socket, atcp_server_release); +} + + + + + static void tcp_stream_on_data_cb(adb_tcp_socket_t *socket, apacket *p) { adb_stream_service_t *svc; @@ -101,6 +175,27 @@ static int atcp_stream_on_ack(adb_service_t *service, apacket *p) { return 0; } +static void try_open(adb_stream_service_t *svc) +{ + int ret; + apacket *p; + adb_log("entry\n"); + atcp_rstream_service_t *rsvc = container_of(svc, atcp_rstream_service_t, stream_service); + + p = adb_hal_apacket_allocate(svc->client); + if (p == NULL) { + /* kick callback will be called when a packet is free */ + return; + } + + svc->state = F_CONNECTED; + adb_hal_socket_start(&svc->socket, tcp_stream_on_data_cb); + + ret = sprintf((char*)p->data, "tcp:%d", rsvc->parent->server.remote_port); + adb_send_open_frame(svc->client, p, + svc->service.id, 0, ret+1); +} + static void try_close(adb_stream_service_t *svc) { apacket *p; @@ -137,6 +232,10 @@ static void atcp_stream_on_kick(adb_service_t *service) { case F_NOT_CONNECTED: case F_WAIT_ACK: break; + case F_WAIT_OPEN_ACK: + /* New connection to reverse server. OPEN needs to be sent */ + try_open(svc); + break; case F_CONNECTED: /* Resume read after failed allocation */ adb_hal_socket_start(&svc->socket, tcp_stream_on_data_cb); @@ -219,6 +318,8 @@ static void tcp_stream_on_connect_cb(adb_tcp_socket_t *socket, int status) { try_connect(svc); } +/* TCP forward socket service creation */ + adb_service_t* tcp_forward_service(adb_client_t *client, const char *params, apacket *p) { int port; @@ -258,3 +359,267 @@ adb_service_t* tcp_forward_service(adb_client_t *client, const char *params, apa p->write_len = APACKET_SERVICE_INIT_ASYNC; return &fsvc->stream_service.service; } + +/* Reverse server creation */ + +static void tcp_reverse_on_connect_cb(adb_tcp_server_t *socket, + adb_tcp_socket_t *stream_socket) { + atcp_rstream_service_t *rsvc = container_of(stream_socket, atcp_rstream_service_t, stream_service.socket); + atcp_reverse_server_t *svc = container_of(socket, atcp_reverse_server_t, socket); + + adb_log("entry %d %d\n", svc->server.local_port, svc->server.remote_port); + + // Register service properly now + rsvc->stream_service.service.ops = &atcp_stream_ops; + rsvc->parent = svc; + adb_register_service(&rsvc->stream_service.service, rsvc->stream_service.client); + + rsvc->stream_service.state = F_WAIT_OPEN_ACK; + try_open(&rsvc->stream_service); +} + +static adb_service_t* tcp_reverse_create_service(adb_client_t *client, const char *params, apacket *p) +{ + int ret; + int local_port, remote_port; + atcp_reverse_server_t *server; + + params += 12; // Skip "forward:tcp:" + + /* This is remote port for adb client but local port from the device view */ + local_port = atoi(params); + + params = strstr(params, "tcp:"); + if (params == NULL) { + return NULL; + } + + remote_port = atoi(¶ms[4]); + + if (!local_port || !remote_port) { + tcp_error_printf(p, "invalid ports %d / %d", local_port, remote_port); + return NULL; + } + + /* Check that local port does not already exist */ + adb_list_foreach(&client->reverse_servers, server, + atcp_reverse_server_t, server.entry) { + if (local_port == server->server.local_port) { + tcp_error_printf(p, "port %d used", local_port); + return NULL; + } + } + + server = (atcp_reverse_server_t*)malloc(sizeof(atcp_reverse_server_t)); + + if (server == NULL) { + return NULL; + } + + server->server.local_port = local_port; + server->server.remote_port = remote_port; + + ret = adb_hal_server_listen(client, &server->socket, local_port, + tcp_reverse_on_connect_cb); + if (ret) { + adb_log("listen failed %d\n", ret); + free(server); + tcp_error_printf(p, "Failed to listen on port %d", local_port); + return NULL; + } + /* Register reverse service and fake a one shot service */ + adb_register_reverse_server(client, &server->server); + p->write_len = sprintf((char*)p->data, "OKAY%04x%d", 4, local_port); + return NULL; +} + +/* Active reverse socket allocation */ + +adb_tcp_socket_t* tcp_allocate_rstream_socket(adb_client_t *client) +{ + atcp_rstream_service_t *service; + + service = (atcp_rstream_service_t*)malloc(sizeof(atcp_rstream_service_t)); + if (service == NULL) { + return NULL; + } + + service->stream_service.client = client; + service->stream_service.state = F_NOT_CONNECTED; // required ?? + return &service->stream_service.socket; +} + +void tcp_release_rstream_socket(adb_tcp_socket_t *socket) +{ + free(socket); +} + +/* Reverse list service */ + +typedef struct atcp_reverse_list_service_s { + adb_service_t service; + unsigned int index; + unsigned int length; + char data[0]; +} atcp_reverse_list_service_t; + +static int atcp_reverse_list_on_write(adb_service_t *service, apacket *p) { + UNUSED(service); + UNUSED(p); + return -1; +} + +static int atcp_reverse_list_on_ack(adb_service_t *service, apacket *p) { + atcp_reverse_list_service_t *svc = + container_of(service, atcp_reverse_list_service_t, service); + + p->write_len = min(svc->length - svc->index, CONFIG_ADBD_PAYLOAD_SIZE); + + /* Last frame ack */ + if (p->write_len == 0) + return -1; + + memcpy(p->data, &svc->data[svc->index], p->write_len); + svc->index += p->write_len; + return 0; +} + +static void atcp_reverse_list_on_close(struct adb_service_s *service) { + free(service); +} + +static const adb_service_ops_t atcp_reverse_list_ops = { + .on_write_frame = atcp_reverse_list_on_write, + .on_ack_frame = atcp_reverse_list_on_ack, + .on_kick = NULL, + .on_close = atcp_reverse_list_on_close +}; + +static int size_10(uint16_t port) +{ + if (port >= 10000) + return 5; + if (port >= 1000) + return 4; + if (port >= 100) + return 3; + return (port >= 10) ? 2 : 1; +} + +static adb_service_t* tcp_reverse_list_service(adb_client_t *client, apacket *p) +{ + adb_reverse_server_t *server; + atcp_reverse_list_service_t *rl_service; + unsigned int ret_len = 0; + char *data; + + if (adb_list_empty(&client->reverse_servers)) { + p->write_len = sprintf((char*)p->data, "0000"); + return NULL; + } + + /* Go through reverse list to compute returned message length */ + adb_list_foreach(&client->reverse_servers, server, + adb_reverse_server_t, entry) { + /* Substract trailing zero (-1) and the two %d (-4) */ + ret_len += sizeof(REVERSE_LIST_FMT) - 1 - 4 + + size_10(server->local_port) + + size_10(server->remote_port); + } + + /* Allocate reverse list service with space for content, + * +1 for sprintf() trailing '\0' + */ + rl_service = (atcp_reverse_list_service_t*)malloc( + sizeof(atcp_reverse_list_service_t) + ret_len + 1); + + if (rl_service == NULL) { + adb_log("failed to allocate service (content len=%d)\n", ret_len); + return NULL; + } + + rl_service->service.ops = &atcp_reverse_list_ops; + rl_service->index = 0; + rl_service->length = ret_len; + + /* Go through reverse list to generate message content */ + data = rl_service->data; + adb_list_foreach(&client->reverse_servers, server, + adb_reverse_server_t, entry) { + data += sprintf(data, REVERSE_LIST_FMT, + server->local_port, server->remote_port); + } + + p->write_len = sprintf((char*)p->data, "%04x", ret_len); + return &rl_service->service; +} + +/* TCP reverse server kill service */ + +static adb_service_t* tcp_reverse_kill_service(adb_client_t *client, const char *params, apacket *p) +{ + int local_port; + + params += 11; /* Skip "killforward" */ + if (!strncmp(params, "-all", 4)) { + adb_reverse_server_t *server; + + p->write_len = sprintf((char*)p->data, "OKAY"); + + while (!adb_list_empty(&client->reverse_servers)) { + server = container_of(client->reverse_servers.next, adb_reverse_server_t, entry); + adb_log("stop reverse server tcp:%d <-> tcp:%d\n", + server->local_port, server->remote_port); + adb_reverse_server_close(client, server); + } + return NULL; + } + + if (!strncmp(params, ":tcp:", 5)) { + adb_reverse_server_t *server; + + params += 5; /* Skip ":tcp:" */ + local_port = atoi(params); + + adb_list_foreach(&client->reverse_servers, server, + adb_reverse_server_t, entry) { + if (server->local_port == local_port) { + adb_log("stop reverse server tcp:%d <-> tcp:%d\n", + server->local_port, server->remote_port); + adb_reverse_server_close(client, server); + // TODO return??? + return NULL; + } + } + + tcp_error_printf(p, "cannot remove port %d", local_port); + return NULL; + } + + return NULL; +} + +/* TCP reverse service */ + +adb_service_t* tcp_reverse_service(adb_client_t *client, const char *params, apacket *p) +{ + /* Skip "reverse:" */ + params += 8; + + adb_log("start reverse service <%s>\n", params); + + if (!strncmp(params, "list-forward", 12)) { + return tcp_reverse_list_service(client, p); + } + + if (!strncmp(params, "killforward", 11)) { + return tcp_reverse_kill_service(client, params, p); + } + + if (!strncmp(params, "forward:tcp:", 12)) { + return tcp_reverse_create_service(client, params, p); + } + + return NULL; +} + diff --git a/tcp_service.h b/tcp_service.h index 669071e..265ad57 100644 --- a/tcp_service.h +++ b/tcp_service.h @@ -26,5 +26,10 @@ adb_service_t* tcp_forward_service(adb_client_t *client, const char *params, apacket *p); +adb_service_t* tcp_reverse_service(adb_client_t *client, const char *params, + apacket *p); + +adb_tcp_socket_t* tcp_allocate_rstream_socket(adb_client_t *client); +void tcp_release_rstream_socket(adb_tcp_socket_t *socket); #endif /* __TCP_SERVICE_H__ */