hharry.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. #include <fcntl.h>
  2. #include <pty.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/select.h>
  6. #include <sys/wait.h>
  7. #include <termios.h>
  8. #include <unistd.h>
  9. #include <string>
  10. using namespace std;
  11. // #include <signal.h> // handle Ctrl-C/SIGINT
  12. #include <strings.h> // strcasecmp
  13. #include <time.h>
  14. #include <ctype.h>
  15. #include <stdlib.h> // random()
  16. /* Log level guideline:
  17. * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected.
  18. * Process can't continue and must be terminated.
  19. * Example: division by zero, unexpected modifications from other thread.
  20. * - ZF_LOG_ERROR - happened something possible, but highly unexpected. The
  21. * process is able to recover and continue execution.
  22. * Example: out of memory (could also be FATAL if not handled properly).
  23. * - ZF_LOG_WARN - happened something that *usually* should not happen and
  24. * significantly changes application behavior for some period of time.
  25. * Example: configuration file not found, auth error.
  26. * - ZF_LOG_INFO - happened significant life cycle event or major state
  27. * transition.
  28. * Example: app started, user logged in.
  29. * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the
  30. * execution path. Usually disabled in release builds.
  31. * - ZF_LOG_VERBOSE - all other events. Usually disabled in release builds.
  32. *
  33. * *Ideally*, log file of debugged, well tested, production ready application
  34. * should be empty or very small. Choosing a right log level is as important as
  35. * providing short and self descriptive log message.
  36. */
  37. /*
  38. #define ZF_LOG_VERBOSE 1
  39. #define ZF_LOG_DEBUG 2
  40. #define ZF_LOG_INFO 3
  41. #define ZF_LOG_WARN 4
  42. #define ZF_LOG_ERROR 5
  43. #define ZF_LOG_FATAL 6
  44. */
  45. // When debugging low-level, use this:
  46. // #define ZF_LOG_LEVEL ZF_LOG_VERBOSE
  47. // Except this doesn't work. It needs to be anywere the
  48. // zf_log.h is included.
  49. // LOGGING with file output
  50. #include "zf_log.h"
  51. FILE *g_log_file;
  52. static void file_output_callback(const zf_log_message *msg, void *arg) {
  53. (void)arg;
  54. *msg->p = '\n';
  55. fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
  56. fflush(g_log_file);
  57. }
  58. static void file_output_close(void) { fclose(g_log_file); }
  59. static int file_output_open(const char *const log_path) {
  60. g_log_file = fopen(log_path, "a");
  61. if (!g_log_file) {
  62. ZF_LOGW("Failed to open log file %s", log_path);
  63. return 0;
  64. }
  65. atexit(file_output_close);
  66. zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
  67. return 1;
  68. }
  69. void log_flush(void) { fflush(g_log_file); }
  70. // END LOGGING
  71. #include "terminal.h"
  72. #include "utils.h"
  73. struct console_details console;
  74. #include "wordplay.h"
  75. /*
  76. What is the name of the actual, real Mystic executable
  77. that we'll be executing and mangling?
  78. */
  79. #define TARGET "./mySTIC"
  80. // Size of our input and output buffers.
  81. #define BSIZE 1024
  82. /*
  83. These are harry "timeout" events.
  84. These happen when we've been sitting around awhile.
  85. */
  86. /*
  87. The code to get the username and fullname is useless on telnet
  88. connections.
  89. */
  90. const char *username = NULL;
  91. const char *fullname = NULL;
  92. /*
  93. This only works for those few idiots that use the
  94. horribly broken SSH crap that Mystic uses.
  95. */
  96. int locate_user(const char *alias) {
  97. FILE *user;
  98. char buffer[0x600];
  99. char temp[100];
  100. user = fopen("data/users.dat", "rb");
  101. if (user == NULL)
  102. return 0;
  103. // Carry on!
  104. while (fread(buffer, 0x600, 1, user) == 1) {
  105. pcopy(buffer + 0x6d, temp);
  106. if (strcasecmp(temp, username) == 0) {
  107. pcopy(buffer + 0x8c, temp);
  108. fullname = strdup(temp);
  109. break;
  110. }
  111. /*
  112. printf("Alias: %s\n", temp);
  113. pcopy(buffer + 0x8c, temp );
  114. printf("Full Name: %s\n", temp );
  115. */
  116. }
  117. fclose(user);
  118. return 1;
  119. }
  120. /*
  121. This is done. :D My buffering system works with stack'em.
  122. TO FIX: Stop using c strings, must use char * buffer + int length.
  123. MAY CONTAIN NULL VALUES.
  124. Rework some things here.
  125. Here's the "plan":
  126. if buffer is EMPTY:
  127. time_idle = 1;
  128. // setup for "random timeout value mess"
  129. // we're in luck! The last parameter is time interval/timeout. :D
  130. timeout.tv_sec = 10; // randrange(10-25)
  131. timeout.tv_usec = 0;
  132. NOT EMPTY:
  133. // we're in luck! The last parameter is time interval/timeout. :D
  134. timeout.tv_sec = 0;
  135. timeout.tv_usec = 10; // Wild Guess Here? Maybe higher, maybe lower?
  136. time_idle = 0;
  137. ON READ:
  138. read/append to current buffer.
  139. We can't use nulls -- what if they are using ZModem, there's nulls in the
  140. file! Look for trailing / the very last "\r\n".
  141. (I could mangle/chunk it line by line. But I'm not sure I'd need to do
  142. that.)
  143. Optional "mangle" buffer up to that very point -- and send up to that point.
  144. Option #2: Maybe we send everything if program has been running for under
  145. 20 seconds. This would allow the ANSI detect to not get screwed up by this
  146. new idea.
  147. ON TIMEOUT:
  148. if time_idle:
  149. Activate funny harry timeout events.
  150. else:
  151. Ok, we *STILL* haven't received any more characters into the buffer --
  152. even after waiting. (Maybe we haven't waited long enough?)
  153. send the pending information in the buffer and clear it out.
  154. Maybe this is a prompt, and there won't be a \r\n.
  155. This allows for cleaner process of "lines" of buffer. We shouldn't break
  156. in the midDLE OF A WORD. Downside is that we sit on buffer contents a little
  157. while / some amount of time -- which will add some lag to prompts showing up.
  158. (LAG? Are you kidding?)
  159. ZModem:
  160. start: "rz^M**"...
  161. 05-12 18:12:15.916 >> rz^M**^XB00000000000000^M<8A>^Q
  162. 05-12 18:12:15.928 << **\x18B0100000023be50\r\n\x11
  163. 05-12 18:12:15.928 >> *^XC^D
  164. 05-12 18:12:15.939 << **\x18B0900000000a87c\r\n\x11
  165. 05-12 18:12:15.940 >> *^XC
  166. # Start of PK zipfile.
  167. 05-12 18:12:15.941 >> PK^C^D^T
  168. end:
  169. 05-12 18:26:38.700 << **\x18B0100000023be50\r\n\x11
  170. 05-12 18:26:38.700 >> **^XB0823a77600344c^M<8A>
  171. 05-12 18:26:38.711 << **\x18B0800000000022d\r\n
  172. 05-12 18:26:38.712 >> OO^MESC[0m
  173. */
  174. // TODO: Get everything above this -- into another file.
  175. int main(int argc, char *argv[]) {
  176. int master;
  177. pid_t pid;
  178. int node = -1;
  179. if (!file_output_open("horrible_harry.log"))
  180. return 2;
  181. init_harry();
  182. srandom(time(NULL));
  183. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  184. // -Ubugz -PUWISHPASSWORD
  185. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  186. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  187. // -Ubugz -PUWISH
  188. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  189. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
  190. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
  191. // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown
  192. // -Ubugz -PDUMBWAYTODOTHIS
  193. // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown
  194. // -Ubugz -PIDONTUSEPASCAL
  195. // SSH: -ML1 -ST2
  196. // Telnet: -ML0 -ST0
  197. // Locate username (if given) in the command line
  198. // -U<username>
  199. for (int x = 0; x < argc; x++) {
  200. if (strncmp("-U", argv[x], 2) == 0) {
  201. username = argv[x] + 2;
  202. ZF_LOGI("Username: [%s]", username);
  203. };
  204. if (strncmp("-SL", argv[x], 3) == 0) {
  205. node = atoi(argv[x] + 3) + 1;
  206. ZF_LOGI("Node: %d", node);
  207. }
  208. }
  209. if (username != NULL) {
  210. locate_user(username);
  211. ZF_LOGD("Username: [%s] A.K.A. [%s]", username, fullname);
  212. }
  213. pid = forkpty(&master, NULL, NULL, NULL);
  214. // impossible to fork
  215. if (pid < 0) {
  216. return 1;
  217. }
  218. // child
  219. else if (pid == 0) {
  220. char *args[20]; // max 20 args
  221. int x;
  222. char new_exec[] = TARGET;
  223. // build new args list
  224. args[0] = new_exec;
  225. for (x = 1; x < argc; x++) {
  226. args[x] = argv[x];
  227. };
  228. // null term the list
  229. args[x] = NULL;
  230. // run Mystic, run!
  231. execvp(TARGET, args);
  232. }
  233. // parent
  234. else {
  235. struct termios tios, orig1;
  236. struct timeval timeout;
  237. time_t last_event = 0; // time(NULL);
  238. ZF_LOGD("starting");
  239. tcgetattr(master, &tios);
  240. tios.c_lflag &= ~(ECHO | ECHONL | ICANON);
  241. /*
  242. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  243. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  244. tios.c_oflag &= ~(OPOST);
  245. */
  246. tcsetattr(master, TCSAFLUSH, &tios);
  247. tcgetattr(1, &orig1);
  248. tios = orig1;
  249. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  250. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  251. tios.c_oflag &= ~(OPOST);
  252. // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
  253. tcsetattr(1, TCSAFLUSH, &tios);
  254. /*
  255. This doesn't need to be static -- because it is part of
  256. main. Once main ends, we're done.
  257. */
  258. string buffer;
  259. buffer.reserve(BSIZE * 2);
  260. string play;
  261. play.reserve(4096);
  262. // int size = 0; // use buffer.size() instead
  263. for (;;) {
  264. int time_idle;
  265. // define estruturas para o select, que serve para verificar qual
  266. // se tornou "pronto pra uso"
  267. fd_set read_fd;
  268. fd_set write_fd;
  269. fd_set except_fd;
  270. // inicializa as estruturas
  271. FD_ZERO(&read_fd);
  272. FD_ZERO(&write_fd);
  273. FD_ZERO(&except_fd);
  274. // atribui o descritor master, obtido pelo forkpty, ao read_fd
  275. FD_SET(master, &read_fd);
  276. // atribui o stdin ao read_fd
  277. FD_SET(STDIN_FILENO, &read_fd);
  278. // o descritor tem que ser unico para o programa, a documentacao
  279. // recomenda um calculo entre os descritores sendo usados + 1
  280. /*
  281. TODO: Figure out how this would work.
  282. I'm thinking something like timeouts 30-50 seconds?
  283. And as we get closer, 15-25 seconds.
  284. */
  285. if (buffer.size() == 0) {
  286. // buffer is empty
  287. timeout.tv_sec = randrange(10, 20);
  288. timeout.tv_usec = 0;
  289. time_idle = 1;
  290. } else {
  291. // buffer is not empty
  292. timeout.tv_sec = 0;
  293. timeout.tv_usec = 1;
  294. time_idle = 0;
  295. }
  296. if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) {
  297. ZF_LOGI("TIMEOUT");
  298. // This means timeout!
  299. if (time_idle) {
  300. harry_idle_event(STDOUT_FILENO);
  301. } else {
  302. ZF_LOGI_MEM(buffer.data(), buffer.size(), "TIMEOUT buffer size=%lu",
  303. buffer.size());
  304. play.assign(buffer);
  305. mangle(STDOUT_FILENO, play);
  306. /*
  307. ZF_LOGI("console_receive");
  308. console_receive(&console, buffer);
  309. ZF_LOGI("write buffer");
  310. write(STDOUT_FILENO, buffer.data(), buffer.size());
  311. */
  312. ZF_LOGI("buffer clear");
  313. buffer.clear();
  314. // size = 0;
  315. // buffer is empty now
  316. }
  317. }
  318. // read_fd esta atribuido com read_fd?
  319. if (FD_ISSET(master, &read_fd)) {
  320. // leia o que bc esta mandando
  321. // ZF_LOGD("read (%d) %d bytes", size, BSIZE - size);
  322. char read_buffer[BSIZE + 1];
  323. int total;
  324. // We may adjust this later on (adjusting read length).
  325. if ((total = read(master, read_buffer, BSIZE)) != -1) {
  326. // Ok, we've read more into the buffer.
  327. ZF_LOGV("Read %d bytes", total);
  328. buffer.append(read_buffer, total);
  329. // ZF_LOGV_MEM(buffer + size, total, "Read %d bytes:", total);
  330. // size += total;
  331. // ZF_LOGV_MEM(buffer, size, "Buffer now:");
  332. int pos = buffer.rfind("\r\n");
  333. // rstrnstr(buffer, size, "\r\n");
  334. // >= 0) {
  335. if (pos != string::npos) {
  336. // found something!
  337. pos += 2;
  338. // play = buffer.substr() wipes out play's reserve.
  339. // play = buffer.substr(0, pos);
  340. play.assign(buffer, 0, pos);
  341. ZF_LOGI("play %lu size, %lu cap", play.size(), play.capacity());
  342. // play.copy(buffer.data(), pos);
  343. //) = buffer.substr(0, pos);
  344. buffer.erase(0, pos);
  345. mangle(STDOUT_FILENO, play);
  346. // ZF_LOGD_MEM(buffer, pos, "mangle buffer %d bytes:", pos);
  347. // mangle(STDOUT_FILENO, buffer, pos);
  348. // memmove(buffer, buffer + pos, size - pos);
  349. // size -= pos;
  350. // } else {
  351. // ZF_LOGV("position of /r/n not found.");
  352. }
  353. // Ok, we failed to find CR+NL. What's the buffer size at?
  354. if (buffer.size() > BSIZE) {
  355. // Ok, there's something going on, and it doesn't look good
  356. // unsure if I want to feed this into the console
  357. // my guess at this point would be zmodem xfer
  358. ZF_LOGI("Buffer %lu bytes, write only...", buffer.size());
  359. // console_receive(&console, buffer);
  360. write(STDOUT_FILENO, buffer.data(), buffer.size());
  361. buffer.clear();
  362. }
  363. } else
  364. break;
  365. }
  366. // read_fd esta atribuido com a entrada padrao?
  367. if (FD_ISSET(STDIN_FILENO, &read_fd)) {
  368. // leia a entrada padrao
  369. char input[BSIZE];
  370. int r = read(STDIN_FILENO, &input, BSIZE);
  371. input[r] = 0;
  372. // e escreva no bc
  373. if (r > 50) {
  374. ZF_LOGI("<< %d bytes", r);
  375. } else {
  376. ZF_LOGI("<< %s", repr(input));
  377. };
  378. write(master, &input, r);
  379. // This is INPUT from the USER
  380. // ZF_LOGI_MEM( input, strlen(input), "<< ");
  381. }
  382. }
  383. // Restore terminal
  384. tcsetattr(1, TCSAFLUSH, &orig1);
  385. ZF_LOGD("exit");
  386. }
  387. return 0;
  388. }