1 /* radare2 - LGPL - Copyright 2015-2020 pancake */
2 
3 #include "r_lib.h"
4 #include "r_core.h"
5 #include "r_lang.h"
6 #if __WINDOWS__
7 #include <windows.h>
8 #endif
9 #ifdef _MSC_VER
10 #include <process.h>
11 #endif
12 
13 #if __WINDOWS__
myCreateChildProcess(const char * szCmdline)14 static HANDLE myCreateChildProcess(const char * szCmdline) {
15 	PROCESS_INFORMATION piProcInfo = {0};
16 	STARTUPINFO siStartInfo = {0};
17 	BOOL bSuccess = FALSE;
18 	siStartInfo.cb = sizeof (STARTUPINFO);
19 	siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
20 	siStartInfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
21 	siStartInfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
22 	siStartInfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
23 
24 	LPTSTR cmdline_ = r_sys_conv_utf8_to_win (szCmdline);
25 	bSuccess = CreateProcess (NULL, cmdline_, NULL, NULL,
26 		TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
27 	free (cmdline_);
28 	return bSuccess ? piProcInfo.hProcess : NULL;
29 }
30 
31 static HANDLE hPipeInOut = NULL;
32 static HANDLE hproc = NULL;
33 #define PIPE_BUF_SIZE 8192
34 
lang_pipe_run_win(RLang * lang)35 static void lang_pipe_run_win(RLang *lang) {
36 	CHAR buf[PIPE_BUF_SIZE];
37 	BOOL bSuccess = TRUE;
38 	int i, res = 0;
39 	DWORD dwRead = 0, dwWritten = 0, dwEvent;
40 	HANDLE hRead = CreateEvent (NULL, TRUE, FALSE, NULL);
41 	if (!hRead) {
42 		r_sys_perror ("lang_pipe_run_win/CreateEvent hRead");
43 		return;
44 	}
45 	HANDLE hWritten = CreateEvent (NULL, TRUE, FALSE, NULL);
46 	if (!hWritten) {
47 		r_sys_perror ("lang_pipe_run_win/CreateEvent hWritten");
48 		CloseHandle (hRead);
49 		return;
50 	}
51 	r_cons_break_push (NULL, NULL);
52 	do {
53 		if (r_cons_is_breaked ()) {
54 			TerminateProcess (hproc, 0);
55 			break;
56 		}
57 		OVERLAPPED oRead = { 0 };
58 		oRead.hEvent = hRead;
59 		memset (buf, 0, PIPE_BUF_SIZE);
60 		ReadFile (hPipeInOut, buf, PIPE_BUF_SIZE, NULL, &oRead);
61 		HANDLE hReadEvents[] = { hRead, hproc };
62 		dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hReadEvents), hReadEvents,
63 		                                  FALSE, INFINITE);
64 		if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
65 			break;
66 		} else if (dwEvent == WAIT_FAILED) {
67 			r_sys_perror ("lang_pipe_run_win/WaitForMultipleObjects read");
68 			break;
69 		}
70 		bSuccess = GetOverlappedResult (hPipeInOut, &oRead, &dwRead, TRUE);
71 		if (!bSuccess) {
72 			break;
73 		}
74 		if (bSuccess && dwRead > 0) {
75 			buf[sizeof (buf) - 1] = 0;
76 			OVERLAPPED oWrite = { 0 };
77 			oWrite.hEvent = hWritten;
78 			char *res = lang->cmd_str ((RCore*)lang->user, buf);
79 			if (res) {
80 				int res_len = strlen (res) + 1;
81 				for (i = 0; i < res_len; i++) {
82 					memset (buf, 0, PIPE_BUF_SIZE);
83 					dwWritten = 0;
84 					int writelen = res_len - i;
85 					WriteFile (hPipeInOut, res + i,
86 					           writelen > PIPE_BUF_SIZE ? PIPE_BUF_SIZE : writelen,
87 					           NULL, &oWrite);
88 					HANDLE hWriteEvents[] = { hWritten, hproc };
89 					dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hWriteEvents), hWriteEvents,
90 					                                  FALSE, INFINITE);
91 					if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
92 						break;
93 					} else if (dwEvent == WAIT_FAILED) {
94 						r_sys_perror ("lang_pipe_run_win/WaitForMultipleObjects write");
95 					}
96 					BOOL rc = GetOverlappedResult (hPipeInOut, &oWrite, &dwWritten, TRUE);
97 					if (!rc) {
98 						r_sys_perror ("lang_pipe_run_win/WriteFile res");
99 					}
100 					if (dwWritten > 0) {
101 						i += dwWritten - 1;
102 					} else {
103 						// send null termination // chop
104 						r_sys_perror ("lang_pipe_run_win/dwWritten");
105 						//WriteFile (hPipeInOut, "", 1, &dwWritten, NULL);
106 						//break;
107 					}
108 				}
109 				free (res);
110 			} else {
111 				WriteFile (hPipeInOut, "", 1, NULL, &oWrite);
112 				if (!GetOverlappedResult (hPipeInOut, &oWrite, &dwWritten, TRUE)) {
113 					r_sys_perror ("lang_pipe_run_win/WriteFile nul");
114 				}
115 			}
116 		}
117 	} while (true);
118 	r_cons_break_pop ();
119 	CloseHandle (hWritten);
120 	CloseHandle (hRead);
121 }
122 #else
env(const char * s,int f)123 static void env(const char *s, int f) {
124 	char *a = r_str_newf ("%d", f);
125 	r_sys_setenv (s, a);
126 //	eprintf ("%s %s\n", s, a);
127 	free (a);
128 }
129 #endif
130 
lang_pipe_run(RLang * lang,const char * code,int len)131 static bool lang_pipe_run(RLang *lang, const char *code, int len) {
132 #if __UNIX__
133 	int safe_in = dup (0);
134 	int child, ret;
135 	int input[2];
136 	int output[2];
137 
138 	if (pipe (input) != 0) {
139 		eprintf ("r_lang_pipe: pipe failed on input\n");
140 		if (safe_in != -1) {
141 			close (safe_in);
142 		}
143 		return false;
144 	}
145 	if (pipe (output) != 0) {
146 		eprintf ("r_lang_pipe: pipe failed on output\n");
147 		if (safe_in != -1) {
148 			close (safe_in);
149 		}
150 		return false;
151 	}
152 
153 	env ("R2PIPE_IN", input[0]);
154 	env ("R2PIPE_OUT", output[1]);
155 
156 	child = r_sys_fork ();
157 	if (child == -1) {
158 		/* error */
159 		perror ("pipe run");
160 	} else if (!child) {
161 		/* children */
162 		r_sandbox_system (code, 1);
163 		(void) write (input[1], "", 1);
164 		close (input[0]);
165 		close (input[1]);
166 		close (output[0]);
167 		close (output[1]);
168 		fflush (stdout);
169 		fflush (stderr);
170 		r_sys_exit (0, true);
171 		return false;
172 	} else {
173 		/* parent */
174 		char *res, buf[8192]; // TODO: use the heap?
175 		/* Close pipe ends not required in the parent */
176 		close (output[1]);
177 		close (input[0]);
178 		r_cons_break_push (NULL, NULL);
179 		for (;;) {
180 			if (r_cons_is_breaked ()) {
181 				break;
182 			}
183 			memset (buf, 0, sizeof (buf));
184 			void *bed = r_cons_sleep_begin ();
185 			ret = read (output[0], buf, sizeof (buf) - 1);
186 			r_cons_sleep_end (bed);
187 			if (ret < 1) {
188 				break;
189 			}
190 			if (!buf[0]) {
191 				continue;
192 			}
193 			buf[sizeof (buf) - 1] = 0;
194 			res = lang->cmd_str ((RCore*)lang->user, buf);
195 			if (res) {
196 				// r_cons_print (res);
197 				(void) write (input[1], res, strlen (res) + 1);
198 				free (res);
199 			} else {
200 				eprintf ("r_lang_pipe: NULL reply for (%s)\n", buf);
201 				(void) write (input[1], "", 1); // NULL byte
202 			}
203 		}
204 		r_cons_break_pop ();
205 		/* workaround to avoid stdin closed */
206 		if (safe_in != -1) {
207 			close (safe_in);
208 		}
209 		safe_in = -1;
210 		char *term_in = ttyname (0);
211 		if (term_in) {
212 			safe_in = open (term_in, O_RDONLY);
213 			if (safe_in != -1) {
214 				dup2 (safe_in, 0);
215 			} else {
216 				eprintf ("Cannot open ttyname(0) %s\n", term_in);
217 			}
218 		}
219 	}
220 
221 	close (input[0]);
222 	close (input[1]);
223 	close (output[0]);
224 	close (output[1]);
225 	if (safe_in != -1) {
226 		close (safe_in);
227 	}
228 	waitpid (child, NULL, WNOHANG);
229 	return true;
230 #else
231 #if __WINDOWS__
232 	char *r2pipe_var = r_str_newf ("R2PIPE_IN%x", _getpid ());
233 	char *r2pipe_paz = r_str_newf ("\\\\.\\pipe\\%s", r2pipe_var);
234 	LPTSTR r2pipe_paz_ = r_sys_conv_utf8_to_win (r2pipe_paz);
235 
236 	SetEnvironmentVariable (TEXT ("R2PIPE_PATH"), r2pipe_paz_);
237 	hPipeInOut = CreateNamedPipe (r2pipe_paz_,
238 			PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
239 			PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
240 			PIPE_BUF_SIZE,
241 			PIPE_BUF_SIZE,
242 			0, NULL);
243 	if (hPipeInOut == INVALID_HANDLE_VALUE) {
244 		r_sys_perror ("lang_pipe_run/CreateNamedPipe");
245 		goto beach;
246 	}
247 	HANDLE hConnected = CreateEvent (NULL, TRUE, FALSE, NULL);
248 	if (!hConnected) {
249 		r_sys_perror ("lang_pipe_run/CreateEvent hConnected");
250 		goto pipe_cleanup;
251 	}
252 	OVERLAPPED oConnect = { 0 };
253 	oConnect.hEvent = hConnected;
254 	hproc = myCreateChildProcess (code);
255 	BOOL connected = FALSE;
256 	if (hproc) {
257 		connected = ConnectNamedPipe (hPipeInOut, &oConnect);
258 		DWORD err = GetLastError ();
259 		if (!connected && err != ERROR_PIPE_CONNECTED) {
260 			if (err == ERROR_IO_PENDING) {
261 				HANDLE hEvents[] = { hConnected, hproc };
262 				DWORD dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hEvents), hEvents,
263 				                                        FALSE, INFINITE);
264 				if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
265 					goto cleanup;
266 				} else if (dwEvent == WAIT_FAILED) {
267 					r_sys_perror ("lang_pipe_run/WaitForMultipleObjects connect");
268 					goto cleanup;
269 				}
270 				DWORD dummy;
271 				connected = GetOverlappedResult (hPipeInOut, &oConnect, &dummy, TRUE);
272 				err = GetLastError ();
273 			}
274 			if (!connected && err != ERROR_PIPE_CONNECTED) {
275 				r_sys_perror ("lang_pipe_run/ConnectNamedPipe");
276 				goto cleanup;
277 			}
278 		}
279 		lang_pipe_run_win (lang);
280 	}
281 cleanup:
282 	CloseHandle (hConnected);
283 pipe_cleanup:
284 	DeleteFile (r2pipe_paz_);
285 	CloseHandle (hPipeInOut);
286 beach:
287 	free (r2pipe_var);
288 	free (r2pipe_paz);
289 	free (r2pipe_paz_);
290 	return hproc != NULL;
291 #endif
292 #endif
293 }
294 
lang_pipe_file(RLang * lang,const char * file)295 static bool lang_pipe_file(RLang *lang, const char *file) {
296 	return lang_pipe_run (lang, file, -1);
297 }
298 
299 static RLangPlugin r_lang_plugin_pipe = {
300 	.name = "pipe",
301 	.ext = "pipe",
302 	.license = "LGPL",
303 	.desc = "Use #!pipe node script.js",
304 	.run = lang_pipe_run,
305 	.run_file = (void*)lang_pipe_file,
306 };
307