psocks.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /*
  2. * Platform-independent parts of a standalone SOCKS server program
  3. * based on the PuTTY SOCKS code.
  4. */
  5. #include <string.h>
  6. #include <errno.h>
  7. #include "putty.h"
  8. #include "storage.h"
  9. #include "misc.h"
  10. #include "ssh.h"
  11. #include "ssh/channel.h"
  12. #include "psocks.h"
  13. /*
  14. * Possible later TODOs:
  15. *
  16. * - verbosity setting for log messages
  17. *
  18. * - could import proxy.c and use name_lookup rather than
  19. * sk_namelookup, to allow forwarding via some other proxy type
  20. */
  21. #define BUFLIMIT 16384
  22. #define LOGBITS(X) \
  23. X(CONNSTATUS) \
  24. X(DIALOGUE) \
  25. /* end of list */
  26. #define BITINDEX_ENUM(x) LOG_##x##_bitindex,
  27. enum { LOGBITS(BITINDEX_ENUM) };
  28. #define BITFLAG_ENUM(x) LOG_##x = 1 << LOG_##x##_bitindex,
  29. enum { LOGBITS(BITFLAG_ENUM) };
  30. typedef struct psocks_connection psocks_connection;
  31. typedef enum RecordDestination {
  32. REC_NONE, REC_FILE, REC_PIPE
  33. } RecordDestination;
  34. struct psocks_state {
  35. const PsocksPlatform *platform;
  36. int listen_port;
  37. bool acceptall;
  38. PortFwdManager *portfwdmgr;
  39. uint64_t next_conn_index;
  40. FILE *logging_fp;
  41. unsigned log_flags;
  42. RecordDestination rec_dest;
  43. char *rec_cmd;
  44. strbuf *subcmd;
  45. ConnectionLayer cl;
  46. };
  47. struct psocks_connection {
  48. psocks_state *ps;
  49. Channel *chan;
  50. char *host, *realhost;
  51. int port;
  52. SockAddr *addr;
  53. Socket *socket;
  54. bool connecting, eof_pfmgr_to_socket, eof_socket_to_pfmgr;
  55. uint64_t index;
  56. PsocksDataSink *rec_sink;
  57. Plug plug;
  58. SshChannel sc;
  59. };
  60. static SshChannel *psocks_lportfwd_open(
  61. ConnectionLayer *cl, const char *hostname, int port,
  62. const char *description, const SocketEndpointInfo *pi, Channel *chan);
  63. static const ConnectionLayerVtable psocks_clvt = {
  64. .lportfwd_open = psocks_lportfwd_open,
  65. /* everything else is NULL */
  66. };
  67. static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *,
  68. size_t);
  69. static void psocks_sc_write_eof(SshChannel *sc);
  70. static void psocks_sc_initiate_close(SshChannel *sc, const char *err);
  71. static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize);
  72. static const SshChannelVtable psocks_scvt = {
  73. .write = psocks_sc_write,
  74. .write_eof = psocks_sc_write_eof,
  75. .initiate_close = psocks_sc_initiate_close,
  76. .unthrottle = psocks_sc_unthrottle,
  77. /* all the rest are NULL */
  78. };
  79. static void psocks_plug_log(Plug *p, Socket *s, PlugLogType type,
  80. SockAddr *addr, int port,
  81. const char *error_msg, int error_code);
  82. static void psocks_plug_closing(Plug *p, PlugCloseType, const char *error_msg);
  83. static void psocks_plug_receive(Plug *p, int urgent,
  84. const char *data, size_t len);
  85. static void psocks_plug_sent(Plug *p, size_t bufsize);
  86. static const PlugVtable psocks_plugvt = {
  87. .log = psocks_plug_log,
  88. .closing = psocks_plug_closing,
  89. .receive = psocks_plug_receive,
  90. .sent = psocks_plug_sent,
  91. };
  92. static void psocks_conn_log(psocks_connection *conn, const char *fmt, ...)
  93. {
  94. if (!conn->ps->logging_fp)
  95. return;
  96. va_list ap;
  97. va_start(ap, fmt);
  98. char *msg = dupvprintf(fmt, ap);
  99. va_end(ap);
  100. fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s\n", conn->index, msg);
  101. sfree(msg);
  102. fflush(conn->ps->logging_fp);
  103. }
  104. static void psocks_conn_log_data(psocks_connection *conn, PsocksDirection dir,
  105. const void *vdata, size_t len)
  106. {
  107. if ((conn->ps->log_flags & LOG_DIALOGUE) && conn->ps->logging_fp) {
  108. const char *data = vdata;
  109. while (len > 0) {
  110. const char *nl = memchr(data, '\n', len);
  111. size_t thislen = nl ? (nl+1) - data : len;
  112. const char *thisdata = data;
  113. data += thislen;
  114. len -= thislen;
  115. static const char *const direction_names[2] = {
  116. [UP] = "send", [DN] = "recv" };
  117. fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s \"", conn->index,
  118. direction_names[dir]);
  119. write_c_string_literal(conn->ps->logging_fp,
  120. make_ptrlen(thisdata, thislen));
  121. fprintf(conn->ps->logging_fp, "\"\n");
  122. }
  123. fflush(conn->ps->logging_fp);
  124. }
  125. if (conn->rec_sink)
  126. put_data(conn->rec_sink->s[dir], vdata, len);
  127. }
  128. static void psocks_connection_establish(void *vctx);
  129. static SshChannel *psocks_lportfwd_open(
  130. ConnectionLayer *cl, const char *hostname, int port,
  131. const char *description, const SocketEndpointInfo *pi, Channel *chan)
  132. {
  133. psocks_state *ps = container_of(cl, psocks_state, cl);
  134. psocks_connection *conn = snew(psocks_connection);
  135. memset(conn, 0, sizeof(*conn));
  136. conn->ps = ps;
  137. conn->sc.vt = &psocks_scvt;
  138. conn->plug.vt = &psocks_plugvt;
  139. conn->chan = chan;
  140. conn->host = dupstr(hostname);
  141. conn->port = port;
  142. conn->index = ps->next_conn_index++;
  143. if (conn->ps->log_flags & LOG_CONNSTATUS)
  144. psocks_conn_log(conn, "request from %s for %s port %d",
  145. pi->log_text, hostname, port);
  146. switch (conn->ps->rec_dest) {
  147. case REC_FILE:
  148. {
  149. char *fnames[2];
  150. FILE *fp[2];
  151. bool ok = true;
  152. static const char *const direction_names[2] = {
  153. [UP] = "sockout", [DN] = "sockin" };
  154. for (size_t i = 0; i < 2; i++) {
  155. fnames[i] = dupprintf("%s.%"PRIu64, direction_names[i],
  156. conn->index);
  157. fp[i] = fopen(fnames[i], "wb");
  158. if (!fp[i]) {
  159. psocks_conn_log(conn, "cannot log this connection: "
  160. "creating file '%s': %s",
  161. fnames[i], strerror(errno));
  162. ok = false;
  163. }
  164. }
  165. if (ok) {
  166. if (conn->ps->log_flags & LOG_CONNSTATUS)
  167. psocks_conn_log(conn, "logging to '%s' / '%s'",
  168. fnames[0], fnames[1]);
  169. conn->rec_sink = pds_stdio(fp);
  170. } else {
  171. for (size_t i = 0; i < 2; i++) {
  172. if (fp[i]) {
  173. remove(fnames[i]);
  174. fclose(fp[i]);
  175. }
  176. }
  177. }
  178. for (size_t i = 0; i < 2; i++)
  179. sfree(fnames[i]);
  180. }
  181. break;
  182. case REC_PIPE:
  183. {
  184. static const char *const direction_args[2] = {
  185. [UP] = "out", [DN] = "in" };
  186. char *index_arg = dupprintf("%"PRIu64, conn->index);
  187. char *err;
  188. conn->rec_sink = conn->ps->platform->open_pipes(
  189. conn->ps->rec_cmd, direction_args, index_arg, &err);
  190. if (!conn->rec_sink) {
  191. psocks_conn_log(conn, "cannot log this connection: "
  192. "creating pipes: %s", err);
  193. sfree(err);
  194. }
  195. sfree(index_arg);
  196. }
  197. break;
  198. default:
  199. break;
  200. }
  201. queue_toplevel_callback(psocks_connection_establish, conn);
  202. return &conn->sc;
  203. }
  204. static void psocks_conn_free(psocks_connection *conn)
  205. {
  206. if (conn->ps->log_flags & LOG_CONNSTATUS)
  207. psocks_conn_log(conn, "closed");
  208. sfree(conn->host);
  209. sfree(conn->realhost);
  210. if (conn->socket)
  211. sk_close(conn->socket);
  212. if (conn->chan)
  213. chan_free(conn->chan);
  214. if (conn->rec_sink)
  215. pds_free(conn->rec_sink);
  216. delete_callbacks_for_context(conn);
  217. sfree(conn);
  218. }
  219. static void psocks_connection_establish(void *vctx)
  220. {
  221. psocks_connection *conn = (psocks_connection *)vctx;
  222. /*
  223. * Look up destination host name.
  224. */
  225. conn->addr = sk_namelookup(conn->host, &conn->realhost, ADDRTYPE_UNSPEC);
  226. const char *err = sk_addr_error(conn->addr);
  227. if (err) {
  228. char *msg = dupprintf("name lookup failed: %s", err);
  229. chan_open_failed(conn->chan, msg);
  230. sfree(msg);
  231. psocks_conn_free(conn);
  232. return;
  233. }
  234. /*
  235. * Make the connection.
  236. */
  237. conn->connecting = true;
  238. conn->socket = sk_new(conn->addr, conn->port, false, false, false, false,
  239. &conn->plug);
  240. }
  241. static size_t psocks_sc_write(SshChannel *sc, bool is_stderr,
  242. const void *data, size_t len)
  243. {
  244. psocks_connection *conn = container_of(sc, psocks_connection, sc);
  245. if (!conn->socket) return 0;
  246. psocks_conn_log_data(conn, UP, data, len);
  247. return sk_write(conn->socket, data, len);
  248. }
  249. static void psocks_check_close(void *vctx)
  250. {
  251. psocks_connection *conn = (psocks_connection *)vctx;
  252. if (chan_want_close(conn->chan, conn->eof_pfmgr_to_socket,
  253. conn->eof_socket_to_pfmgr))
  254. psocks_conn_free(conn);
  255. }
  256. static void psocks_sc_write_eof(SshChannel *sc)
  257. {
  258. psocks_connection *conn = container_of(sc, psocks_connection, sc);
  259. if (!conn->socket) return;
  260. sk_write_eof(conn->socket);
  261. conn->eof_pfmgr_to_socket = true;
  262. if (conn->ps->log_flags & LOG_DIALOGUE)
  263. psocks_conn_log(conn, "send eof");
  264. queue_toplevel_callback(psocks_check_close, conn);
  265. }
  266. static void psocks_sc_initiate_close(SshChannel *sc, const char *err)
  267. {
  268. psocks_connection *conn = container_of(sc, psocks_connection, sc);
  269. sk_close(conn->socket);
  270. conn->socket = NULL;
  271. }
  272. static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize)
  273. {
  274. psocks_connection *conn = container_of(sc, psocks_connection, sc);
  275. if (bufsize < BUFLIMIT)
  276. sk_set_frozen(conn->socket, false);
  277. }
  278. static void psocks_plug_log(Plug *plug, Socket *s, PlugLogType type,
  279. SockAddr *addr, int port,
  280. const char *error_msg, int error_code)
  281. {
  282. psocks_connection *conn = container_of(plug, psocks_connection, plug);
  283. char addrbuf[256];
  284. if (!(conn->ps->log_flags & LOG_CONNSTATUS))
  285. return;
  286. switch (type) {
  287. case PLUGLOG_CONNECT_TRYING:
  288. sk_getaddr(addr, addrbuf, sizeof(addrbuf));
  289. if (sk_addr_needs_port(addr))
  290. psocks_conn_log(conn, "trying to connect to %s port %d",
  291. addrbuf, port);
  292. else
  293. psocks_conn_log(conn, "trying to connect to %s", addrbuf);
  294. break;
  295. case PLUGLOG_CONNECT_FAILED:
  296. psocks_conn_log(conn, "connection attempt failed: %s", error_msg);
  297. break;
  298. case PLUGLOG_CONNECT_SUCCESS:
  299. psocks_conn_log(conn, "connection established", error_msg);
  300. if (conn->connecting) {
  301. chan_open_confirmation(conn->chan);
  302. conn->connecting = false;
  303. }
  304. break;
  305. case PLUGLOG_PROXY_MSG:
  306. psocks_conn_log(conn, "connection setup: %s", error_msg);
  307. break;
  308. };
  309. }
  310. static void psocks_plug_closing(Plug *plug, PlugCloseType type,
  311. const char *error_msg)
  312. {
  313. psocks_connection *conn = container_of(plug, psocks_connection, plug);
  314. if (conn->connecting) {
  315. if (conn->ps->log_flags & LOG_CONNSTATUS)
  316. psocks_conn_log(conn, "unable to connect: %s", error_msg);
  317. chan_open_failed(conn->chan, error_msg);
  318. conn->eof_socket_to_pfmgr = true;
  319. conn->eof_pfmgr_to_socket = true;
  320. conn->connecting = false;
  321. } else {
  322. if (conn->ps->log_flags & LOG_DIALOGUE)
  323. psocks_conn_log(conn, "recv eof");
  324. chan_send_eof(conn->chan);
  325. conn->eof_socket_to_pfmgr = true;
  326. }
  327. queue_toplevel_callback(psocks_check_close, conn);
  328. }
  329. static void psocks_plug_receive(Plug *plug, int urgent,
  330. const char *data, size_t len)
  331. {
  332. psocks_connection *conn = container_of(plug, psocks_connection, plug);
  333. size_t bufsize = chan_send(conn->chan, false, data, len);
  334. sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
  335. psocks_conn_log_data(conn, DN, data, len);
  336. }
  337. static void psocks_plug_sent(Plug *plug, size_t bufsize)
  338. {
  339. psocks_connection *conn = container_of(plug, psocks_connection, plug);
  340. sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
  341. }
  342. psocks_state *psocks_new(const PsocksPlatform *platform)
  343. {
  344. psocks_state *ps = snew(psocks_state);
  345. memset(ps, 0, sizeof(*ps));
  346. ps->listen_port = 1080;
  347. ps->acceptall = false;
  348. ps->cl.vt = &psocks_clvt;
  349. ps->portfwdmgr = portfwdmgr_new(&ps->cl);
  350. ps->logging_fp = stderr; /* could make this configurable later */
  351. ps->log_flags = LOG_CONNSTATUS;
  352. ps->rec_dest = REC_NONE;
  353. ps->platform = platform;
  354. ps->subcmd = strbuf_new();
  355. return ps;
  356. }
  357. void psocks_free(psocks_state *ps)
  358. {
  359. portfwdmgr_free(ps->portfwdmgr);
  360. strbuf_free(ps->subcmd);
  361. sfree(ps->rec_cmd);
  362. sfree(ps);
  363. }
  364. void psocks_cmdline(psocks_state *ps, int argc, char **argv)
  365. {
  366. bool doing_opts = true;
  367. bool accumulating_exec_args = false;
  368. size_t args_seen = 0;
  369. while (--argc > 0) {
  370. const char *p = *++argv;
  371. if (doing_opts && p[0] == '-' && p[1]) {
  372. if (!strcmp(p, "--")) {
  373. doing_opts = false;
  374. } else if (!strcmp(p, "-g")) {
  375. ps->acceptall = true;
  376. } else if (!strcmp(p, "-d")) {
  377. ps->log_flags |= LOG_DIALOGUE;
  378. } else if (!strcmp(p, "-f")) {
  379. ps->rec_dest = REC_FILE;
  380. } else if (!strcmp(p, "-p")) {
  381. if (!ps->platform->open_pipes) {
  382. fprintf(stderr, "psocks: '-p' is not supported on this "
  383. "platform\n");
  384. exit(1);
  385. }
  386. if (--argc > 0) {
  387. ps->rec_cmd = dupstr(*++argv);
  388. } else {
  389. fprintf(stderr, "psocks: expected an argument to '-p'\n");
  390. exit(1);
  391. }
  392. ps->rec_dest = REC_PIPE;
  393. } else if (!strcmp(p, "--exec")) {
  394. if (!ps->platform->start_subcommand) {
  395. fprintf(stderr, "psocks: running a subcommand is not "
  396. "supported on this platform\n");
  397. exit(1);
  398. }
  399. accumulating_exec_args = true;
  400. /* Now consume all further argv words for the
  401. * subcommand, even if they look like options */
  402. doing_opts = false;
  403. } else if (!strcmp(p, "--help")) {
  404. printf("usage: psocks [ -d ] [ -f");
  405. if (ps->platform->open_pipes)
  406. printf(" | -p pipe-cmd");
  407. printf(" ] [ -g ] port-number");
  408. printf("\n");
  409. printf("where: -d log all connection contents to"
  410. " standard output\n");
  411. printf(" -f record each half-connection to "
  412. "a file sockin.N/sockout.N\n");
  413. if (ps->platform->open_pipes)
  414. printf(" -p pipe-cmd pipe each half-connection"
  415. " to 'pipe-cmd [in|out] N'\n");
  416. printf(" -g accept connections from anywhere,"
  417. " not just localhost\n");
  418. if (ps->platform->start_subcommand)
  419. printf(" --exec subcmd [args...] run command, and "
  420. "terminate when it exits\n");
  421. printf(" port-number listen on this port"
  422. " (default 1080)\n");
  423. printf("also: psocks --help display this help text\n");
  424. exit(0);
  425. } else {
  426. fprintf(stderr, "psocks: unrecognised option '%s'\n", p);
  427. exit(1);
  428. }
  429. } else {
  430. if (accumulating_exec_args) {
  431. put_asciz(ps->subcmd, p);
  432. } else switch (args_seen++) {
  433. case 0:
  434. ps->listen_port = atoi(p);
  435. break;
  436. default:
  437. fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p);
  438. exit(1);
  439. break;
  440. }
  441. }
  442. }
  443. }
  444. void psocks_start(psocks_state *ps)
  445. {
  446. Conf *conf = conf_new();
  447. conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall);
  448. char *key = dupprintf("AL%d", ps->listen_port);
  449. conf_set_str_str(conf, CONF_portfwd, key, "D");
  450. sfree(key);
  451. portfwdmgr_config(ps->portfwdmgr, conf);
  452. if (ps->subcmd->len)
  453. ps->platform->start_subcommand(ps->subcmd);
  454. conf_free(conf);
  455. }
  456. /*
  457. * Some stubs that are needed to link against PuTTY modules.
  458. */
  459. int check_stored_host_key(const char *hostname, int port,
  460. const char *keytype, const char *key)
  461. {
  462. unreachable("host keys not handled in this tool");
  463. }
  464. void store_host_key(Seat *seat, const char *hostname, int port,
  465. const char *keytype, const char *key)
  466. {
  467. unreachable("host keys not handled in this tool");
  468. }
  469. /*
  470. * stdio-targeted PsocksDataSink.
  471. */
  472. typedef struct PsocksDataSinkStdio {
  473. stdio_sink sink[2];
  474. PsocksDataSink pds;
  475. } PsocksDataSinkStdio;
  476. static void stdio_free(PsocksDataSink *pds)
  477. {
  478. PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds);
  479. for (size_t i = 0; i < 2; i++)
  480. fclose(pdss->sink[i].fp);
  481. sfree(pdss);
  482. }
  483. PsocksDataSink *pds_stdio(FILE *fp[2])
  484. {
  485. PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio);
  486. for (size_t i = 0; i < 2; i++) {
  487. setvbuf(fp[i], NULL, _IONBF, 0);
  488. stdio_sink_init(&pdss->sink[i], fp[i]);
  489. pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]);
  490. }
  491. pdss->pds.free = stdio_free;
  492. return &pdss->pds;
  493. }