/* * fd-socket.c: implementation of Socket that just talks to two * existing input and output file descriptors. */ #include #include #include #include #include #include "tree234.h" #include "putty.h" #include "network.h" typedef struct FdSocket { int outfd, infd, inerrfd; /* >= 0 if socket is open */ DeferredSocketOpener *opener; /* non-NULL if not opened yet */ bufchain pending_output_data; bufchain pending_input_data; ProxyStderrBuf psb; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int pending_error; SockAddr *addr; int port; Plug *plug; Socket sock; } FdSocket; static void fdsocket_select_result_input(int fd, int event); static void fdsocket_select_result_output(int fd, int event); static void fdsocket_select_result_input_error(int fd, int event); /* * Trees to look up the fds in. */ static tree234 *fdsocket_by_outfd; static tree234 *fdsocket_by_infd; static tree234 *fdsocket_by_inerrfd; static int fdsocket_infd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->infd < b->infd) return -1; if (a->infd > b->infd) return +1; return 0; } static int fdsocket_infd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->infd) return -1; if (a > b->infd) return +1; return 0; } static int fdsocket_inerrfd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->inerrfd < b->inerrfd) return -1; if (a->inerrfd > b->inerrfd) return +1; return 0; } static int fdsocket_inerrfd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->inerrfd) return -1; if (a > b->inerrfd) return +1; return 0; } static int fdsocket_outfd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->outfd < b->outfd) return -1; if (a->outfd > b->outfd) return +1; return 0; } static int fdsocket_outfd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->outfd) return -1; if (a > b->outfd) return +1; return 0; } static Plug *fdsocket_plug(Socket *s, Plug *p) { FdSocket *fds = container_of(s, FdSocket, sock); Plug *ret = fds->plug; if (p) fds->plug = p; return ret; } static void fdsocket_close(Socket *s) { FdSocket *fds = container_of(s, FdSocket, sock); if (fds->opener) deferred_socket_opener_free(fds->opener); if (fds->outfd >= 0) { del234(fdsocket_by_outfd, fds); uxsel_del(fds->outfd); close(fds->outfd); } if (fds->infd >= 0) { del234(fdsocket_by_infd, fds); uxsel_del(fds->infd); close(fds->infd); } if (fds->inerrfd >= 0) { del234(fdsocket_by_inerrfd, fds); uxsel_del(fds->inerrfd); close(fds->inerrfd); } bufchain_clear(&fds->pending_input_data); bufchain_clear(&fds->pending_output_data); if (fds->addr) sk_addr_free(fds->addr); delete_callbacks_for_context(fds); sfree(fds); } static void fdsocket_error_callback(void *vs) { FdSocket *fds = (FdSocket *)vs; /* * Just in case other socket work has caused this socket to vanish * or become somehow non-erroneous before this callback arrived... */ if (!fds->pending_error) return; /* * An error has occurred on this socket. Pass it to the plug. */ plug_closing_errno(fds->plug, fds->pending_error); } static int fdsocket_try_send(FdSocket *fds) { int sent = 0; if (fds->opener) return sent; while (bufchain_size(&fds->pending_output_data) > 0) { ssize_t ret; ptrlen data = bufchain_prefix(&fds->pending_output_data); ret = write(fds->outfd, data.ptr, data.len); noise_ultralight(NOISE_SOURCE_IOID, ret); if (ret < 0 && errno != EWOULDBLOCK) { if (!fds->pending_error) { fds->pending_error = errno; queue_toplevel_callback(fdsocket_error_callback, fds); } return 0; } else if (ret <= 0) { break; } else { bufchain_consume(&fds->pending_output_data, ret); sent += ret; } } if (fds->outgoingeof == EOF_PENDING) { del234(fdsocket_by_outfd, fds); close(fds->outfd); uxsel_del(fds->outfd); fds->outfd = -1; fds->outgoingeof = EOF_SENT; } if (bufchain_size(&fds->pending_output_data) == 0) uxsel_del(fds->outfd); else uxsel_set(fds->outfd, SELECT_W, fdsocket_select_result_output); return sent; } static size_t fdsocket_write(Socket *s, const void *data, size_t len) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->outgoingeof == EOF_NO); bufchain_add(&fds->pending_output_data, data, len); fdsocket_try_send(fds); return bufchain_size(&fds->pending_output_data); } static size_t fdsocket_write_oob(Socket *s, const void *data, size_t len) { /* * oob data is treated as inband; nasty, but nothing really * better we can do */ return fdsocket_write(s, data, len); } static void fdsocket_write_eof(Socket *s) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->outgoingeof == EOF_NO); fds->outgoingeof = EOF_PENDING; fdsocket_try_send(fds); } static void fdsocket_set_frozen(Socket *s, bool is_frozen) { FdSocket *fds = container_of(s, FdSocket, sock); if (fds->infd < 0) return; if (is_frozen) uxsel_del(fds->infd); else uxsel_set(fds->infd, SELECT_R, fdsocket_select_result_input); } static const char *fdsocket_socket_error(Socket *s) { return NULL; } static void fdsocket_select_result_input(int fd, int event) { FdSocket *fds; char buf[20480]; int retd; if (!(fds = find234(fdsocket_by_infd, &fd, fdsocket_infd_find))) return; retd = read(fds->infd, buf, sizeof(buf)); if (retd > 0) { plug_receive(fds->plug, 0, buf, retd); } else { del234(fdsocket_by_infd, fds); uxsel_del(fds->infd); close(fds->infd); fds->infd = -1; if (retd < 0) { plug_closing_errno(fds->plug, errno); } else { plug_closing_normal(fds->plug); } } } static void fdsocket_select_result_output(int fd, int event) { FdSocket *fds; if (!(fds = find234(fdsocket_by_outfd, &fd, fdsocket_outfd_find))) return; if (fdsocket_try_send(fds)) plug_sent(fds->plug, bufchain_size(&fds->pending_output_data)); } static void fdsocket_select_result_input_error(int fd, int event) { FdSocket *fds; char buf[20480]; int retd; if (!(fds = find234(fdsocket_by_inerrfd, &fd, fdsocket_inerrfd_find))) return; retd = read(fd, buf, sizeof(buf)); if (retd > 0) { log_proxy_stderr(fds->plug, &fds->sock, &fds->psb, buf, retd); } else { del234(fdsocket_by_inerrfd, fds); uxsel_del(fds->inerrfd); close(fds->inerrfd); fds->inerrfd = -1; } } static const SocketVtable FdSocket_sockvt = { .plug = fdsocket_plug, .close = fdsocket_close, .write = fdsocket_write, .write_oob = fdsocket_write_oob, .write_eof = fdsocket_write_eof, .set_frozen = fdsocket_set_frozen, .socket_error = fdsocket_socket_error, .endpoint_info = nullsock_endpoint_info, }; static void fdsocket_connect_success_callback(void *ctx) { FdSocket *fds = (FdSocket *)ctx; plug_log(fds->plug, &fds->sock, PLUGLOG_CONNECT_SUCCESS, fds->addr, fds->port, NULL, 0); } void setup_fd_socket(Socket *s, int infd, int outfd, int inerrfd) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->sock.vt == &FdSocket_sockvt); if (fds->opener) { deferred_socket_opener_free(fds->opener); fds->opener = NULL; } fds->infd = infd; fds->outfd = outfd; fds->inerrfd = inerrfd; if (fds->outfd >= 0) { if (!fdsocket_by_outfd) fdsocket_by_outfd = newtree234(fdsocket_outfd_cmp); add234(fdsocket_by_outfd, fds); } if (fds->infd >= 0) { if (!fdsocket_by_infd) fdsocket_by_infd = newtree234(fdsocket_infd_cmp); add234(fdsocket_by_infd, fds); uxsel_set(fds->infd, SELECT_R, fdsocket_select_result_input); } if (fds->inerrfd >= 0) { assert(fds->inerrfd != fds->infd); if (!fdsocket_by_inerrfd) fdsocket_by_inerrfd = newtree234(fdsocket_inerrfd_cmp); add234(fdsocket_by_inerrfd, fds); uxsel_set(fds->inerrfd, SELECT_R, fdsocket_select_result_input_error); } queue_toplevel_callback(fdsocket_connect_success_callback, fds); } void fd_socket_set_psb_prefix(Socket *s, const char *prefix) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->sock.vt == &FdSocket_sockvt); psb_set_prefix(&fds->psb, prefix); } static FdSocket *make_fd_socket_internal(SockAddr *addr, int port, Plug *plug) { FdSocket *fds; fds = snew(FdSocket); fds->sock.vt = &FdSocket_sockvt; fds->addr = addr; fds->port = port; fds->plug = plug; fds->outgoingeof = EOF_NO; fds->pending_error = 0; fds->opener = NULL; fds->infd = fds->outfd = fds->inerrfd = -1; bufchain_init(&fds->pending_input_data); bufchain_init(&fds->pending_output_data); psb_init(&fds->psb); return fds; } Socket *make_fd_socket(int infd, int outfd, int inerrfd, SockAddr *addr, int port, Plug *plug) { FdSocket *fds = make_fd_socket_internal(addr, port, plug); setup_fd_socket(&fds->sock, infd, outfd, inerrfd); return &fds->sock; } Socket *make_deferred_fd_socket(DeferredSocketOpener *opener, SockAddr *addr, int port, Plug *plug) { FdSocket *fds = make_fd_socket_internal(addr, port, plug); fds->opener = opener; return &fds->sock; }