/* radare - LGPL - Copyright 2015-2016 - pancake */ #include #include #include #define R2P_MAGIC 0x329193 #define R2P_PID(x) (((R2Pipe*)x->data)->pid) #define R2P_INPUT(x) (((R2Pipe*)x->data)->input[0]) #define R2P_OUTPUT(x) (((R2Pipe*)x->data)->output[1]) #if !__WINDOWS__ static void env(const char *s, int f) { char *a = r_str_newf ("%d", f); r_sys_setenv (s, a); free (a); } #endif R_API int r2p_close(R2Pipe *r2p) { if (!r2p) return 0; #if __WINDOWS__ && !defined(__CYGWIN__) if (r2p->pipe) { CloseHandle (r2p->pipe); r2p->pipe = NULL; } #else if (r2p->input[0] != -1) { close (r2p->input[0]); close (r2p->input[1]); r2p->input[0] = -1; r2p->input[1] = -1; } if (r2p->output[0] != -1) { close (r2p->output[0]); close (r2p->output[1]); r2p->output[0] = -1; r2p->output[1] = -1; } if (r2p->child != -1) { kill (r2p->child, SIGTERM); waitpid (r2p->child, NULL, 0); r2p->child = -1; } #endif free (r2p); return 0; } #if __WINDOWS__ && !defined(__CYGWIN__) static int w32_createChildProcess(const char * szCmdline) { PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; ZeroMemory (&piProcInfo, sizeof (PROCESS_INFORMATION)); ZeroMemory (&siStartInfo, sizeof (STARTUPINFO)); siStartInfo.cb = sizeof (STARTUPINFO); bSuccess = CreateProcess (NULL, (LPSTR)szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); if (!bSuccess) return false; CloseHandle (piProcInfo.hProcess); CloseHandle (piProcInfo.hThread); return true; } static int w32_createPipe(R2Pipe *r2p, const char *cmd) { CHAR buf[1024]; r2p->pipe = CreateNamedPipe ("\\\\.\\pipe\\R2PIPE_IN", PIPE_ACCESS_DUPLEX,PIPE_TYPE_MESSAGE | \ PIPE_READMODE_MESSAGE | \ PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof (buf), sizeof (buf), 0, NULL); if (w32_createChildProcess (cmd)) { if (ConnectNamedPipe (r2p->pipe, NULL)) return true; } return false; } #endif static R2Pipe* r2p_open_spawn(R2Pipe* r2p, const char *cmd) { #if __UNIX__ || defined(__CYGWIN__) char *out = r_sys_getenv ("R2PIPE_IN"); char *in = r_sys_getenv ("R2PIPE_OUT"); int done = false; if (in && out) { int i_in = atoi (in); int i_out = atoi (out); if (i_in >= 0 && i_out >= 0) { r2p->input[0] = r2p->input[1] = i_in; r2p->output[0] = r2p->output[1] = i_out; done = true; } } if (!done) { eprintf ("Cannot find R2PIPE_IN or R2PIPE_OUT environment\n"); R_FREE (r2p); } free (in); free (out); return r2p; #else eprintf ("r2p_open(NULL) not supported on windows\n"); return NULL; #endif } R_API R2Pipe *r2p_open(const char *cmd) { R2Pipe *r2p = R_NEW0 (R2Pipe); if (!r2p) { return NULL; } r2p->magic = R2P_MAGIC; if (!cmd) { r2p->child = -1; return r2p_open_spawn (r2p, cmd); } #if __WINDOWS__ && !defined(__CYGWIN__) w32_createPipe (r2p, cmd); r2p->child = (int)(r2p->pipe); #else pipe (r2p->input); pipe (r2p->output); #if LIBC_HAVE_FORK r2p->child = fork (); #else r2p->child = -1; #endif if (r2p->child == -1) { r2p_close (r2p); return NULL; } env ("R2PIPE_IN", r2p->input[0]); env ("R2PIPE_OUT", r2p->output[1]); if (r2p->child) { eprintf ("[+] r2pipe child is %d\n", r2p->child); char ch; if (read (r2p->output[0], &ch, 1) != 1) { eprintf ("Failed to read 1 byte\n"); r2p_close (r2p); return NULL; } if (ch != 0x00) { eprintf ("[+] r2pipe-io link failed. Expected two null bytes.\n"); r2p_close (r2p); return NULL; } else { if (read (r2p->output[0], &ch, 1) != 1) { eprintf ("Failed to read 1 byte\n"); r2p_close (r2p); return NULL; } if (ch == 0x00) { eprintf ("[+] r2pipe-io link stablished\n"); } } } else { int rc = 0; if (cmd && *cmd) { close (0); close (1); dup2 (r2p->input[0], 0); dup2 (r2p->output[1], 1); rc = r_sandbox_system (cmd, 0); } r2p_close (r2p); exit (rc); return NULL; } #endif return r2p; } R_API char *r2p_cmd(R2Pipe *r2p, const char *str) { if (!r2p_write (r2p, str)) { perror ("r2p_write"); return NULL; } return r2p_read (r2p); } R_API char *r2p_cmdf(R2Pipe *r2p, const char *fmt, ...) { int ret, ret2; char *p, string[1024]; va_list ap, ap2; va_start (ap, fmt); va_start (ap2, fmt); ret = vsnprintf (string, sizeof (string) - 1, fmt, ap); if (ret < 1 || ret >= sizeof (string)) { p = malloc (ret + 2); if (!p) { va_end (ap2); va_end (ap); return NULL; } ret2 = vsnprintf (p, ret+1, fmt, ap2); if (ret2 < 1 || ret2 > ret + 1) { free (p); va_end (ap2); va_end (ap); return NULL; } fmt = r2p_cmd (r2p, p); free (p); } else { fmt = r2p_cmd (r2p, string); } va_end (ap2); va_end (ap); return (char*)fmt; } R_API int r2p_write(R2Pipe *r2p, const char *str) { char *cmd; int ret, len; if (!r2p || !str) { return -1; } len = strlen (str) + 2; /* include \n\x00 */ cmd = malloc (len + 2); if (!cmd) { return 0; } memcpy (cmd, str, len - 1); strcpy (cmd + len - 2, "\n"); #if __WINDOWS__ && !defined(__CYGWIN__) DWORD dwWritten = -1; WriteFile (r2p->pipe, cmd, len, &dwWritten, NULL); ret = (dwWritten == len); #else ret = (write (r2p->input[1], cmd, len) == len); #endif free (cmd); return ret; } /* TODO: add timeout here ? */ R_API char *r2p_read(R2Pipe *r2p) { int bufsz = 0; char *newbuf, *buf = NULL; if (!r2p) return NULL; bufsz = 4096; buf = calloc (1, bufsz); if (!buf) return NULL; #if __WINDOWS__ && !defined(__CYGWIN__) BOOL bSuccess = FALSE; DWORD dwRead = 0; // TODO: handle > 4096 buffers here bSuccess = ReadFile (r2p->pipe, buf, bufsz, &dwRead, NULL); if (!bSuccess || !buf[0]) { return NULL; } if (dwRead > 0) { buf[dwRead] = 0; } buf[bufsz - 1] = 0; #else int i, rv; for (i = 0; i < bufsz; i++) { rv = read (r2p->output[0], buf + i, 1); if (i + 2 >= bufsz) { bufsz += 4096; newbuf = realloc (buf, bufsz); if (!newbuf) { free (buf); buf = NULL; break; } buf = newbuf; } if (rv != 1 || !buf[i]) { break; } } if (buf) { int zpos = (i < bufsz)? i: i - 1; buf[zpos] = 0; } #endif return buf; } R_API void r2p_free (R2Pipe *r2p) { r2p->magic = 0; r2p_close (r2p); }