/* Android console command implementation. * * Copyright (c) 2014 Linaro Limited * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2 or later, as published by the Free Software Foundation. * * This program is distributed in the hope 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 . */ #include "android-console.h" #include "monitor/monitor.h" #include "qemu/sockets.h" #include "net/slirp.h" #include "slirp/libslirp.h" #include "qmp-commands.h" typedef struct { int is_udp; int host_port; int guest_port; } RedirRec; GList *redir_list; void android_monitor_print_error(Monitor *mon, const char *fmt, ...) { /* Print an error (typically a syntax error from the parser), with * the required "KO: " prefix. */ va_list ap; monitor_printf(mon, "KO: "); va_start(ap, fmt); monitor_vprintf(mon, fmt, ap); va_end(ap); } void android_console_kill(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "OK: killing emulator, bye bye\n"); monitor_suspend(mon); qmp_quit(NULL); } void android_console_quit(Monitor *mon, const QDict *qdict) { /* Don't print an OK response for success, just close the connection */ if (monitor_disconnect(mon)) { monitor_printf(mon, "KO: this connection doesn't support quitting\n"); } } #ifdef CONFIG_SLIRP void android_console_redir_list(Monitor *mon, const QDict *qdict) { if (!redir_list) { monitor_printf(mon, "no active redirections\n"); } else { GList *l; for (l = redir_list; l; l = l->next) { RedirRec *r = l->data; monitor_printf(mon, "%s:%-5d => %-5d\n", r->is_udp ? "udp" : "tcp", r->host_port, r->guest_port); } } monitor_printf(mon, "OK\n"); } static int parse_proto(const char *s) { if (!strcmp(s, "tcp")) { return 0; } else if (!strcmp(s, "udp")) { return 1; } else { return -1; } } static int parse_port(const char *s) { char *end; int port; port = strtol(s, &end, 10); if (*end != 0 || port < 1 || port > 65535) { return 0; } return port; } void android_console_redir_add(Monitor *mon, const QDict *qdict) { const char *arg = qdict_get_str(qdict, "arg"); char **tokens; int is_udp, host_port, guest_port; Slirp *slirp; Error *err = NULL; struct in_addr host_addr = { .s_addr = htonl(INADDR_LOOPBACK) }; struct in_addr guest_addr = { .s_addr = 0 }; RedirRec *redir; slirp = net_slirp_lookup(NULL, NULL, &err); if (err) { monitor_printf(mon, "KO: %s\n", error_get_pretty(err)); error_free(err); return; } /* Argument syntax: "proto:hostport:guestport" */ tokens = g_strsplit(arg, ":", 0); if (g_strv_length(tokens) != 3) { goto fail_syntax; } is_udp = parse_proto(tokens[0]); host_port = parse_port(tokens[1]); guest_port = parse_port(tokens[2]); if (is_udp < 0 || host_port == 0 || guest_port == 0) { goto fail_syntax; } g_strfreev(tokens); if (slirp_add_hostfwd(slirp, is_udp, host_addr, host_port, guest_addr, guest_port) < 0) { monitor_printf(mon, "KO: can't setup redirection, " "port probably used by another program on host\n"); return; } redir = g_new0(RedirRec, 1); redir->is_udp = is_udp; redir->host_port = host_port; redir->guest_port = guest_port; redir_list = g_list_append(redir_list, redir); monitor_printf(mon, "OK\n"); return; fail_syntax: monitor_printf(mon, "KO: bad redirection format, try " "(tcp|udp):hostport:guestport\n"); g_strfreev(tokens); } static gint redir_cmp(gconstpointer a, gconstpointer b) { const RedirRec *ra = a; const RedirRec *rb = b; /* For purposes of list deletion, only protocol and host port matter */ if (ra->is_udp != rb->is_udp) { return ra->is_udp - rb->is_udp; } return ra->host_port - rb->host_port; } void android_console_redir_del(Monitor *mon, const QDict *qdict) { const char *arg = qdict_get_str(qdict, "arg"); char **tokens; int is_udp, host_port; Slirp *slirp; Error *err = NULL; struct in_addr host_addr = { .s_addr = INADDR_ANY }; RedirRec rr; GList *entry; slirp = net_slirp_lookup(NULL, NULL, &err); if (err) { monitor_printf(mon, "KO: %s\n", error_get_pretty(err)); error_free(err); return; } /* Argument syntax: "proto:hostport" */ tokens = g_strsplit(arg, ":", 0); if (g_strv_length(tokens) != 2) { goto fail_syntax; } is_udp = parse_proto(tokens[0]); host_port = parse_port(tokens[1]); if (is_udp < 0 || host_port == 0) { goto fail_syntax; } g_strfreev(tokens); rr.is_udp = is_udp; rr.host_port = host_port; entry = g_list_find_custom(redir_list, &rr, redir_cmp); if (!entry || slirp_remove_hostfwd(slirp, is_udp, host_addr, host_port)) { monitor_printf(mon, "KO: can't remove unknown redirection (%s:%d)\n", is_udp ? "udp" : "tcp", host_port); return; } g_free(entry->data); redir_list = g_list_delete_link(redir_list, entry); monitor_printf(mon, "OK\n"); return; fail_syntax: monitor_printf(mon, "KO: bad redirection format, try (tcp|udp):hostport\n"); g_strfreev(tokens); } #else /* not CONFIG_SLIRP */ void android_console_redir_list(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n"); } void android_console_redir_add(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n"); } void android_console_redir_remove(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n"); } #endif void android_console_redir(Monitor *mon, const QDict *qdict) { const char *arg = qdict_get_try_str(qdict, "arg"); if (!arg) { goto fail; } monitor_printf(mon, "redir: arg %s\n", arg); return; fail: monitor_printf(mon, "allows you to add, list and remove and/or " "PORT redirection from the host to the device\n" "as an example, 'redir tcp:5000:6000' will route " "any packet sent to the host's TCP port 5000\n" "to TCP port 6000 of the emulated device\n" "\n" "available sub-commands:\n" " list list current redirections\n" " add add new redirection\n" " del remove existing redirection\n" "\n" "KO: missing sub-command\n"); }