diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2009-11-30 23:44:40 +0100 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2009-11-30 23:44:40 +0100 |
commit | 92a50f731cdb098bbbb605630f335a07d839496f (patch) | |
tree | 48b586e16741a3cd775fa197adc700bcf7e143b3 | |
parent | 76d6838223718a5432baddb4fa5b3e82440c9ff2 (diff) | |
download | libssh-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.h | 2 | ||||
-rw-r--r-- | include/libssh/priv.h | 3 | ||||
-rw-r--r-- | include/libssh/socket.h | 1 | ||||
-rw-r--r-- | libssh/connect.c | 82 | ||||
-rw-r--r-- | libssh/socket.c | 77 | ||||
-rw-r--r-- | tests/Makefile | 2 | ||||
-rw-r--r-- | tests/test_socket.c | 14 |
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; } |