psocks.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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. bool got_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. return ps;
  355. }
  356. void psocks_free(psocks_state *ps)
  357. {
  358. portfwdmgr_free(ps->portfwdmgr);
  359. sfree(ps->rec_cmd);
  360. sfree(ps);
  361. }
  362. void psocks_cmdline(psocks_state *ps, CmdlineArgList *arglist)
  363. {
  364. bool doing_opts = true;
  365. size_t arglistpos = 0;
  366. size_t args_seen = 0;
  367. while (arglist->args[arglistpos]) {
  368. CmdlineArg *arg = arglist->args[arglistpos++];
  369. const char *p = cmdline_arg_to_str(arg);
  370. if (doing_opts && p[0] == '-' && p[1]) {
  371. if (!strcmp(p, "--")) {
  372. doing_opts = false;
  373. } else if (!strcmp(p, "-g")) {
  374. ps->acceptall = true;
  375. } else if (!strcmp(p, "-d")) {
  376. ps->log_flags |= LOG_DIALOGUE;
  377. } else if (!strcmp(p, "-f")) {
  378. ps->rec_dest = REC_FILE;
  379. } else if (!strcmp(p, "-p")) {
  380. if (!ps->platform->open_pipes) {
  381. fprintf(stderr, "psocks: '-p' is not supported on this "
  382. "platform\n");
  383. exit(1);
  384. }
  385. if (arglist->args[arglistpos] > 0) {
  386. ps->rec_cmd = dupstr(
  387. cmdline_arg_to_str(arglist->args[arglistpos++]));
  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. if (!arglist->args[arglistpos]) {
  400. fprintf(stderr, "psocks: --exec requires a command\n");
  401. exit(1);
  402. }
  403. /* Now consume all further argv words for the
  404. * subcommand, even if they look like options */
  405. ps->platform->found_subcommand(arglist->args[arglistpos]);
  406. ps->got_subcmd = true;
  407. break;
  408. } else if (!strcmp(p, "--help")) {
  409. printf("usage: psocks [ -d ] [ -f");
  410. if (ps->platform->open_pipes)
  411. printf(" | -p pipe-cmd");
  412. printf(" ] [ -g ] port-number");
  413. printf("\n");
  414. printf("where: -d log all connection contents to"
  415. " standard output\n");
  416. printf(" -f record each half-connection to "
  417. "a file sockin.N/sockout.N\n");
  418. if (ps->platform->open_pipes)
  419. printf(" -p pipe-cmd pipe each half-connection"
  420. " to 'pipe-cmd [in|out] N'\n");
  421. printf(" -g accept connections from anywhere,"
  422. " not just localhost\n");
  423. if (ps->platform->start_subcommand)
  424. printf(" --exec subcmd [args...] run command, and "
  425. "terminate when it exits\n");
  426. printf(" port-number listen on this port"
  427. " (default 1080)\n");
  428. printf("also: psocks --help display this help text\n");
  429. exit(0);
  430. } else {
  431. fprintf(stderr, "psocks: unrecognised option '%s'\n", p);
  432. exit(1);
  433. }
  434. } else {
  435. switch (args_seen++) {
  436. case 0:
  437. ps->listen_port = atoi(p);
  438. break;
  439. default:
  440. fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p);
  441. exit(1);
  442. break;
  443. }
  444. }
  445. }
  446. }
  447. void psocks_start(psocks_state *ps)
  448. {
  449. Conf *conf = conf_new();
  450. conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall);
  451. char *key = dupprintf("AL%d", ps->listen_port);
  452. conf_set_str_str(conf, CONF_portfwd, key, "D");
  453. sfree(key);
  454. portfwdmgr_config(ps->portfwdmgr, conf);
  455. if (ps->got_subcmd)
  456. ps->platform->start_subcommand();
  457. conf_free(conf);
  458. }
  459. /*
  460. * Some stubs that are needed to link against PuTTY modules.
  461. */
  462. int check_stored_host_key(const char *hostname, int port,
  463. const char *keytype, const char *key)
  464. {
  465. unreachable("host keys not handled in this tool");
  466. }
  467. void store_host_key(Seat *seat, const char *hostname, int port,
  468. const char *keytype, const char *key)
  469. {
  470. unreachable("host keys not handled in this tool");
  471. }
  472. /*
  473. * stdio-targeted PsocksDataSink.
  474. */
  475. typedef struct PsocksDataSinkStdio {
  476. stdio_sink sink[2];
  477. PsocksDataSink pds;
  478. } PsocksDataSinkStdio;
  479. static void stdio_free(PsocksDataSink *pds)
  480. {
  481. PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds);
  482. for (size_t i = 0; i < 2; i++)
  483. fclose(pdss->sink[i].fp);
  484. sfree(pdss);
  485. }
  486. PsocksDataSink *pds_stdio(FILE *fp[2])
  487. {
  488. PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio);
  489. for (size_t i = 0; i < 2; i++) {
  490. setvbuf(fp[i], NULL, _IONBF, 0);
  491. stdio_sink_init(&pdss->sink[i], fp[i]);
  492. pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]);
  493. }
  494. pdss->pds.free = stdio_free;
  495. return &pdss->pds;
  496. }