aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libssh/priv.h25
-rw-r--r--libssh/CMakeLists.txt1
-rw-r--r--libssh/poll.c204
-rw-r--r--libssh/socket.c99
4 files changed, 233 insertions, 96 deletions
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index 47652a1c..51e89c6b 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -133,6 +133,28 @@ typedef BN_CTX* bignum_CTX;
#include <sys/time.h>
#endif
+/* poll support */
+#ifdef HAVE_POLL
+#include <poll.h>
+typedef struct pollfd pollfd_t;
+#else /* HAVE_POLL */
+typedef struct pollfd_s {
+ socket_t fd; /* file descriptor */
+ short events; /* requested events */
+ short revents; /* returned events */
+} pollfd_t;
+
+#define POLLIN 0x001 /* There is data to read. */
+#define POLLPRI 0x002 /* There is urgent data to read. */
+#define POLLOUT 0x004 /* Writing now will not block. */
+
+#define POLLERR 0x008 /* Error condition. */
+#define POLLHUP 0x010 /* Hung up. */
+#define POLLNVAL 0x020 /* Invalid polling request. */
+
+typedef unsigned long int nfds_t;
+#endif /* HAVE_POLL */
+
/* wrapper.c */
MD5CTX md5_init(void);
void md5_update(MD5CTX c, const void *data, unsigned long len);
@@ -474,6 +496,9 @@ STRING *agent_sign_data(struct ssh_session *session,
struct public_key_struct *pubkey);
#endif
+/* poll.c */
+int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout);
+
/* socket.c */
struct socket;
diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt
index 114a0265..0aceca88 100644
--- a/libssh/CMakeLists.txt
+++ b/libssh/CMakeLists.txt
@@ -84,6 +84,7 @@ set(libssh_SRCS
misc.c
options.c
packet.c
+ poll.c
session.c
socket.c
string.c
diff --git a/libssh/poll.c b/libssh/poll.c
new file mode 100644
index 00000000..c20300dd
--- /dev/null
+++ b/libssh/poll.c
@@ -0,0 +1,204 @@
+/*
+ * poll.c - poll wrapper
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2003-2008 by Aris Adamantiadis
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * vim: ts=2 sw=2 et cindent
+ */
+
+/* This code is based on glib's gpoll */
+
+#include "config.h"
+#include "libssh/priv.h"
+
+#ifdef HAVE_POLL
+#include <poll.h>
+
+int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) {
+ return poll((struct pollfd *) fds, nfds, timeout);
+}
+
+#else /* HAVE_POLL */
+#ifdef _WIN32
+
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
+
+#include <winsock2.h>
+
+int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) {
+ return WSAPoll(fds, nfds, timeout);
+}
+
+#else /* _WIN32_WINNT */
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+#include <stdio.h>
+#include <windows.h>
+
+static int poll_rest (HANDLE *handles, int nhandles,
+ pollfd_t *fds, nfds_t nfds, int timeout) {
+ DWORD ready;
+ pollfd_t *f;
+ int recursed_result;
+
+ if (nhandles == 0) {
+ /* No handles to wait for, just the timeout */
+ if (timeout == INFINITE) {
+ ready = WAIT_FAILED;
+ } else {
+ SleepEx(timeout, 1);
+ ready = WAIT_TIMEOUT;
+ }
+ } else {
+ /* Wait for just handles */
+ ready = WaitForMultipleObjectsEx(nhandles, handles, FALSE, timeout, TRUE);
+#if 0
+ if (ready == WAIT_FAILED) {
+ fprintf(stderr, "WaitForMultipleObjectsEx failed: %d\n", GetLastError());
+ }
+#endif
+ }
+
+ if (ready == WAIT_FAILED) {
+ return -1;
+ } else if (ready == WAIT_TIMEOUT || ready == WAIT_IO_COMPLETION) {
+ return 0;
+ } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
+ for (f = fds; f < &fds[nfds]; f++) {
+ if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0]) {
+ f->revents = f->events;
+ }
+ }
+
+ /*
+ * If no timeout and polling several handles, recurse to poll
+ * the rest of them.
+ */
+ if (timeout == 0 && nhandles > 1) {
+ /* Remove the handle that fired */
+ int i;
+ if (ready < nhandles - 1) {
+ for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++) {
+ handles[i-1] = handles[i];
+ }
+ }
+ nhandles--;
+ recursed_result = poll_rest(handles, nhandles, fds, nfds, 0);
+ if (recursed_result < 0) {
+ return -1;
+ }
+ return recursed_result + 1;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) {
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ pollfd_t *f;
+ int nhandles = 0;
+ int rc = -1;
+
+ if (fds == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (nfds >= MAXIMUM_WAIT_OBJECTS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (f = fds; f < &fds[nfds]; f++) {
+ if (f->fd > 0) {
+ int i;
+
+ /*
+ * Don't add the same handle several times into the array, as
+ * docs say that is not allowed, even if it actually does seem
+ * to work.
+ */
+ for (i = 0; i < nhandles; i++) {
+ if (handles[i] == (HANDLE) f->fd) {
+ break;
+ }
+ }
+
+ if (i == nhandles) {
+ if (nhandles == MAXIMUM_WAIT_OBJECTS) {
+ break;
+ } else {
+ handles[nhandles++] = (HANDLE) f->fd;
+ }
+ }
+ }
+ }
+
+ if (timeout == -1) {
+ timeout = INFINITE;
+ }
+
+ if (nhandles > 1) {
+ /*
+ * First check if one or several of them are immediately
+ * available.
+ */
+ rc = poll_rest(handles, nhandles, fds, nfds, 0);
+
+ /*
+ * If not, and we have a significant timeout, poll again with
+ * timeout then. Note that this will return indication for only
+ * one event, or only for messages. We ignore timeouts less than
+ * ten milliseconds as they are mostly pointless on Windows, the
+ * MsgWaitForMultipleObjectsEx() call will timeout right away
+ * anyway.
+ */
+ if (rc == 0 && (timeout == INFINITE || timeout >= 10)) {
+ rc = poll_rest(handles, nhandles, fds, nfds, timeout);
+ }
+ } else {
+ /*
+ * Just polling for one thing, so no need to check first if
+ * available immediately
+ */
+ rc = poll_rest(handles, nhandles, fds, nfds, timeout);
+ }
+
+ if (rc < 0) {
+ for (f = fds; f < &fds[nfds]; f++) {
+ f->revents = 0;
+ }
+ errno = EBADF;
+ }
+
+ return rc;
+}
+
+#endif /* _WIN32_WINNT */
+
+#endif /* _WIN32 */
+
+#endif /* HAVE_POLL */
+
diff --git a/libssh/socket.c b/libssh/socket.c
index 5bd00133..16da8cca 100644
--- a/libssh/socket.c
+++ b/libssh/socket.c
@@ -36,22 +36,6 @@
#endif
#include "libssh/priv.h"
-#if !defined(HAVE_SELECT) && !defined(HAVE_POLL)
-#error Your system must have either select() or poll()
-#endif
-
-#if !defined(HAVE_POLL) && !defined(_WIN32)
-#warning your system does not have poll. Select has known limitations
-#define SELECT_LIMIT_CHECK
-#endif
-
-#ifdef HAVE_POLL
-#define USE_POLL
-#include <poll.h>
-#else
-#define USE_SELECT
-#endif
-
/** \defgroup ssh_socket SSH Sockets
* \addtogroup ssh_socket
* @{
@@ -472,86 +456,10 @@ int ssh_socket_wait_for_data(struct socket *s, SSH_SESSION *session, u32 len) {
return SSH_OK;
}
-#ifdef USE_SELECT
-/* ssh_socket_poll, select() version */
-
-/* \internal
- * \brief polls the socket for data
- * \param session ssh session
- * \param writeable value pointed to set to 1 if it is possible to write
- * \param except value pointed to set to 1 if there is an exception
- * \return 1 if it is possible to read, 0 otherwise, -1 on error
- */
-int ssh_socket_poll(struct socket *s, int *writeable, int *except) {
- SSH_SESSION *session = s->session;
- struct timeval sometime;
- fd_set rdes; // read set
- fd_set wdes; // writing set
- fd_set edes; // exception set
- int fdmax =- 1;
-
- enter_function();
-
- FD_ZERO(&rdes);
- FD_ZERO(&wdes);
- FD_ZERO(&edes);
-
- if (!ssh_socket_is_open(s)) {
- *except = 1;
- *writeable = 0;
- return 0;
- }
-#ifdef SELECT_LIMIT_CHECK
- // some systems don't handle the fds > FD_SETSIZE
- if(s->fd > FD_SETSIZE){
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "File descriptor out of range for select: %d", s->fd);
-
- leave_function();
- return -1;
- }
-#endif
- if (!s->data_to_read) {
- ssh_socket_fd_set(s, &rdes, &fdmax);
- }
- if (!s->data_to_write) {
- ssh_socket_fd_set(s, &wdes, &fdmax);
- }
- ssh_socket_fd_set(s, &edes, &fdmax);
-
- /* Set to return immediately (no blocking) */
- sometime.tv_sec = 0;
- sometime.tv_usec = 0;
-
- /* Make the call, and listen for errors */
- if (select(fdmax, &rdes, &wdes, &edes, &sometime) < 0) {
- ssh_set_error(session, SSH_FATAL, "select(): %s", strerror(errno));
- leave_function();
- return -1;
- }
-
- if (!s->data_to_read) {
- s->data_to_read = ssh_socket_fd_isset(s, &rdes);
- }
- if (!s->data_to_write) {
- s->data_to_write = ssh_socket_fd_isset(s, &wdes);
- }
- if (!s->data_except) {
- s->data_except = ssh_socket_fd_isset(s, &edes);
- }
- *except = s->data_except;
- *writeable = s->data_to_write;
-
- leave_function();
- return (s->data_to_read || (buffer_get_rest_len(s->in_buffer) > 0));
-}
-#endif
-
-#ifdef USE_POLL
-/* ssh_socket_poll, poll() version */
+/* ssh_socket_poll */
int ssh_socket_poll(struct socket *s, int *writeable, int *except) {
SSH_SESSION *session = s->session;
- struct pollfd fd[1];
+ pollfd_t fd[1];
int rc = -1;
enter_function();
@@ -573,7 +481,7 @@ int ssh_socket_poll(struct socket *s, int *writeable, int *except) {
}
/* Make the call, and listen for errors */
- rc = poll(fd, 1, 0);
+ rc = ssh_poll(fd, 1, 0);
if (rc < 0) {
ssh_set_error(session, SSH_FATAL, "poll(): %s", strerror(errno));
leave_function();
@@ -596,7 +504,6 @@ int ssh_socket_poll(struct socket *s, int *writeable, int *except) {
leave_function();
return (s->data_to_read || (buffer_get_rest_len(s->in_buffer) > 0));
}
-#endif
/** \internal
* \brief nonblocking flush of the output buffer