radare2/libr/core/rtr.c
2016-09-02 20:57:45 +02:00

1405 lines
38 KiB
C

/* radare - Copyright 2009-2016 - pancake, nibble */
#include "r_core.h"
#include "r_socket.h"
#if 0
SECURITY IMPLICATIONS
=====================
- no ssl
- no auth
- commands can be executed by anyone
- default is to listen on localhost
- can access full filesystem
- follow symlinks
#endif
#define USE_THREADS 1
#define rtr_n core->rtr_n
#define rtr_host core->rtr_host
static RSocket *s = NULL;
static RThread *httpthread = NULL;
static RThread *rapthread = NULL;
static const char *listenport = NULL;
typedef struct {
const char *host;
const char *port;
const char *file;
} TextLog;
typedef struct {
RCore *core;
int launch;
char *path;
} HttpThread;
typedef struct {
RCore *core;
const char* input;
} RapThread;
static char *rtrcmd (TextLog T, const char *str) {
char *res, *ptr2;
char *ptr = r_str_uri_encode (str);
char *uri = r_str_newf ("http://%s:%s/%s%s", T.host, T.port, T.file, ptr? ptr: str);
int len;
free (ptr);
ptr2 = r_socket_http_get (uri, NULL, &len);
free (uri);
if (ptr2) {
ptr2[len] = 0;
res = strstr (ptr2, "\n\n");
if (res) res = strstr (res + 1, "\n\n");
return res? res + 2: ptr2;
// return res; // ptr2;
}
return NULL;
}
static void showcursor(RCore *core, int x) {
if (core && core->vmode) {
r_cons_show_cursor (x);
r_cons_enable_mouse (x? r_config_get_i (core->config, "scr.wheel"): false);
} else r_cons_enable_mouse (false);
r_cons_flush ();
}
// TODO: rename /name to /nick or /so?
// clone of textlog_chat () using rtrcmd()
static void rtr_textlog_chat (RCore *core, TextLog T) {
char prompt[64];
char buf[1024];
int lastmsg = 0;
const char *me = r_config_get (core->config, "cfg.user");
char *ret, msg[1024];
eprintf ("Type '/help' for commands and ^D to quit:\n");
char *oldprompt = strdup (r_line_singleton ()->prompt);
snprintf (prompt, sizeof (prompt) - 1, "[%s]> ", me);
r_line_set_prompt (prompt);
ret = rtrcmd (T, msg);
for (;;) {
if (lastmsg >= 0) {
snprintf (msg, sizeof (msg) - 1, "T %d", lastmsg);
} else {
strcpy (msg, "T");
}
ret = rtrcmd (T, msg);
r_cons_println (ret);
free (ret);
ret = rtrcmd (T, "Tl");
lastmsg = atoi (ret)-1;
free (ret);
if (r_cons_fgets (buf, sizeof (buf) - 1, 0, NULL) < 0) {
goto beach;
}
if (!*buf) continue;
if (!strcmp (buf, "/help")) {
eprintf ("/quit quit the chat (same as ^D)\n");
eprintf ("/nick <nick> set cfg.user nick name\n");
eprintf ("/log show full log\n");
eprintf ("/clear clear text log messages\n");
} else if (!strncmp (buf, "/nick ", 6)) {
snprintf (msg, sizeof (msg) - 1, "* '%s' is now known as '%s'", me, buf+6);
r_cons_println (msg);
r_core_log_add (core, msg);
r_config_set (core->config, "cfg.user", buf+6);
me = r_config_get (core->config, "cfg.user");
snprintf (prompt, sizeof (prompt) - 1, "[%s]> ", me);
r_line_set_prompt (prompt);
} else if (!strcmp (buf, "/log")) {
char *ret = rtrcmd (T, "T");
if (ret) {
r_cons_println (ret);
free (ret);
}
} else if (!strcmp (buf, "/clear")) {
//r_core_log_del (core, 0);
free (rtrcmd (T, "T-"));
} else if (!strcmp (buf, "/quit")) {
goto beach;
} else if (*buf=='/') {
eprintf ("Unknown command: %s\n", buf);
} else {
snprintf (msg, sizeof (msg) - 1, "T [%s] %s", me, buf);
free (rtrcmd (T, msg));
}
}
beach:
r_line_set_prompt (oldprompt);
free (oldprompt);
}
static bool rtr_visual (RCore *core, TextLog T, const char *cmd) {
bool autorefresh = false;
if (cmd) {
r_cons_break (NULL, NULL);
for (;;) {
char *ret;
r_cons_clear00 ();
ret = rtrcmd (T, cmd);
r_cons_println (ret);
free (ret);
r_cons_flush ();
if (r_cons_singleton ()->breaked)
break;
r_sys_sleep (1);
}
r_cons_break_end ();
} else {
const char *cmds[] = { "px", "pd", "pxa", "dr", "sr SP;pxa", NULL };
int cmdidx = 0;
char *ret, ch;
free (rtrcmd (T, "e scr.color=true"));
free (rtrcmd (T, "e scr.html=false"));
for (;;) {
r_cons_clear00 ();
ret = rtrcmd (T, cmds[cmdidx]);
if (ret) {
r_cons_println (ret);
free (ret);
}
r_cons_flush ();
if (autorefresh) {
r_cons_printf ("(auto-refresh)\n");
r_cons_flush ();
r_cons_break (NULL, NULL);
r_sys_sleep (1);
if (r_cons_singleton ()->breaked) {
autorefresh = false;
ch = r_cons_readchar ();
} else {
r_cons_break_end ();
continue;
}
r_cons_break_end ();
} else {
ch = r_cons_readchar ();
}
#if 0
TODO:
i insert hex/string/asm
0-9 follow jumps
#endif
switch (ch) {
case '?':
r_cons_clear00 ();
r_cons_printf ("Remote Visual keys:\n"
" hjkl : move\n"
" HJKL : move faster\n"
" +-*/ : change block size\n"
" pP : rotate print modes\n"
" T : enter TextLog chat console\n"
" @ : enter auto-refresh mode\n"
" i : insert hexpair\n"
" q : quit this mode and go back to the shell\n"
" sS : step / step over\n"
" . : seek entry or pc\n");
r_cons_flush ();
r_cons_any_key (NULL);
break;
case 'i':
{
#if __UNIX__
#define COLORFLAGS (core->print->flags & R_PRINT_FLAGS_COLOR)
#else
#define COLORFLAGS 0
#endif
char buf[1024];
if (COLORFLAGS) {
r_line_set_prompt (Color_RESET":> ");
} else {
r_line_set_prompt (":> ");
}
showcursor (core, true);
r_cons_fgets (buf+3, sizeof (buf) - 4, 0, NULL);
memcpy (buf, "wx ", 3);
if (buf[3]) {
char *res = rtrcmd (T, buf);
if (res) {
r_cons_println (res);
free (res);
}
r_cons_flush ();
}
}
break;
case 's':
free (rtrcmd (T, "ds;.dr*"));
break;
case 'S':
free (rtrcmd (T, "dso;.dr*"));
break;
case '.':
free (rtrcmd (T, "s entry0;dr?rip;?? sr PC"));
break;
case ':':
{
int ret;
eprintf ("Press <enter> to return to Visual mode.\n");
do {
char buf[1024];
#if __UNIX__
r_line_set_prompt (Color_RESET":> ");
#else
r_line_set_prompt (":> ");
#endif
showcursor (core, true);
r_cons_fgets (buf, sizeof (buf), 0, NULL);
if (*buf) {
r_line_hist_add (buf);
char *res = rtrcmd (T, buf);
if (res) {
r_cons_println (res);
free (res);
}
r_cons_flush ();
ret = true;
} else {
ret = false;
//r_cons_any_key ();
r_cons_clear00 ();
showcursor (core, false);
}
} while (ret);
}
break;
case '@': autorefresh = true; break;
case 'j':
if (cmdidx == 1) {
free (rtrcmd (T, "so")); break;
} else {
free (rtrcmd (T, "s+16")); break;
}
break;
case 'k': free (rtrcmd (T, "s-16")); break;
case 'h': free (rtrcmd (T, "s-1")); break;
case 'l': free (rtrcmd (T, "s+1")); break;
case 'J':
if (cmdidx==1) {
free (rtrcmd (T, "4so"));
} else {
free (rtrcmd (T, "s+32"));
} break;
case 'K': free (rtrcmd (T, "s-32")); break;
case 'H': free (rtrcmd (T, "s-2")); break;
case 'L': free (rtrcmd (T, "s+2")); break;
case 'T': rtr_textlog_chat (core, T); break;
case '+': free (rtrcmd (T, "b+1")); break;
case '*': free (rtrcmd (T, "b+16")); break;
case '-': free (rtrcmd (T, "b-1")); break;
case '/': free (rtrcmd (T, "b-16")); break;
case 'p': cmdidx++; if (!cmds[cmdidx]) cmdidx = 0; break;
case 'P': cmdidx--; if (cmdidx<0) cmdidx = 2; break;
case 'q': return false;
}
}
}
return true;
}
R_API int r_core_rtr_http_stop(RCore *u) {
RCore *core = (RCore*)u;
const int timeout = 1; // 1 second
const char *port;
RSocket* sock;
#if __WINDOWS__
r_socket_http_server_set_breaked (&r_cons_singleton()->breaked);
#endif
if (((size_t)u) > 0xff) {
port = listenport? listenport: r_config_get (
core->config, "http.port");
sock = r_socket_new (0);
(void)r_socket_connect (sock, "localhost",
port, R_SOCKET_PROTO_TCP, timeout);
r_socket_free (sock);
}
r_socket_free (s);
s = NULL;
return 0;
}
static char *rtr_dir_files (const char *path) {
char *ptr = strdup ("<html><body>\n");
const char *file;
RListIter *iter;
// list files
RList *files = r_sys_dir (path);
eprintf ("Listing directory %s\n", path);
r_list_foreach (files, iter, file) {
if (file[0] == '.') continue;
ptr = r_str_concatf (ptr, "<a href=\"%s%s\">%s</a><br />\n",
path, file, file);
}
r_list_free (files);
return r_str_concat (ptr, "</body></html>\n");
}
#if __UNIX__
static void dietime (int sig) {
eprintf ("It's Die Time!\n");
exit (0);
}
#endif
static void activateDieTime (RCore *core) {
int dt = r_config_get_i (core->config, "http.dietime");
if (dt > 0) {
#if __UNIX__
signal (SIGALRM, dietime);
alarm (dt);
#else
eprintf ("http.dietime only works on *nix systems\n");
#endif
}
}
// return 1 on error
static int r_core_rtr_http_run (RCore *core, int launch, const char *path) {
RConfig *newcfg = NULL, *origcfg = NULL;
char headers[128] = {0};
RSocketHTTPRequest *rs;
char buf[32];
int ret = 0;
RSocket *s;
char *dir;
int iport, timeout = r_config_get_i (core->config, "http.timeout");
const char *host = r_config_get (core->config, "http.bind");
const char *root = r_config_get (core->config, "http.root");
const char *homeroot = r_config_get (core->config, "http.homeroot");
const char *port = r_config_get (core->config, "http.port");
const char *allow = r_config_get (core->config, "http.allow");
const char *httpui = r_config_get (core->config, "http.ui");
if (!r_file_is_directory (root)) {
if (!r_file_is_directory (homeroot)) {
eprintf ("Cannot find http.root (%s) or http.homeroot (%s)\n", root, homeroot);
return false;
}
return false;
}
if (path && atoi (path)) {
port = path;
path = NULL;
} else {
if (core->file && (!path || !*path)) {
if (!strcmp (httpui, "p")
|| !strcmp (httpui, "m")
|| !strcmp (httpui, "enyo")
|| !strcmp (httpui, "t")) {
path = httpui;
}
}
}
if (!strcmp (port, "0")) {
r_num_irand ();
iport = 1024 + r_num_rand (45256);
snprintf (buf, sizeof (buf), "%d", iport);
port = buf;
}
s = r_socket_new (false);
{
if (host && *host) {
if (!strcmp (host, "::1")) {
s->local = true;
} else if (!strcmp (host, "localhost")) {
s->local = true;
} else if (!strcmp (host, "127.0.0.1")) {
s->local = true;
} else if (!strcmp (host, "local")) {
s->local = true;
r_config_set (core->config, "http.bind", "localhost");
} else if (host[0]=='0' || !strcmp (host, "public")) {
// public
host = "127.0.0.1";
r_config_set (core->config, "http.bind", "0.0.0.0");
s->local = false;
} else {
s->local = true;
}
} else {
s->local = true;
}
}
if (!r_socket_listen (s, port, NULL)) {
r_socket_free (s);
eprintf ("Cannot listen on http.port\n");
return 1;
}
if (launch=='H') {
const char *browser = r_config_get (core->config, "http.browser");
r_sys_cmdf ("%s http://%s:%d/%s &",
browser, host, atoi (port), path? path:"");
}
origcfg = core->config;
newcfg = r_config_clone (core->config);
core->config = newcfg;
r_config_set (core->config, "asm.cmtright", "false");
r_config_set (core->config, "scr.html", "true");
r_config_set (core->config, "scr.color", "false");
r_config_set (core->config, "asm.bytes", "false");
r_config_set (core->config, "scr.interactive", "false");
if (r_config_get_i (core->config, "http.sandbox")) {
//(void)r_config_get_i (core->config, "cfg.sandbox");
r_config_set (core->config, "cfg.sandbox", "true");
}
eprintf ("Starting http server...\n");
eprintf ("open http://%s:%d/\n", host, atoi (port));
eprintf ("r2 -C http://%s:%d/cmd/\n", host, atoi (port));
core->http_up = true;
ut64 newoff, origoff = core->offset;
int newblksz, origblksz = core->blocksize;
ut8 *newblk, *origblk = core->block;
newblk = malloc (core->blocksize);
memcpy (newblk, core->block, core->blocksize);
core->block = newblk;
// TODO: handle mutex lock/unlock here
while (!r_cons_singleton ()->breaked) {
r_cons_break ((RConsBreak)r_core_rtr_http_stop, core);
/* restore environment */
core->config = origcfg;
r_config_set (origcfg, "scr.html", r_config_get (origcfg, "scr.html"));
r_config_set (origcfg, "scr.color", r_config_get (origcfg, "scr.color"));
r_config_set (origcfg, "scr.interactive", r_config_get (origcfg, "scr.interactive"));
core->http_up = 0; // DAT IS NOT TRUE AT ALL.. but its the way to enable visual
newoff = core->offset;
newblk = core->block;
newblksz = core->blocksize;
core->offset = origoff;
core->block = origblk;
core->blocksize = origblksz;
// backup and restore offset and blocksize
/* this is blocking */
activateDieTime (core);
rs = r_socket_http_accept (s, timeout);
origoff = core->offset;
origblk = core->block;
origblksz = core->blocksize;
core->offset = newoff;
core->block = newblk;
core->blocksize = newblksz;
/* set environment */
// backup and restore offset and blocksize
core->http_up = 1;
core->config = newcfg;
r_config_set (newcfg, "scr.html", r_config_get (newcfg, "scr.html"));
r_config_set (newcfg, "scr.color", r_config_get (newcfg, "scr.color"));
r_config_set (newcfg, "scr.interactive", r_config_get (newcfg, "scr.interactive"));
if (!rs) {
r_sys_usleep (100);
continue;
}
if (allow && *allow) {
int accepted = false;
const char *allows_host;
char *p, *peer = r_socket_to_string (rs->s);
char *allows = strdup (allow);
//eprintf ("Firewall (%s)\n", allows);
int i, count = r_str_split (allows, ',');
p = strchr (peer, ':');
if (p) *p = 0;
for (i = 0; i < count; i++) {
allows_host = r_str_word_get0 (allows, i);
//eprintf ("--- (%s) (%s)\n", host, peer);
if (!strcmp (allows_host, peer)) {
accepted = true;
break;
}
}
free (peer);
free (allows);
if (!accepted) {
r_socket_http_close (rs);
continue;
}
}
if (!rs->method || !rs->path) {
eprintf ("Invalid http headers received from client\n");
r_socket_http_close (rs);
continue;
}
dir = NULL;
if (r_config_get_i (core->config, "http.verbose")) {
char *peer = r_socket_to_string (rs->s);
eprintf ("[HTTP] %s %s\n", peer, rs->path);
free (peer);
}
if (r_config_get_i (core->config, "http.dirlist"))
if (r_file_is_directory (rs->path))
dir = strdup (rs->path);
if (r_config_get_i (core->config, "http.cors")) {
strcpy (headers, "Access-Control-Allow-Origin: *\n"
"Access-Control-Allow-Headers: Origin, "
"X-Requested-With, Content-Type, Accept\n");
}
if (!strcmp (rs->method, "OPTIONS")) {
r_socket_http_response (rs, 200, "", 0, headers);
} else
if (!strcmp (rs->method, "GET")) {
if (!strncmp (rs->path, "/up/", 4)) {
if (r_config_get_i (core->config, "http.upget")) {
const char *uproot = r_config_get (core->config, "http.uproot");
if (!rs->path[3] || (rs->path[3]=='/' && !rs->path[4])) {
char *ptr = rtr_dir_files (uproot);
r_socket_http_response (rs, 200, ptr, 0, headers);
free (ptr);
} else {
char *path = r_file_root (uproot, rs->path + 4);
if (r_file_exists (path)) {
int sz = 0;
char *f = r_file_slurp (path, &sz);
if (f) {
r_socket_http_response (rs, 200, f, sz, headers);
free (f);
} else {
r_socket_http_response (rs, 403, "Permission denied", 0, headers);
eprintf ("http: Cannot open '%s'\n", path);
}
} else {
if (dir) {
char *resp = rtr_dir_files (dir);
r_socket_http_response (rs, 404, resp, 0, headers);
free (resp);
} else {
eprintf ("File '%s' not found\n", path);
r_socket_http_response (rs, 404, "File not found\n", 0, headers);
}
}
free (path);
}
} else {
r_socket_http_response (rs, 403,
"Permission denied\n", 0, NULL);
}
} else if (!strncmp (rs->path, "/cmd/", 5)) {
char *cmd = rs->path +5;
const char *httpcmd = r_config_get (core->config, "http.uri");
const char *httpref = r_config_get (core->config, "http.referer");
bool httpref_enabled;
char *refstr = NULL;
if (httpref && *httpref) {
httpref_enabled = true;
if (strstr (httpref, "http")) {
refstr = strdup (httpref);
} else {
refstr = r_str_newf ("http://localhost:%d/", atoi (port));
}
} else {
httpref_enabled = false;
}
while (*cmd=='/') cmd++;
if (httpref_enabled && (!rs->referer || (refstr && !strstr (rs->referer, refstr)))) {
r_socket_http_response (rs, 503, "", 0, headers);
} else {
if (httpcmd && *httpcmd) {
int len; // do remote http query and proxy response
char *res, *bar = r_str_newf ("%s/%s", httpcmd, cmd);
res = r_socket_http_get (bar, NULL, &len);
if (res) {
res[len] = 0;
r_cons_println (res);
}
free (bar);
} else {
char *out, *cmd = rs->path + 5;
r_str_uri_decode (cmd);
r_config_set (core->config, "scr.interactive", "false");
if (!strcmp (cmd, "=h*")) {
if (r_sandbox_enable (0)) {
out = NULL;
} else {
/* do stuff */
out = NULL;
r_socket_http_close (rs);
free (dir);
free (refstr);
ret = -2;
goto the_end;
}
}
if (*cmd == ':') {
/* commands in /cmd/: starting with : do not show any output */
r_core_cmd0 (core, cmd + 1);
out = NULL;
} else {
// eprintf ("CMD (%s)\n", cmd);
out = r_core_cmd_str_pipe (core, cmd);
}
// eprintf ("\nOUT LEN = %d\n", strlen (out));
if (out) {
char *res = r_str_uri_encode (out);
char *newheaders = r_str_newf (
"Content-Type: text/plain\n%s", headers);
r_socket_http_response (rs, 200, out, 0, newheaders);
free (out);
free (newheaders);
free (res);
} else r_socket_http_response (rs, 200, "", 0, headers);
}
}
free (refstr);
} else {
const char *root = r_config_get (core->config, "http.root");
const char *homeroot = r_config_get (core->config, "http.homeroot");
char *path;
if (!strcmp (rs->path, "/")) {
free (rs->path);
rs->path = strdup ("/index.html");
}
if (homeroot && *homeroot) {
char *homepath = r_file_abspath (homeroot);
path = r_file_root (homepath, rs->path);
free (homepath);
if (!r_file_exists (path) && !r_file_is_directory (path)) {
free (path);
path = r_file_root (root, rs->path);
}
} else {
path = r_file_root (root, rs->path);
}
// FD IS OK HERE
if (rs->path [strlen (rs->path) - 1] == '/') {
path = r_str_concat (path, "index.html");
//rs->path = r_str_concat (rs->path, "index.html");
} else {
//snprintf (path, sizeof (path), "%s/%s", root, rs->path);
if (r_file_is_directory (path)) {
char *res = r_str_newf ("Location: %s/\n%s", rs->path, headers);
r_socket_http_response (rs, 302, NULL, 0, res);
r_socket_http_close (rs);
free (path);
free (res);
R_FREE (dir);
continue;
}
}
if (r_file_exists (path)) {
int sz = 0;
char *f = r_file_slurp (path, &sz);
if (f) {
const char *ct = NULL;
if (strstr (path, ".js")) ct = "Content-Type: application/javascript\n";
if (strstr (path, ".css")) ct = "Content-Type: text/css\n";
if (strstr (path, ".html")) ct = "Content-Type: text/html\n";
char *hdr = r_str_newf ("%s%s", ct, headers);
r_socket_http_response (rs, 200, f, sz, hdr);
free (hdr);
free (f);
} else {
r_socket_http_response (rs, 403, "Permission denied", 0, headers);
eprintf ("http: Cannot open '%s'\n", path);
}
} else {
if (dir) {
char *resp = rtr_dir_files (dir);
eprintf ("Dirlisting %s\n", dir);
r_socket_http_response (rs, 404, resp, 0, headers);
free (resp);
} else {
eprintf ("File '%s' not found\n", path);
r_socket_http_response (rs, 404, "File not found\n", 0, headers);
}
}
free (path);
}
} else
if (!strcmp (rs->method, "POST")) {
ut8 *ret;
int retlen;
char buf[128];
if (r_config_get_i (core->config, "http.upload")) {
ret = r_socket_http_handle_upload (rs->data, rs->data_length, &retlen);
if (ret) {
ut64 size = r_config_get_i (core->config, "http.maxsize");
if (size && retlen > size) {
r_socket_http_response (rs, 403, "403 File too big\n", 0, headers);
} else {
char *filename = r_file_root (
r_config_get (core->config, "http.uproot"),
rs->path + 4);
eprintf ("UPLOADED '%s'\n", filename);
r_file_dump (filename, ret, retlen, 0);
free (filename);
snprintf (buf, sizeof (buf),
"<html><body><h2>uploaded %d bytes. Thanks</h2>\n", retlen);
r_socket_http_response (rs, 200, buf, 0, headers);
}
free (ret);
}
} else {
r_socket_http_response (rs, 403, "403 Forbidden\n", 0, headers);
}
} else {
r_socket_http_response (rs, 404, "Invalid protocol", 0, headers);
}
r_socket_http_close (rs);
free (dir);
}
the_end:
{
int timeout = r_config_get_i (core->config, "http.timeout");
const char *host = r_config_get (core->config, "http.bind");
const char *port = r_config_get (core->config, "http.port");
const char *cors = r_config_get (core->config, "http.cors");
const char *allow = r_config_get (core->config, "http.allow");
const char *httpui = r_config_get (core->config, "http.ui");
core->config = origcfg;
r_config_set_i (core->config, "http.timeout", timeout);
r_config_set (core->config, "http.bind", host);
r_config_set (core->config, "http.port", port);
r_config_set (core->config, "http.cors", cors);
r_config_set (core->config, "http.allow", allow);
r_config_set (core->config, "http.ui", httpui);
}
r_cons_break_end ();
core->http_up = false;
r_socket_free (s);
r_config_free (newcfg);
r_config_set (origcfg, "scr.html", r_config_get (origcfg, "scr.html"));
r_config_set (origcfg, "scr.color", r_config_get (origcfg, "scr.color"));
r_config_set (origcfg, "scr.interactive", r_config_get (origcfg, "scr.interactive"));
return ret;
}
static int r_core_rtr_http_thread (RThread *th) {
int ret;
if (!th) {
return false;
}
HttpThread *ht = th->user;
if (!ht || !ht->core) {
return false;
}
ret = r_core_rtr_http_run (ht->core, ht->launch, ht->path);
R_FREE (ht->path);
return ret;
}
R_API int r_core_rtr_http(RCore *core, int launch, const char *path) {
int ret;
if (r_sandbox_enable (0)) {
eprintf ("sandbox: connect disabled\n");
return 1;
}
if (launch == '-') {
if (httpthread) {
eprintf ("Press ^C to stop the webserver\n");
r_th_free (httpthread);
httpthread = NULL;
} else {
eprintf ("No webserver running\n");
}
return 0;
}
if (core->http_up) {
eprintf ("http server is already running\n");
return 1;
}
if (launch == '&') {
if (httpthread) {
eprintf ("HTTP Thread is already running\n");
eprintf ("This is experimental and probably buggy. Use at your own risk\n");
eprintf ("TODO: Use different eval environ for scr. for the web\n");
eprintf ("TODO: Visual mode should be enabled on local\n");
} else {
const char *tpath = r_str_trim_const (path + 1);
HttpThread ht = { core, launch, strdup (tpath) };
httpthread = r_th_new (r_core_rtr_http_thread, &ht, false);
r_th_start (httpthread, true);
eprintf ("Background http server started.\n");
}
return 0;
}
do {
ret = r_core_rtr_http_run (core, launch, path);
} while (ret == -2);
return ret;
}
R_API void r_core_rtr_help(RCore *core) {
const char* help_msg[] = {
"Usage:", " =[:!+-=hH] [...]", " # radare remote command execution protocol",
"\nrap commands:", "", "",
"=", "", "list all open connections",
"=<", "[fd] cmd", "send output of local command to remote fd",
"=", "[fd] cmd", "exec cmd at remote 'fd' (last open is default one)",
"=!", " cmd", "run command via r_io_system",
"=+", " [proto://]host", "add host (default=rap://, tcp://, udp://)",
"=-", "[fd]", "remove all hosts or host 'fd'",
"==", "[fd]", "open remote session with host 'fd', 'q' to quit",
"=!=", "", "disable remote cmd mode",
"!=!", "", "enable remote cmd mode",
"\nrap server:","","",
"=", ":port", "listen on given port using rap protocol (o rap://9999)",
"=&", ":port", "start rap server in background",
"=", ":host:port cmd", "run 'cmd' command on remote server",
"\nhttp server:", "", "",
"=h", " port", "listen for http connections (r2 -qc=H /bin/ls)",
"=h-", "", "stop background webserver",
"=h*", "", "restart current webserver",
"=h&", " port", "start http server in background)",
"=H", " port", "launch browser and listen for http",
"=H&", " port", "launch browser and listen for http in background",
NULL };
r_core_cmd_help (core, help_msg);
}
R_API void r_core_rtr_pushout(RCore *core, const char *input) {
int fd = atoi (input);
const char *cmd = NULL;
char *str = NULL;
if (fd) {
for (rtr_n = 0; rtr_host[rtr_n].fd->fd != fd \
&& rtr_n < RTR_MAX_HOSTS - 1; rtr_n++);
if (!(cmd = strchr (input, ' '))) {
eprintf ("Error\n");
return;
}
} else cmd = input;
if (!rtr_host[rtr_n].fd->fd) {
eprintf("Error: Unknown host\n");
return;
}
if (!(str = r_core_cmd_str (core, cmd))) {
eprintf ("Error: radare_cmd_str returned NULL\n");
return;
}
switch (rtr_host[rtr_n].proto) {
case RTR_PROT_RAP:
eprintf ("Error: Cannot use '=<' to a rap connection.\n");
break;
case RTR_PROT_TCP:
case RTR_PROT_UDP:
default:
r_socket_write (rtr_host[rtr_n].fd, str, strlen (str));
break;
}
free (str);
}
R_API void r_core_rtr_list(RCore *core) {
int i;
for (i = 0; i < RTR_MAX_HOSTS; i++) {
if (!rtr_host[i].fd)
continue;
r_cons_printf ("%i - ", rtr_host[i].fd->fd);
switch (rtr_host[i].proto) {
case RTR_PROT_HTTP: r_cons_printf ( "http://"); break;
case RTR_PROT_TCP: r_cons_printf ("tcp://"); break;
case RTR_PROT_UDP: r_cons_printf ("udp://"); break;
default: r_cons_printf ("rap://"); break;
}
r_cons_printf ("%s:%i/%s\n", rtr_host[i].host,
rtr_host[i].port, rtr_host[i].file);
}
}
R_API void r_core_rtr_add(RCore *core, const char *_input) {
char *port, input[1024], *host = NULL, *file = NULL, *ptr = NULL, buf[1024];
int proto, i, timeout, ret;
RSocket *fd;
timeout = r_config_get_i (core->config, "http.timeout");
strncpy (input, _input, sizeof (input) - 4);
input[sizeof(input)-4] = '\0';
/* Parse uri */
if ((ptr = strstr (input, "tcp://"))) {
proto = RTR_PROT_TCP;
host = ptr + 6;
} else if ((ptr = strstr(input, "http://"))) {
proto = RTR_PROT_HTTP;
host = ptr + 7;
} else if ((ptr = strstr(input, "udp://"))) {
proto = RTR_PROT_UDP;
host = ptr + 6;
} else if ((ptr = strstr(input, "rap://"))) {
proto = RTR_PROT_RAP;
host = ptr + 6;
} else {
proto = RTR_PROT_RAP;
host = input;
}
while (*host && iswhitechar (*host))
host++;
if (!(ptr = strchr (host, ':'))) {
ptr = host;
port = "80";
} else {
*ptr++ = '\0';
port = ptr;
}
if (!(file = strchr (ptr, '/'))) {
eprintf ("Error: Missing '/'\n");
return;
}
*file++ = 0;
port = r_str_chop (port);
while (*file == ' ') file++;
if (r_sandbox_enable (0)) {
eprintf ("sandbox: connect disabled\n");
return;
}
fd = r_socket_new (false);
if (!fd) {
eprintf ("Error: Cannot create new socket\n");
return;
}
switch (proto) {
case RTR_PROT_HTTP:
{
char prompt[64], prompt2[64], *str, *ptr;
int len, flen = strlen (file);
bool is_valid, is_visual;
const char* res;
is_visual = (file[flen - 1] == 'V');
is_valid = (file[flen - (is_visual? 2: 1)] == '/');
if (is_valid) {
TextLog T = { host, port, file };
if (is_visual) {
file[flen - 1] = 0; // remove V from url
rtr_visual (core, T, NULL);
}
snprintf (prompt, sizeof (prompt), "[http://%s:%s/%s]> ",
host, port, file);
snprintf (prompt2, sizeof (prompt2), "[%s:%s]$ ", host, port);
for (;;) {
r_line_set_prompt (prompt);
res = r_line_readline ();
if (!res || !*res) break;
if (*res == 'q') break;
if (!strcmp (res, "!sh")) {
for (;;) {
r_line_set_prompt (prompt2);
res = r_line_readline ();
if (!res || !*res || !strcmp (res, "exit")) {
break;
}
ptr = r_str_uri_encode (res);
char *uri = r_str_newf ("http://%s:%s/%s!%s", host, port, file, res);
str = r_socket_http_get (uri, NULL, &len);
if (str) {
str[len] = 0;
res = strstr (str, "\n\n");
if (res) res = strstr (res+1, "\n\n");
res = res? res + 2: str;
const char *tail = (res[strlen (res) - 1] == '\n')? "": "\n";
printf ("%s%s", res, tail);
r_line_hist_add (str);
free (str);
}
free (ptr);
free (uri);
}
} else if (res[0]=='V') {
if (res[1]==' ') {
rtr_visual (core, T, res+1);
} else {
rtr_visual (core, T, NULL);
}
} else if (!strcmp (res, "TT")) {
rtr_textlog_chat (core, T);
} else {
ptr = r_str_uri_encode (res);
if (ptr) res = ptr;
char *uri = r_str_newf ("http://%s:%s/%s%s", host, port, file, res);
if (ptr == res) free (ptr);
str = r_socket_http_get (uri, NULL, &len);
if (str) {
str[len] = 0;
res = strstr (str, "\n\n");
if (res) res = strstr (res+1, "\n\n");
if (res) res += 2; else res = str;
printf ("%s%s", res, (res[strlen (res)-1]=='\n')?"":"\n");
r_line_hist_add (str);
free (str);
}
free (uri);
}
}
} else {
char *http_uri = r_str_newf ("http://%s:%s/%s", host, port, file);
str = r_socket_http_get (http_uri, NULL, &len);
if (str) {
str[len] = 0;
res = strstr (str, "\n\n");
if (res) res = strstr (res + 1, "\n\n");
printf ("%s", res? res + 2: str);
free (str);
} else eprintf ("HTTP connection has failed\n");
free (http_uri);
}
r_socket_free (fd);
return;
}
break;
case RTR_PROT_RAP:
if (!r_socket_connect_tcp (fd, host, port, timeout)) { //TODO: Use rap.ssl
eprintf ("Error: Cannot connect to '%s' (%s)\n", host, port);
r_socket_free (fd);
return;
}
eprintf ("Connected to %s at port %s\n", host, port);
/* send */
buf[0] = RTR_RAP_OPEN;
buf[1] = 0;
buf[2] = (ut8)(strlen (file) + 1);
memcpy (buf + 3, file, buf[2]);
r_socket_write (fd, buf, 3 + buf[2]);
/* read */
eprintf ("waiting... ");
fflush (stdout);
r_socket_read (fd, (ut8*)buf, 5);
i = r_read_at_be32 (buf, 1);
if (buf[0] != (char)(RTR_RAP_OPEN | RTR_RAP_REPLY) || i <= 0) {
eprintf ("Error: Wrong reply\n");
r_socket_free (fd);
return;
}
eprintf ("ok\n");
break;
case RTR_PROT_TCP:
if (!r_socket_connect_tcp (fd, host, port, timeout)) { //TODO: Use rap.ssl
core->num->value = 1;
eprintf ("Error: Cannot connect to '%s' (%s)\n", host, port);
r_socket_free (fd);
return;
}
core->num->value = 0;
eprintf ("Connected to: %s at port %s\n", host, port);
break;
case RTR_PROT_UDP:
if (!r_socket_connect_udp (fd, host, port, timeout)) { //TODO: Use rap.ssl
core->num->value = 1;
eprintf ("Error: Cannot connect to '%s' (%s)\n", host, port);
r_socket_free (fd);
return;
}
core->num->value = 0;
eprintf ("Connected to: %s at port %s\n", host, port);
break;
}
ret = core->num->value;
for (i = 0; i < RTR_MAX_HOSTS; i++) {
if (rtr_host[i].fd)
continue;
rtr_host[i].proto = proto;
strncpy (rtr_host[i].host, host, sizeof (rtr_host[i].proto)-1);
rtr_host[i].port = r_num_get (core->num, port);
strncpy (rtr_host[i].file, file, sizeof (rtr_host[i].file)-1);
rtr_host[i].fd = fd;
rtr_n = i;
break;
}
core->num->value = ret;
// double free wtf is freed this here? r_socket_free(fd);
//r_core_rtr_list (core);
}
R_API void r_core_rtr_remove(RCore *core, const char *input) {
int fd, i;
if (input[0] >= '0' && input[0] <= '9') {
fd = r_num_math (core->num, input);
for (i = 0; i < RTR_MAX_HOSTS; i++)
if (rtr_host[i].fd && rtr_host[i].fd->fd == fd) {
r_socket_free (rtr_host[i].fd);
rtr_host[i].fd = NULL;
if (rtr_n == i)
for (rtr_n = 0; !rtr_host[rtr_n].fd \
&& rtr_n < RTR_MAX_HOSTS - 1; rtr_n++);
break;
}
} else {
for (i = 0; i < RTR_MAX_HOSTS; i++) {
if (rtr_host[i].fd) {
r_socket_free (rtr_host[i].fd);
rtr_host[i].fd = NULL;
}
}
memset (rtr_host, '\0', RTR_MAX_HOSTS * sizeof (RCoreRtrHost));
rtr_n = 0;
}
}
R_API void r_core_rtr_session(RCore *core, const char *input) {
char prompt[64], buf[1024];
int fd;
prompt[0] = 0;
if (input[0] >= '0' && input[0] <= '9') {
fd = r_num_math (core->num, input);
for (rtr_n = 0; rtr_host[rtr_n].fd \
&& rtr_host[rtr_n].fd->fd != fd \
&& rtr_n < RTR_MAX_HOSTS - 1; rtr_n++);
}
for (;;) {
if (rtr_host[rtr_n].fd)
snprintf (prompt, sizeof (prompt),
"fd:%d> ", rtr_host[rtr_n].fd->fd);
free (r_line_singleton ()->prompt);
r_line_singleton ()->prompt = strdup (prompt);
if (r_cons_fgets (buf, sizeof (buf), 0, NULL) < 1)
break;
if (!*buf || *buf == 'q')
break;
if (*buf == 'V') {
eprintf ("Visual mode not supported\n");
continue;
}
r_core_rtr_cmd (core, buf);
r_cons_flush ();
}
}
static ut8 *r_rap_packet(ut8 type, ut32 len) {
ut8 *buf = malloc (len + 5);
if (buf) {
buf[0] = type;
r_write_be32 (buf + 1, len);
}
return buf;
}
static void r_rap_packet_fill(ut8 *buf, const ut8* src, int len) {
if (buf && src && len > 0) {
ut32 curlen = r_read_be32 (buf + 1);
memcpy (buf + 5, src, R_MIN (curlen, len));
}
}
static void r_core_rtr_rap_run(RCore *core, const char *input) {
r_core_cmdf (core, "o rap://%s", input);
}
static int r_core_rtr_rap_thread (RThread *th) {
if (!th) {
return false;
}
RapThread *rt = th->user;
if (!rt || !rt->core) {
return false;
}
r_core_rtr_rap_run (rt->core, rt->input);
return true;
}
R_API void r_core_rtr_cmd(RCore *core, const char *input) {
char bufw[1024], bufr[8], *cmd_output = NULL;
const char *cmd = NULL;
int i, cmd_len, fd = atoi (input);
if (*input==':' && !strchr (input + 1, ':')) {
r_core_rtr_rap_run (core, input);
return;
}
if (*input=='&') {
if (rapthread) {
eprintf ("RAP Thread is already running\n");
eprintf ("This is experimental and probably buggy. Use at your own risk\n");
} else {
RapThread rt = { core, input + 1 };
rapthread = r_th_new (r_core_rtr_rap_thread, &rt, false);
r_th_start (rapthread, true);
eprintf ("Background rap server started.\n");
}
return;
}
if (fd != 0) {
RSocket *fh = rtr_host[rtr_n].fd;
for (rtr_n = 0; fh && fh->fd != fd && rtr_n < RTR_MAX_HOSTS - 1; rtr_n++) {
/* do nothing */
}
if (!(cmd = strchr (input, ' '))) {
eprintf ("Error\n");
return;
}
} else {
cmd = input;
}
if (!rtr_host[rtr_n].fd){
eprintf ("Error: Unknown host\n");
core->num->value = 1; // fail
return;
}
if (rtr_host[rtr_n].proto != RTR_PROT_RAP) {
eprintf ("Error: Not a rap:// host\n");
return;
}
core->num->value = 0; // that's fine
while (IS_WHITESPACE (*cmd)) {
cmd++;
}
RSocket *fh = rtr_host[rtr_n].fd;
if (!strlen (cmd)) {
// just check if we can connect
r_socket_close (fh);
return;
}
/* send request */
bufw[0] = RAP_RMT_CMD;
i = strlen (cmd) + 1;
r_write_be32 (bufw + 1, i);
memcpy (bufw + 5, cmd, i);
r_socket_write (fh, bufw, 5 + i);
/* read response */
r_socket_read (fh, (ut8*)bufr, 5);
if (bufr[0] == (char)(RAP_RMT_CMD)) {
cmd_len = r_read_at_be32 (bufr, 1);
cmd = malloc (cmd_len);
if (cmd) {
char *res = r_core_cmd_str (core, cmd);
if (res) {
int res_len = strlen (res) + 1;
ut8 *pkt = r_rap_packet ((RAP_RMT_CMD | RAP_RMT_REPLY), res_len);
r_rap_packet_fill (pkt, (const ut8*)res, res_len);
r_socket_write (fh, pkt, 5 + res_len);
free (res);
free (pkt);
}
}
/* read response */
r_socket_read (fh, (ut8*)bufr, 5);
}
if (bufr[0] != (char)(RAP_RMT_CMD | RTR_RAP_REPLY)) {
eprintf ("Error: Wrong reply\n");
return;
}
cmd_len = r_read_at_be32 (bufr, 1);
if (cmd_len < 1 || cmd_len > 16384) {
eprintf ("Error: cmd_len is wrong\n");
return;
}
cmd_output = calloc (1, cmd_len + 1);
if (!cmd_output) {
eprintf ("Error: Allocating cmd output\n");
return;
}
r_socket_read (fh, (ut8*)cmd_output, cmd_len);
//ensure the termination
cmd_output[cmd_len] = 0;
r_cons_println (cmd_output);
free ((void *)cmd_output);
}
// TODO: support len for binary data?
R_API char *r_core_rtr_cmds_query (RCore *core, const char *host, const char *port, const char *cmd) {
RSocket *s = r_socket_new (0);
const int timeout = 0;
char *rbuf = NULL;
int retries = 6;
ut8 buf[1024];
for (; retries > 0; r_sys_usleep (10 * 1000)) {
if (r_socket_connect (s, host, port, R_SOCKET_PROTO_TCP, timeout))
break;
retries--;
}
if (retries > 0) {
rbuf = strdup ("");
r_socket_write (s, (void*)cmd, strlen (cmd));
//r_socket_write (s, "px\n", 3);
for (;;) {
int ret = r_socket_read (s, buf, sizeof (buf));
if (ret < 1) break;
buf[ret] = 0;
rbuf = r_str_concat (rbuf, (const char *)buf);
}
} else {
eprintf ("Cannot connect\n");
}
r_socket_free (s);
return rbuf;
}
R_API int r_core_rtr_cmds (RCore *core, const char *port) {
unsigned char buf[4097];
RSocket *ch = NULL;
RSocket *s;
int i, ret;
char *str;
if (!port || port[0] == '?') {
r_cons_printf ("Usage: .:[tcp-port] run r2 commands for clients\n");
return false;
}
s = r_socket_new (0);
if (!r_socket_listen (s, port, NULL)) {
eprintf ("Error listening on port %s\n", port);
r_socket_free (s);
return false;
}
eprintf ("Listening for commands on port %s\n", port);
listenport = port;
for (;;) {
r_cons_break ((RConsBreak)r_core_rtr_http_stop, core);
ch = r_socket_accept (s);
buf[0] = 0;
ret = r_socket_read (ch, buf, sizeof (buf) - 1);
if (ret > 0) {
buf[ret] = 0;
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n')
buf[i] = buf[i+1]? ';': '\0';
}
if (!r_config_get_i (core->config, "scr.prompt") \
&& !strcmp ((char*)buf, "q!"))
break;
str = r_core_cmd_str (core, (const char *)buf);
if (str && *str) {
r_socket_write (ch, str, strlen (str));
} else {
r_socket_write (ch, "\n", 1);
}
free (str);
}
if (r_cons_singleton()->breaked)
break;
r_socket_close (ch);
r_socket_free (ch);
r_cons_break_end ();
}
r_socket_free (s);
r_socket_free (ch);
return 0;
}