net.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /******************************************************************************
  2. * This file is part of TinTin++ *
  3. * *
  4. * Copyright 2004-2019 Igor van den Hoven *
  5. * *
  6. * TinTin++ is free software; you can redistribute it and/or modify *
  7. * it under the terms of the GNU General Public License as published by *
  8. * the Free Software Foundation; either version 3 of the License, or *
  9. * (at your option) any later version. *
  10. * *
  11. * This program is distributed in the hope that it will be useful, *
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  14. * GNU General Public License for more details. *
  15. * *
  16. * *
  17. * You should have received a copy of the GNU General Public License *
  18. * along with TinTin++. If not, see https://www.gnu.org/licenses. *
  19. ******************************************************************************/
  20. /******************************************************************************
  21. * (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t *
  22. * *
  23. * coded by Peter Unold 1992 *
  24. ******************************************************************************/
  25. #include "tintin.h"
  26. #include <arpa/inet.h>
  27. #include <fcntl.h>
  28. #include <netinet/in.h>
  29. #include <netdb.h>
  30. #include <signal.h>
  31. #include <sys/types.h>
  32. /*
  33. IPv6 compatible connect code.
  34. */
  35. #ifdef HAVE_GETADDRINFO
  36. int connect_mud(struct session *ses, char *host, char *port)
  37. {
  38. int sock, error;
  39. struct addrinfo *address;
  40. static struct addrinfo hints;
  41. char ip[100];
  42. if (!is_number(port))
  43. {
  44. tintin_puts(ses, "#THE PORT SHOULD BE A NUMBER.");
  45. return -1;
  46. }
  47. // hints.ai_family = AF_UNSPEC;
  48. hints.ai_family = AF_INET;
  49. hints.ai_protocol = IPPROTO_TCP;
  50. hints.ai_socktype = SOCK_STREAM;
  51. error = getaddrinfo(host, port, &hints, &address);
  52. if (error)
  53. {
  54. hints.ai_family = AF_INET6;
  55. error = getaddrinfo(host, port, &hints, &address);
  56. if (error)
  57. {
  58. tintin_printf2(ses, "#SESSION '%s' COULD NOT CONNECT - UNKNOWN HOST.", ses->name);
  59. return -1;
  60. }
  61. }
  62. sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
  63. if (sock < 0)
  64. {
  65. syserr_printf(ses, "connect_mud: socket");
  66. freeaddrinfo(address);
  67. return -1;
  68. }
  69. // fcntl(sock, F_SETFL, O_NDELAY);
  70. ses->connect_error = connect(sock, address->ai_addr, address->ai_addrlen);
  71. if (ses->connect_error)
  72. {
  73. syserr_printf(ses, "connect_mud: connect");
  74. close(sock);
  75. freeaddrinfo(address);
  76. return -1;
  77. }
  78. if (fcntl(sock, F_SETFL, O_NDELAY|O_NONBLOCK) == -1)
  79. {
  80. syserr_printf(ses, "connect_mud: fcntl O_NDELAY|O_NONBLOCK");
  81. close(sock);
  82. freeaddrinfo(address);
  83. return -1;
  84. }
  85. error = getnameinfo(address->ai_addr, address->ai_addrlen, ip, 100, NULL, 0, NI_NUMERICHOST);
  86. if (error)
  87. {
  88. syserr_printf(ses, "connect_mud: getnameinfo:");
  89. }
  90. else
  91. {
  92. RESTRING(ses->session_ip, ip);
  93. }
  94. freeaddrinfo(address);
  95. return sock;
  96. }
  97. #else
  98. int connect_mud(struct session *ses, char *host, char *port)
  99. {
  100. int sock, d;
  101. struct sockaddr_in sockaddr;
  102. print_stdout("debug: NO ADDRESS INFO?\n");
  103. if (sscanf(host, "%d.%d.%d.%d", &d, &d, &d, &d) == 4)
  104. {
  105. sockaddr.sin_addr.s_addr = inet_addr(host);
  106. }
  107. else
  108. {
  109. struct hostent *hp;
  110. if (!(hp = gethostbyname(host)))
  111. {
  112. tintin_puts2(ses, "#ERROR - UNKNOWN HOST.");
  113. return -1;
  114. }
  115. memcpy((char *)&sockaddr.sin_addr, hp->h_addr, sizeof(sockaddr.sin_addr));
  116. }
  117. if (is_number(port))
  118. {
  119. sockaddr.sin_port = htons(atoi(port));
  120. }
  121. else
  122. {
  123. tintin_puts(ses, "#THE PORT SHOULD BE A NUMBER.");
  124. return -1;
  125. }
  126. if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  127. {
  128. syserr_printf(ses, "old_connect_mud: socket()");
  129. return -1;
  130. }
  131. sockaddr.sin_family = AF_INET;
  132. ses->connect_error = connect(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
  133. if (ses->connect_error)
  134. {
  135. syserr_printf(ses, "connect_mud: connect");
  136. close(sock);
  137. return 0;
  138. }
  139. if (fcntl(sock, F_SETFL, O_NDELAY|O_NONBLOCK) == -1)
  140. {
  141. syserr_printf(ses, "connect_mud: fcntl O_NDELAY|O_NONBLOCK");
  142. }
  143. RESTRING(ses->session_ip, inet_ntoa(sockaddr.sin_addr));
  144. return sock;
  145. }
  146. #endif
  147. void write_line_mud(struct session *ses, char *line, int size)
  148. {
  149. int result;
  150. push_call("write_line_mud(%p,%p)",line,ses);
  151. if (ses == gts)
  152. {
  153. if (HAS_BIT(gtd->flags, TINTIN_FLAG_CHILDLOCK))
  154. {
  155. tintin_printf2(ses, "#THIS SESSION IS CHILD LOCKED, PRESS CTRL-D TO EXIT.");
  156. }
  157. else
  158. {
  159. tintin_printf2(ses, "#NO SESSION ACTIVE. USE: %csession {name} {host} {port} TO START ONE.", gtd->tintin_char);
  160. }
  161. pop_call();
  162. return;
  163. }
  164. if (!HAS_BIT(ses->flags, SES_FLAG_CONNECTED))
  165. {
  166. tintin_printf2(ses, "#THIS SESSION IS NOT CONNECTED, CANNOT SEND: %s", line);
  167. pop_call();
  168. return;
  169. }
  170. if (!HAS_BIT(ses->telopts, TELOPT_FLAG_TELNET) && HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
  171. {
  172. char buf[BUFFER_SIZE];
  173. size = utf8_to_all(ses, line, buf);
  174. strcpy(line, buf);
  175. }
  176. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "SEND OUTPUT", line, ntos(size));
  177. if (!check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "CATCH SEND OUTPUT", line, ntos(size)))
  178. {
  179. if (ses->mccp3)
  180. {
  181. result = client_write_compressed(ses, line, size);
  182. }
  183. #ifdef HAVE_GNUTLS_H
  184. else if (ses->ssl)
  185. {
  186. result = gnutls_record_send(ses->ssl, line, size);
  187. while (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
  188. {
  189. result = gnutls_record_send(ses->ssl, 0, 0);
  190. }
  191. }
  192. #endif
  193. else
  194. {
  195. result = write(ses->socket, line, size);
  196. if (result == -1)
  197. {
  198. syserr_printf(ses, "write_line_mud: write");
  199. }
  200. }
  201. if (result == -1)
  202. {
  203. cleanup_session(ses);
  204. pop_call();
  205. return;
  206. }
  207. }
  208. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "SENT OUTPUT", line, ntos(size));
  209. pop_call();
  210. return;
  211. }
  212. int read_buffer_mud(struct session *ses)
  213. {
  214. unsigned char buffer[BUFFER_SIZE];
  215. int size;
  216. push_call("read_buffer_mud(%p)",ses);
  217. #ifdef HAVE_GNUTLS_H
  218. if (ses->ssl)
  219. {
  220. do
  221. {
  222. size = gnutls_record_recv(ses->ssl, buffer, BUFFER_SIZE - 1);
  223. }
  224. while (size == GNUTLS_E_INTERRUPTED || size == GNUTLS_E_AGAIN);
  225. if (size < 0)
  226. {
  227. tintin_printf2(ses, "#SSL ERROR: %s", gnutls_strerror(size));
  228. }
  229. }
  230. else
  231. #endif
  232. size = read(ses->socket, buffer, BUFFER_SIZE - 1);
  233. if (size <= 0)
  234. {
  235. pop_call();
  236. return FALSE;
  237. }
  238. ses->read_len = client_translate_telopts(ses, buffer, size);
  239. pop_call();
  240. return TRUE;
  241. }
  242. void readmud(struct session *ses)
  243. {
  244. char *line, *next_line;
  245. char linebuf[BUFFER_SIZE];
  246. struct session *cts;
  247. push_call("readmud(%p)", ses);
  248. if (gtd->mud_output_len < BUFFER_SIZE)
  249. {
  250. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 1, "RECEIVED OUTPUT", gtd->mud_output_buf);
  251. }
  252. gtd->mud_output_len = 0;
  253. /* separate into lines and print away */
  254. // cts = current tintin session, may have to make this global to avoid glitches
  255. cts = gtd->ses;
  256. if (HAS_BIT(gtd->ses->flags, SES_FLAG_SPLIT))
  257. {
  258. save_pos(gtd->ses);
  259. goto_pos(gtd->ses, gtd->ses->split->bot_row, 1);
  260. }
  261. SET_BIT(cts->flags, SES_FLAG_READMUD);
  262. for (line = gtd->mud_output_buf ; line && *line ; line = next_line)
  263. {
  264. next_line = strchr(line, '\n');
  265. if (next_line)
  266. {
  267. if (next_line - line >= BUFFER_SIZE / 3)
  268. {
  269. // This is not ideal, but being a rare case it'll suffice for now
  270. next_line = &line[BUFFER_SIZE / 3];
  271. }
  272. *next_line++ = 0;
  273. }
  274. else
  275. {
  276. if (*line == 0)
  277. {
  278. break;
  279. }
  280. if (strlen(line) > BUFFER_SIZE / 3)
  281. {
  282. next_line = &line[BUFFER_SIZE / 3];
  283. *next_line++ = 0;
  284. }
  285. if (strlen(ses->more_output) < BUFFER_SIZE / 3)
  286. {
  287. if (!HAS_BIT(ses->telopts, TELOPT_FLAG_PROMPT))
  288. {
  289. if (ses->packet_patch)
  290. {
  291. strcat(ses->more_output, line);
  292. ses->check_output = utime() + ses->packet_patch;
  293. break;
  294. }
  295. else if (HAS_BIT(ses->flags, SES_FLAG_AUTOPATCH))
  296. {
  297. if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
  298. {
  299. ses->check_output = utime() + 100000ULL;
  300. }
  301. }
  302. }
  303. }
  304. }
  305. if (ses->more_output[0])
  306. {
  307. if (ses->check_output)
  308. {
  309. strcat(ses->more_output, line);
  310. strcpy(linebuf, ses->more_output);
  311. ses->more_output[0] = 0;
  312. }
  313. else
  314. {
  315. strcpy(linebuf, line);
  316. }
  317. }
  318. else
  319. {
  320. strcpy(linebuf, line);
  321. }
  322. if (HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
  323. {
  324. char tempbuf[BUFFER_SIZE];
  325. all_to_utf8(ses, linebuf, tempbuf);
  326. process_mud_output(ses, tempbuf, next_line == NULL);
  327. }
  328. else
  329. {
  330. process_mud_output(ses, linebuf, next_line == NULL);
  331. }
  332. }
  333. DEL_BIT(cts->flags, SES_FLAG_READMUD);
  334. if (HAS_BIT(gtd->ses->flags, SES_FLAG_SPLIT))
  335. {
  336. restore_pos(gtd->ses);
  337. }
  338. pop_call();
  339. return;
  340. }
  341. void process_mud_output(struct session *ses, char *linebuf, int prompt)
  342. {
  343. char line[STRING_SIZE];
  344. push_call("process_mud_output(%p,%p,%d)",ses,linebuf,prompt);
  345. ses->check_output = 0;
  346. strip_vt102_codes(linebuf, line);
  347. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "RECEIVED LINE", linebuf, line);
  348. if (check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "CATCH RECEIVED LINE", linebuf, line))
  349. {
  350. pop_call();
  351. return;
  352. }
  353. if (prompt)
  354. {
  355. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "RECEIVED PROMPT", linebuf, line);
  356. if (check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "CATCH RECEIVED PROMPT", linebuf, line))
  357. {
  358. pop_call();
  359. return;
  360. }
  361. }
  362. if (HAS_BIT(ses->flags, SES_FLAG_COLORPATCH))
  363. {
  364. sprintf(line, "%s%s%s", ses->color_patch, linebuf, "\e[0m");
  365. get_color_codes(ses->color_patch, linebuf, ses->color_patch, GET_ALL);
  366. linebuf = line;
  367. }
  368. do_one_line(linebuf, ses); /* changes linebuf */
  369. /*
  370. Take care of gags, vt102 support still goes
  371. */
  372. if (HAS_BIT(ses->flags, SES_FLAG_GAG))
  373. {
  374. DEL_BIT(ses->flags, SES_FLAG_GAG);
  375. strip_non_vt102_codes(linebuf, line);
  376. print_stdout("%s", line);
  377. strip_vt102_codes(linebuf, line);
  378. show_info(ses, LIST_GAG, "#INFO GAG {%s}", line);
  379. pop_call();
  380. return;
  381. }
  382. if (!check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "CATCH BUFFERED LINE", linebuf, line))
  383. {
  384. add_line_buffer(ses, linebuf, prompt);
  385. }
  386. check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "BUFFERED LINE", linebuf, line);
  387. if (ses == gtd->ses)
  388. {
  389. char *output = str_dup(linebuf);
  390. print_line(ses, &output, prompt);
  391. str_free(output);
  392. if (prompt)
  393. {
  394. if (!IS_SPLIT(ses))
  395. {
  396. gtd->input_off = 1 + strip_vt102_strlen(ses, linebuf);
  397. }
  398. }
  399. }
  400. pop_call();
  401. return;
  402. }