+static void
+sock_close (int fd)
+{
+ close (fd);
+ DEBUGP (("Closed fd %d\n", fd));
+}
+#undef read
+#undef write
+#undef close
+\f
+/* Reading and writing from the network. We build around the socket
+ (file descriptor) API, but support "extended" operations for things
+ that are not mere file descriptors under the hood, such as SSL
+ sockets.
+
+ That way the user code can call fd_read(fd, ...) and we'll run read
+ or SSL_read or whatever is necessary. */
+
+static struct hash_table *transport_map;
+static int transport_map_modified_tick;
+
+struct transport_info {
+ fd_reader_t reader;
+ fd_writer_t writer;
+ fd_poller_t poller;
+ fd_peeker_t peeker;
+ fd_closer_t closer;
+ void *ctx;
+};
+
+/* Register the transport layer operations that will be used when
+ reading, writing, and polling FD.
+
+ This should be used for transport layers like SSL that piggyback on
+ sockets. FD should otherwise be a real socket, on which you can
+ call getpeername, etc. */
+
+void
+fd_register_transport (int fd, fd_reader_t reader, fd_writer_t writer,
+ fd_poller_t poller, fd_peeker_t peeker,
+ fd_closer_t closer, void *ctx)
+{
+ struct transport_info *info;
+
+ /* The file descriptor must be non-negative to be registered.
+ Negative values are ignored by fd_close(), and -1 cannot be used as
+ hash key. */
+ assert (fd >= 0);
+
+ info = xnew (struct transport_info);
+ info->reader = reader;
+ info->writer = writer;
+ info->poller = poller;
+ info->peeker = peeker;
+ info->closer = closer;
+ info->ctx = ctx;
+ if (!transport_map)
+ transport_map = hash_table_new (0, NULL, NULL);
+ hash_table_put (transport_map, (void *) fd, info);
+ ++transport_map_modified_tick;
+}
+
+/* When fd_read/fd_write are called multiple times in a loop, they should
+ remember the INFO pointer instead of fetching it every time. It is
+ not enough to compare FD to LAST_FD because FD might have been
+ closed and reopened. modified_tick ensures that changes to
+ transport_map will not be unnoticed.
+
+ This is a macro because we want the static storage variables to be
+ per-function. */
+
+#define LAZY_RETRIEVE_INFO(info) do { \
+ static struct transport_info *last_info; \
+ static int last_fd = -1, last_tick; \
+ if (!transport_map) \
+ info = NULL; \
+ else if (last_fd == fd && last_tick == transport_map_modified_tick) \
+ info = last_info; \
+ else \
+ { \
+ info = hash_table_get (transport_map, (void *) fd); \
+ last_fd = fd; \
+ last_info = info; \
+ last_tick = transport_map_modified_tick; \
+ } \
+} while (0)
+
+static int
+poll_internal (int fd, struct transport_info *info, int wf, double timeout)
+{
+ if (timeout == -1)
+ timeout = opt.read_timeout;
+ if (timeout)
+ {
+ int test;
+ if (info && info->poller)
+ test = info->poller (fd, timeout, wf, info->ctx);
+ else
+ test = sock_poll (fd, timeout, wf);
+ if (test == 0)
+ errno = ETIMEDOUT;
+ if (test <= 0)
+ return 0;
+ }
+ return 1;
+}
+
+/* Read no more than BUFSIZE bytes of data from FD, storing them to
+ BUF. If TIMEOUT is non-zero, the operation aborts if no data is
+ received after that many seconds. If TIMEOUT is -1, the value of
+ opt.timeout is used for TIMEOUT. */
+
+int
+fd_read (int fd, char *buf, int bufsize, double timeout)
+{
+ struct transport_info *info;
+ LAZY_RETRIEVE_INFO (info);
+ if (!poll_internal (fd, info, WAIT_FOR_READ, timeout))
+ return -1;
+ if (info && info->reader)
+ return info->reader (fd, buf, bufsize, info->ctx);
+ else
+ return sock_read (fd, buf, bufsize);
+}
+
+/* The same as xread, but don't actually read the data, just copy it
+ instead. */