aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2009-11-30 23:44:40 +0100
committerAris Adamantiadis <aris@0xbadc0de.be>2009-11-30 23:44:40 +0100
commit92a50f731cdb098bbbb605630f335a07d839496f (patch)
tree48b586e16741a3cd775fa197adc700bcf7e143b3
parent76d6838223718a5432baddb4fa5b3e82440c9ff2 (diff)
downloadlibssh-92a50f731cdb098bbbb605630f335a07d839496f.tar.gz
libssh-92a50f731cdb098bbbb605630f335a07d839496f.tar.xz
libssh-92a50f731cdb098bbbb605630f335a07d839496f.zip
Socket connect callback working...
Still need to make sure the connect syscall is correctly called
-rw-r--r--include/libssh/callbacks.h2
-rw-r--r--include/libssh/priv.h3
-rw-r--r--include/libssh/socket.h1
-rw-r--r--libssh/connect.c82
-rw-r--r--libssh/socket.c77
-rw-r--r--tests/Makefile2
-rw-r--r--tests/test_socket.c14
7 files changed, 169 insertions, 12 deletions
diff --git a/include/libssh/callbacks.h b/include/libssh/callbacks.h
index 19a4c461..c7a9e3cf 100644
--- a/include/libssh/callbacks.h
+++ b/include/libssh/callbacks.h
@@ -50,7 +50,7 @@ typedef void (*ssh_callback_int) (int code, void *user);
*/
typedef int (*ssh_callback_data) (const void *data, size_t len, void *user);
-typedef void (*ssh_callback_int_int) (void *user, int code, int errno_code);
+typedef void (*ssh_callback_int_int) (int code, int errno_code, void *user);
typedef int (*ssh_message_callback) (ssh_session, ssh_message message, void *user);
typedef int (*ssh_channel_callback_int) (ssh_channel channel, int code, void *user);
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index f96e87c5..137cdb25 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -150,7 +150,8 @@ void ssh_regex_finalize(void);
ssh_session ssh_session_new(void);
socket_t ssh_connect_host(ssh_session session, const char *host,const char
*bind_addr, int port, long timeout, long usec);
-
+socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
+ const char *bind_addr, int port);
/* in kex.c */
extern const char *ssh_kex_nums[];
int ssh_send_kex(ssh_session session, int server_kex);
diff --git a/include/libssh/socket.h b/include/libssh/socket.h
index 858c5d82..0c2a6c9e 100644
--- a/include/libssh/socket.h
+++ b/include/libssh/socket.h
@@ -58,5 +58,6 @@ void ssh_socket_set_callbacks(struct socket *s, ssh_socket_callbacks callbacks);
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, int fd, int revents, void *s);
void ssh_socket_register_pollcallback(struct socket *s, struct ssh_poll_handle_struct *p);
struct ssh_poll_handle_struct * ssh_socket_get_poll_handle(struct socket *s);
+int ssh_socket_connect(struct socket *s, const char *host, int port, const char *bind_addr);
#endif /* SOCKET_H_ */
diff --git a/libssh/connect.c b/libssh/connect.c
index 63aa7cfb..896bcf0b 100644
--- a/libssh/connect.c
+++ b/libssh/connect.c
@@ -356,6 +356,88 @@ socket_t ssh_connect_host(ssh_session session, const char *host,
}
/**
+ * @internal
+ *
+ * @brief Launches a nonblocking connect to an IPv4 or IPv6 host
+ * specified by its IP address or hostname.
+ *
+ * @returns A file descriptor, < 0 on error.
+ * @warning very ugly !!!
+ */
+socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
+ const char *bind_addr, int port) {
+ socket_t s = -1;
+ int rc;
+ struct addrinfo *ai;
+ struct addrinfo *itr;
+
+ enter_function();
+
+ rc = getai(session,host, port, &ai);
+ if (rc != 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "Failed to resolve hostname %s (%s)", host, gai_strerror(rc));
+ leave_function();
+ return -1;
+ }
+
+ for (itr = ai; itr != NULL; itr = itr->ai_next){
+ /* create socket */
+ s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
+ if (s < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "Socket create failed: %s", strerror(errno));
+ continue;
+ }
+
+ if (bind_addr) {
+ struct addrinfo *bind_ai;
+ struct addrinfo *bind_itr;
+
+ ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr);
+
+ rc = getai(session,bind_addr, 0, &bind_ai);
+ if (rc != 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "Failed to resolve bind address %s (%s)",
+ bind_addr,
+ gai_strerror(rc));
+ close(s);
+ s=-1;
+ break;
+ }
+
+ for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) {
+ if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "Binding local address: %s", strerror(errno));
+ continue;
+ } else {
+ break;
+ }
+ }
+ freeaddrinfo(bind_ai);
+
+ /* Cannot bind to any local addresses */
+ if (bind_itr == NULL) {
+ ssh_connect_socket_close(s);
+ s = -1;
+ continue;
+ }
+ }
+ sock_set_nonblocking(s);
+
+ connect(s, itr->ai_addr, itr->ai_addrlen);
+ break;
+ }
+
+ freeaddrinfo(ai);
+ leave_function();
+
+ return s;
+}
+
+/**
* @addtogroup ssh_session
* @{ */
diff --git a/libssh/socket.c b/libssh/socket.c
index a250f2ea..7790af19 100644
--- a/libssh/socket.c
+++ b/libssh/socket.c
@@ -44,6 +44,15 @@
* @{
*/
+enum ssh_socket_states_e {
+ SSH_SOCKET_NONE,
+ SSH_SOCKET_CONNECTING,
+ SSH_SOCKET_CONNECTED,
+ SSH_SOCKET_EOF,
+ SSH_SOCKET_ERROR,
+ SSH_SOCKET_CLOSED
+};
+
struct socket {
socket_t fd;
int last_errno;
@@ -51,6 +60,7 @@ struct socket {
not block */
int data_to_write;
int data_except;
+ enum ssh_socket_states_e state;
ssh_buffer out_buffer;
ssh_buffer in_buffer;
ssh_session session;
@@ -105,7 +115,8 @@ struct socket *ssh_socket_new(ssh_session session) {
s->data_to_read = 0;
s->data_to_write = 0;
s->data_except = 0;
-
+ s->poll=NULL;
+ s->state=SSH_SOCKET_NONE;
return s;
}
@@ -136,17 +147,17 @@ int ssh_socket_pollcallback(ssh_poll_handle p, int fd, int revents, void *v_s){
if(r<0){
ssh_poll_set_events(p,ssh_poll_get_events(p) & ~POLLIN);
if(s->callbacks){
- s->callbacks->exception(s->callbacks->user,
+ s->callbacks->exception(
SSH_SOCKET_EXCEPTION_ERROR,
- s->last_errno);
+ s->last_errno,s->callbacks->user);
}
}
if(r==0){
ssh_poll_set_events(p,ssh_poll_get_events(p) & ~POLLIN);
if(s->callbacks){
- s->callbacks->exception(s->callbacks->user,
+ s->callbacks->exception(
SSH_SOCKET_EXCEPTION_EOF,
- 0);
+ 0,s->callbacks->user);
}
}
if(r>0){
@@ -161,6 +172,16 @@ int ssh_socket_pollcallback(ssh_poll_handle p, int fd, int revents, void *v_s){
}
}
if(revents & POLLOUT){
+ /* First, POLLOUT is a sign we may be connected */
+ if(s->state == SSH_SOCKET_CONNECTING){
+ ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state");
+ s->state = SSH_SOCKET_CONNECTED;
+ ssh_poll_set_events(p,POLLOUT | POLLIN | POLLERR);
+ if(s->callbacks && s->callbacks->connected)
+ s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->user);
+ return 0;
+ }
+ /* So, we can write data */
s->data_to_write=1;
/* If buffered data is pending, write it */
if(buffer_get_rest_len(s->out_buffer) > 0){
@@ -258,6 +279,8 @@ void ssh_socket_close(struct socket *s){
*/
void ssh_socket_set_fd(struct socket *s, socket_t fd) {
s->fd = fd;
+ if(s->poll)
+ ssh_poll_set_fd(s->poll,fd);
}
/* \internal
@@ -724,6 +747,50 @@ int ssh_socket_get_status(struct socket *s) {
return r;
}
+/**
+ * @internal
+ * @brief Launches a socket connection
+ * If a the socket connected callback has been defined and
+ * a poll object exists, this call will be non blocking
+ * @param
+ */
+
+int ssh_socket_connect(struct socket *s, const char *host, int port, const char *bind_addr){
+ socket_t fd;
+ ssh_session session=s->session;
+ ssh_poll_ctx ctx;
+ enter_function();
+ if(s->state != SSH_SOCKET_NONE)
+ return SSH_ERROR;
+ fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port);
+ ssh_socket_set_fd(s,fd);
+ s->state=SSH_SOCKET_CONNECTING;
+ if(s->callbacks && s->callbacks->connected && s->poll){
+ /* POLLOUT is the event to wait for in a nonblocking connect */
+ ssh_poll_set_events(s->poll,POLLOUT);
+ leave_function();
+ return SSH_OK;
+ } else {
+ /* we have to do the connect ourselves */
+ ssh_poll_set_events(ssh_socket_get_poll_handle(s),POLLOUT);
+ ctx=ssh_poll_ctx_new(1);
+ ssh_poll_ctx_add(ctx,s->poll);
+ while(s->state == SSH_SOCKET_CONNECTING){
+ ssh_poll_ctx_dopoll(ctx,-1);
+ }
+ ssh_poll_ctx_free(ctx);
+ if(s->state == SSH_SOCKET_CONNECTED){
+ ssh_log(session,SSH_LOG_PACKET,"ssh_socket_connect blocking: connected");
+ leave_function();
+ return SSH_OK;
+ } else {
+ ssh_log(session,SSH_LOG_PACKET,"ssh_socket_connect blocking: not connected");
+ ssh_set_error(session,SSH_FATAL,"Error during blocking connect: %d",s->last_errno);
+ leave_function();
+ return SSH_ERROR;
+ }
+ }
+}
/** @}
*/
/* vim: set ts=2 sw=2 et cindent: */
diff --git a/tests/Makefile b/tests/Makefile
index 67353465..c3176e05 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,5 +1,5 @@
all: test_socket test_tunnel test_exec test_pcap
-CFLAGS=-I../include/ -g -Wall
+CFLAGS=-I../include/ -g -Wall -I../build/
LDFLAGS=-lssh -L../build/libssh/
test_tunnel: test_tunnel.o authentication.o connection.o
diff --git a/tests/test_socket.c b/tests/test_socket.c
index b0ca03ed..2c53d976 100644
--- a/tests/test_socket.c
+++ b/tests/test_socket.c
@@ -28,6 +28,7 @@
#include <libssh/callbacks.h>
#include <libssh/socket.h>
+#include <libssh/poll.h>
static int data_rcv(const void *data, size_t len, void *user){
printf("Received data: '");
@@ -36,15 +37,15 @@ static int data_rcv(const void *data, size_t len, void *user){
return len;
}
-static void controlflow(void *user, int code){
+static void controlflow(int code,void *user){
printf("Control flow: %x\n",code);
}
-static void exception(void *user, int code, int errno_code){
+static void exception(int code, int errno_code,void *user){
printf("Exception: %d (%d)\n",code,errno_code);
}
-static void connected(void *user, int code, int errno_code){
+static void connected(int code, int errno_code,void *user){
printf("Connected: %d (%d)\n",code, errno_code);
}
@@ -64,10 +65,15 @@ int main(int argc, char **argv){
return EXIT_FAILURE;
}
session=ssh_new();
+ ssh_init();
s=ssh_socket_new(session);
ctx=ssh_poll_ctx_new(2);
ssh_socket_set_callbacks(s, &callbacks);
ssh_poll_ctx_add_socket(ctx,s);
-
+ if(ssh_socket_connect(s,argv[1],atoi(argv[2]),NULL)){
+ printf("ssh_socket_connect: %s\n",ssh_get_error(session));
+ return EXIT_FAILURE;
+ }
+ ssh_poll_ctx_dopoll(ctx,-1);
return EXIT_SUCCESS;
}