Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'unix/fd-socket.c')
-rw-r--r--unix/fd-socket.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/unix/fd-socket.c b/unix/fd-socket.c
new file mode 100644
index 00000000..9758a17b
--- /dev/null
+++ b/unix/fd-socket.c
@@ -0,0 +1,417 @@
+/*
+ * fd-socket.c: implementation of Socket that just talks to two
+ * existing input and output file descriptors.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#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->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,
+ .peer_info = NULL,
+};
+
+static void fdsocket_connect_success_callback(void *ctx)
+{
+ FdSocket *fds = (FdSocket *)ctx;
+ plug_log(fds->plug, 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;
+}