uxproxy.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * uxproxy.c: Unix implementation of platform_new_connection(),
  3. * supporting an OpenSSH-like proxy command.
  4. */
  5. #include <stdio.h>
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #define DEFINE_PLUG_METHOD_MACROS
  11. #include "tree234.h"
  12. #include "putty.h"
  13. #include "network.h"
  14. #include "proxy.h"
  15. typedef struct Socket_localproxy_tag * Local_Proxy_Socket;
  16. struct Socket_localproxy_tag {
  17. const struct socket_function_table *fn;
  18. /* the above variable absolutely *must* be the first in this structure */
  19. int to_cmd, from_cmd; /* fds */
  20. char *error;
  21. Plug plug;
  22. bufchain pending_output_data;
  23. bufchain pending_input_data;
  24. void *privptr;
  25. };
  26. static int localproxy_select_result(int fd, int event);
  27. /*
  28. * Trees to look up the pipe fds in.
  29. */
  30. static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
  31. static int localproxy_fromfd_cmp(void *av, void *bv)
  32. {
  33. Local_Proxy_Socket a = (Local_Proxy_Socket)av;
  34. Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
  35. if (a->from_cmd < b->from_cmd)
  36. return -1;
  37. if (a->from_cmd > b->from_cmd)
  38. return +1;
  39. return 0;
  40. }
  41. static int localproxy_fromfd_find(void *av, void *bv)
  42. {
  43. int a = *(int *)av;
  44. Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
  45. if (a < b->from_cmd)
  46. return -1;
  47. if (a > b->from_cmd)
  48. return +1;
  49. return 0;
  50. }
  51. static int localproxy_tofd_cmp(void *av, void *bv)
  52. {
  53. Local_Proxy_Socket a = (Local_Proxy_Socket)av;
  54. Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
  55. if (a->to_cmd < b->to_cmd)
  56. return -1;
  57. if (a->to_cmd > b->to_cmd)
  58. return +1;
  59. return 0;
  60. }
  61. static int localproxy_tofd_find(void *av, void *bv)
  62. {
  63. int a = *(int *)av;
  64. Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
  65. if (a < b->to_cmd)
  66. return -1;
  67. if (a > b->to_cmd)
  68. return +1;
  69. return 0;
  70. }
  71. /* basic proxy socket functions */
  72. static Plug sk_localproxy_plug (Socket s, Plug p)
  73. {
  74. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  75. Plug ret = ps->plug;
  76. if (p)
  77. ps->plug = p;
  78. return ret;
  79. }
  80. static void sk_localproxy_close (Socket s)
  81. {
  82. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  83. del234(localproxy_by_fromfd, ps);
  84. del234(localproxy_by_tofd, ps);
  85. close(ps->to_cmd);
  86. close(ps->from_cmd);
  87. sfree(ps);
  88. }
  89. static int localproxy_try_send(Local_Proxy_Socket ps)
  90. {
  91. int sent = 0;
  92. while (bufchain_size(&ps->pending_output_data) > 0) {
  93. void *data;
  94. int len, ret;
  95. bufchain_prefix(&ps->pending_output_data, &data, &len);
  96. ret = write(ps->to_cmd, data, len);
  97. if (ret < 0 && errno != EWOULDBLOCK) {
  98. /* We're inside the Unix frontend here, so we know
  99. * that the frontend handle is unnecessary. */
  100. logevent(NULL, strerror(errno));
  101. fatalbox("%s", strerror(errno));
  102. } else if (ret <= 0) {
  103. break;
  104. } else {
  105. bufchain_consume(&ps->pending_output_data, ret);
  106. sent += ret;
  107. }
  108. }
  109. if (bufchain_size(&ps->pending_output_data) == 0)
  110. uxsel_del(ps->to_cmd);
  111. else
  112. uxsel_set(ps->to_cmd, 2, localproxy_select_result);
  113. return sent;
  114. }
  115. static int sk_localproxy_write (Socket s, const char *data, int len)
  116. {
  117. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  118. bufchain_add(&ps->pending_output_data, data, len);
  119. localproxy_try_send(ps);
  120. return bufchain_size(&ps->pending_output_data);
  121. }
  122. static int sk_localproxy_write_oob (Socket s, const char *data, int len)
  123. {
  124. /*
  125. * oob data is treated as inband; nasty, but nothing really
  126. * better we can do
  127. */
  128. return sk_localproxy_write(s, data, len);
  129. }
  130. static void sk_localproxy_flush (Socket s)
  131. {
  132. /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
  133. /* do nothing */
  134. }
  135. static void sk_localproxy_set_private_ptr (Socket s, void *ptr)
  136. {
  137. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  138. ps->privptr = ptr;
  139. }
  140. static void * sk_localproxy_get_private_ptr (Socket s)
  141. {
  142. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  143. return ps->privptr;
  144. }
  145. static void sk_localproxy_set_frozen (Socket s, int is_frozen)
  146. {
  147. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  148. if (is_frozen)
  149. uxsel_del(ps->from_cmd);
  150. else
  151. uxsel_set(ps->from_cmd, 1, localproxy_select_result);
  152. }
  153. static const char * sk_localproxy_socket_error (Socket s)
  154. {
  155. Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
  156. return ps->error;
  157. }
  158. static int localproxy_select_result(int fd, int event)
  159. {
  160. Local_Proxy_Socket s;
  161. char buf[20480];
  162. int ret;
  163. if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
  164. !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
  165. return 1; /* boggle */
  166. if (event == 1) {
  167. assert(fd == s->from_cmd);
  168. ret = read(fd, buf, sizeof(buf));
  169. if (ret < 0) {
  170. return plug_closing(s->plug, strerror(errno), errno, 0);
  171. } else if (ret == 0) {
  172. return plug_closing(s->plug, NULL, 0, 0);
  173. } else {
  174. return plug_receive(s->plug, 1, buf, ret);
  175. }
  176. } else if (event == 2) {
  177. assert(fd == s->to_cmd);
  178. if (localproxy_try_send(s))
  179. plug_sent(s->plug, bufchain_size(&s->pending_output_data));
  180. return 1;
  181. }
  182. return 1;
  183. }
  184. Socket platform_new_connection(SockAddr addr, char *hostname,
  185. int port, int privport,
  186. int oobinline, int nodelay, int keepalive,
  187. Plug plug, const Config *cfg)
  188. {
  189. char *cmd;
  190. static const struct socket_function_table socket_fn_table = {
  191. sk_localproxy_plug,
  192. sk_localproxy_close,
  193. sk_localproxy_write,
  194. sk_localproxy_write_oob,
  195. sk_localproxy_flush,
  196. sk_localproxy_set_private_ptr,
  197. sk_localproxy_get_private_ptr,
  198. sk_localproxy_set_frozen,
  199. sk_localproxy_socket_error
  200. };
  201. Local_Proxy_Socket ret;
  202. int to_cmd_pipe[2], from_cmd_pipe[2], pid;
  203. if (cfg->proxy_type != PROXY_CMD)
  204. return NULL;
  205. cmd = format_telnet_command(addr, port, cfg);
  206. ret = snew(struct Socket_localproxy_tag);
  207. ret->fn = &socket_fn_table;
  208. ret->plug = plug;
  209. ret->error = NULL;
  210. bufchain_init(&ret->pending_input_data);
  211. bufchain_init(&ret->pending_output_data);
  212. /*
  213. * Create the pipes to the proxy command, and spawn the proxy
  214. * command process.
  215. */
  216. if (pipe(to_cmd_pipe) < 0 ||
  217. pipe(from_cmd_pipe) < 0) {
  218. ret->error = dupprintf("pipe: %s", strerror(errno));
  219. return (Socket)ret;
  220. }
  221. pid = fork();
  222. if (pid < 0) {
  223. ret->error = dupprintf("fork: %s", strerror(errno));
  224. return (Socket)ret;
  225. } else if (pid == 0) {
  226. int i;
  227. close(0);
  228. close(1);
  229. dup2(to_cmd_pipe[0], 0);
  230. dup2(from_cmd_pipe[1], 1);
  231. for (i = 3; i < 127; i++)
  232. close(i);
  233. fcntl(0, F_SETFD, 0);
  234. fcntl(1, F_SETFD, 0);
  235. execl("/bin/sh", "sh", "-c", cmd, NULL);
  236. _exit(255);
  237. }
  238. close(to_cmd_pipe[0]);
  239. close(from_cmd_pipe[1]);
  240. ret->to_cmd = to_cmd_pipe[1];
  241. ret->from_cmd = from_cmd_pipe[0];
  242. if (!localproxy_by_fromfd)
  243. localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
  244. if (!localproxy_by_tofd)
  245. localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
  246. add234(localproxy_by_fromfd, ret);
  247. add234(localproxy_by_tofd, ret);
  248. uxsel_set(ret->from_cmd, 1, localproxy_select_result);
  249. /* We are responsible for this and don't need it any more */
  250. sk_addr_free(addr);
  251. return (Socket) ret;
  252. }