summaryrefslogtreecommitdiffstats
path: root/libssh/src/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'libssh/src/socket.c')
-rw-r--r--libssh/src/socket.c260
1 files changed, 144 insertions, 116 deletions
diff --git a/libssh/src/socket.c b/libssh/src/socket.c
index c76ef5ae..498da77e 100644
--- a/libssh/src/socket.c
+++ b/libssh/src/socket.c
@@ -182,8 +182,8 @@ void ssh_socket_reset(ssh_socket s){
s->fd_out= SSH_INVALID_SOCKET;
s->last_errno = -1;
s->fd_is_socket = 1;
- buffer_reinit(s->in_buffer);
- buffer_reinit(s->out_buffer);
+ ssh_buffer_reinit(s->in_buffer);
+ ssh_buffer_reinit(s->out_buffer);
s->read_wontblock = 0;
s->write_wontblock = 0;
s->data_except = 0;
@@ -204,7 +204,7 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
}
/**
- * @brief SSH poll callback. This callback will be used when an event
+ * @brief SSH poll callback. This callback will be used when an event
* caught on the socket.
*
* @param p Poll object this callback belongs to.
@@ -216,123 +216,131 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
* @return 0 on success, < 0 when the poll object has been removed
* from its poll context.
*/
-int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){
- ssh_socket s=(ssh_socket )v_s;
- char buffer[4096];
- int r;
- int err=0;
- socklen_t errlen=sizeof(err);
- /* Do not do anything if this socket was already closed */
- if(!ssh_socket_is_open(s)){
- return -1;
- }
- if(revents & POLLERR || revents & POLLHUP){
- /* Check if we are in a connecting state */
- if(s->state==SSH_SOCKET_CONNECTING){
- s->state=SSH_SOCKET_ERROR;
- r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
+int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd,
+ int revents, void *v_s) {
+ ssh_socket s = (ssh_socket)v_s;
+ char buffer[MAX_BUF_SIZE];
+ int r;
+ int err = 0;
+ socklen_t errlen = sizeof(err);
+ /* Do not do anything if this socket was already closed */
+ if (!ssh_socket_is_open(s)) {
+ return -1;
+ }
+ if (revents & POLLERR || revents & POLLHUP) {
+ /* Check if we are in a connecting state */
+ if (s->state == SSH_SOCKET_CONNECTING) {
+ s->state = SSH_SOCKET_ERROR;
+ r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
if (r < 0) {
err = errno;
}
- s->last_errno=err;
- ssh_socket_close(s);
- if(s->callbacks && s->callbacks->connected)
- s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err,
- s->callbacks->userdata);
- return -1;
- }
- /* Then we are in a more standard kind of error */
- /* force a read to get an explanation */
- revents |= POLLIN;
- }
- if(revents & POLLIN){
- s->read_wontblock=1;
- r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer));
- if(r<0){
- if(p != NULL) {
- ssh_poll_remove_events(p, POLLIN);
- }
- if(s->callbacks && s->callbacks->exception){
- s->callbacks->exception(
- SSH_SOCKET_EXCEPTION_ERROR,
- s->last_errno,s->callbacks->userdata);
- /* p may have been freed, so don't use it
- * anymore in this function */
- p = NULL;
- return -2;
- }
- }
- if(r==0){
- if(p != NULL) {
- ssh_poll_remove_events(p, POLLIN);
- }
- if(p != NULL) {
- ssh_poll_remove_events(p, POLLIN);
- }
- if(s->callbacks && s->callbacks->exception){
- s->callbacks->exception(
- SSH_SOCKET_EXCEPTION_EOF,
- 0,s->callbacks->userdata);
- /* p may have been freed, so don't use it
- * anymore in this function */
- p = NULL;
- return -2;
- }
- }
- if(r>0){
- /* Bufferize the data and then call the callback */
- r = buffer_add_data(s->in_buffer,buffer,r);
+ s->last_errno = err;
+ ssh_socket_close(s);
+ if (s->callbacks && s->callbacks->connected) {
+ s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR, err,
+ s->callbacks->userdata);
+ }
+ return -1;
+ }
+ /* Then we are in a more standard kind of error */
+ /* force a read to get an explanation */
+ revents |= POLLIN;
+ }
+ if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
+ s->read_wontblock = 1;
+ r = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer));
+ if (r < 0) {
+ if (p != NULL) {
+ ssh_poll_remove_events(p, POLLIN);
+ }
+ if (s->callbacks && s->callbacks->exception) {
+ s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
+ s->last_errno, s->callbacks->userdata);
+ /* p may have been freed, so don't use it
+ * anymore in this function */
+ p = NULL;
+ return -2;
+ }
+ }
+ if (r == 0) {
+ if (p != NULL) {
+ ssh_poll_remove_events(p, POLLIN);
+ }
+ if (p != NULL) {
+ ssh_poll_remove_events(p, POLLIN);
+ }
+ if (s->callbacks && s->callbacks->exception) {
+ s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF,
+ 0, s->callbacks->userdata);
+ /* p may have been freed, so don't use it
+ * anymore in this function */
+ p = NULL;
+ return -2;
+ }
+ }
+ if (r > 0) {
+ if (s->session->socket_counter != NULL) {
+ s->session->socket_counter->in_bytes += r;
+ }
+ /* Bufferize the data and then call the callback */
+ r = ssh_buffer_add_data(s->in_buffer, buffer, r);
if (r < 0) {
return -1;
}
- if(s->callbacks && s->callbacks->data){
- do {
- r= s->callbacks->data(buffer_get_rest(s->in_buffer),
- buffer_get_rest_len(s->in_buffer),
- s->callbacks->userdata);
- buffer_pass_bytes(s->in_buffer,r);
- } while (r > 0);
- /* p may have been freed, so don't use it
- * anymore in this function */
- p = NULL;
- }
- }
- }
+ if (s->callbacks && s->callbacks->data) {
+ do {
+ r = s->callbacks->data(buffer_get_rest(s->in_buffer),
+ buffer_get_rest_len(s->in_buffer),
+ s->callbacks->userdata);
+ buffer_pass_bytes(s->in_buffer, r);
+ } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED));
+ /* p may have been freed, so don't use it
+ * anymore in this function */
+ p = NULL;
+ }
+ }
+ }
#ifdef _WIN32
- if(revents & POLLOUT || revents & POLLWRNORM){
+ if (revents & POLLOUT || revents & POLLWRNORM) {
#else
- if(revents & POLLOUT){
+ if (revents & POLLOUT) {
#endif
- /* First, POLLOUT is a sign we may be connected */
- if(s->state == SSH_SOCKET_CONNECTING){
- SSH_LOG(SSH_LOG_PACKET,"Received POLLOUT in connecting state");
- s->state = SSH_SOCKET_CONNECTED;
- ssh_poll_set_events(p,POLLOUT | POLLIN);
+ /* First, POLLOUT is a sign we may be connected */
+ if (s->state == SSH_SOCKET_CONNECTING) {
+ SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
+ s->state = SSH_SOCKET_CONNECTED;
+ if (p != NULL) {
+ ssh_poll_set_events(p, POLLOUT | POLLIN);
+ }
r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s));
if (r < 0) {
return -1;
}
- if(s->callbacks && s->callbacks->connected)
- s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
- return 0;
- }
- /* So, we can write data */
- s->write_wontblock=1;
- if(p != NULL) {
+ if (s->callbacks && s->callbacks->connected) {
+ s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0,
+ s->callbacks->userdata);
+ }
+ return 0;
+ }
+ /* So, we can write data */
+ s->write_wontblock=1;
+ if (p != NULL) {
ssh_poll_remove_events(p, POLLOUT);
}
- /* If buffered data is pending, write it */
- if(buffer_get_rest_len(s->out_buffer) > 0){
- ssh_socket_nonblocking_flush(s);
- } else if(s->callbacks && s->callbacks->controlflow){
- /* Otherwise advertise the upper level that write can be done */
- s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata);
- }
- /* TODO: Find a way to put back POLLOUT when buffering occurs */
- }
- /* Return -1 if one of the poll handlers disappeared */
- return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0;
+ /* If buffered data is pending, write it */
+ if (buffer_get_rest_len(s->out_buffer) > 0) {
+ ssh_socket_nonblocking_flush(s);
+ } else if (s->callbacks && s->callbacks->controlflow) {
+ /* Otherwise advertise the upper level that write can be done */
+ s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
+ s->callbacks->userdata);
+ }
+ /* TODO: Find a way to put back POLLOUT when buffering occurs */
+ }
+ /* Return -1 if one of the poll handlers disappeared */
+ return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0;
}
/** @internal
@@ -438,6 +446,8 @@ void ssh_socket_close(ssh_socket s){
ssh_poll_free(s->poll_out);
s->poll_out=NULL;
}
+
+ s->state = SSH_SOCKET_CLOSED;
}
/**
@@ -449,9 +459,19 @@ void ssh_socket_close(ssh_socket s){
* file descriptors
*/
void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
- s->fd_in = s->fd_out = fd;
- if(s->poll_in)
- ssh_poll_set_fd(s->poll_in,fd);
+ s->fd_in = s->fd_out = fd;
+
+ if (s->poll_in) {
+ ssh_poll_set_fd(s->poll_in,fd);
+ } else {
+ s->state = SSH_SOCKET_CONNECTING;
+
+ /* POLLOUT is the event to wait for in a nonblocking connect */
+ ssh_poll_set_events(ssh_socket_get_poll_handle_in(s), POLLOUT);
+#ifdef _WIN32
+ ssh_poll_add_events(ssh_socket_get_poll_handle_in(s), POLLWRNORM);
+#endif
+ }
}
/**
@@ -594,7 +614,7 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) {
*/
int ssh_socket_write(ssh_socket s, const void *buffer, int len) {
if(len > 0) {
- if (buffer_add_data(s->out_buffer, buffer, len) < 0) {
+ if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
ssh_set_error_oom(s->session);
return SSH_ERROR;
}
@@ -645,6 +665,9 @@ int ssh_socket_nonblocking_flush(ssh_socket s) {
return SSH_ERROR;
}
buffer_pass_bytes(s->out_buffer, w);
+ if (s->session->socket_counter != NULL) {
+ s->session->socket_counter->out_bytes += w;
+ }
}
/* Is there some data pending? */
@@ -695,11 +718,11 @@ int ssh_socket_buffered_write_bytes(ssh_socket s){
int ssh_socket_get_status(ssh_socket s) {
int r = 0;
- if (buffer_get_len(s->in_buffer) > 0) {
+ if (ssh_buffer_get_len(s->in_buffer) > 0) {
r |= SSH_READ_PENDING;
}
- if (buffer_get_len(s->out_buffer) > 0) {
+ if (ssh_buffer_get_len(s->out_buffer) > 0) {
r |= SSH_WRITE_PENDING;
}
@@ -710,6 +733,17 @@ int ssh_socket_get_status(ssh_socket s) {
return r;
}
+int ssh_socket_get_poll_flags(ssh_socket s) {
+ int r = 0;
+ if (s->poll_in != NULL && (ssh_poll_get_events (s->poll_in) & POLLIN) > 0) {
+ r |= SSH_READ_PENDING;
+ }
+ if (s->poll_out != NULL && (ssh_poll_get_events (s->poll_out) & POLLOUT) > 0) {
+ r |= SSH_WRITE_PENDING;
+ }
+ return r;
+}
+
#ifdef _WIN32
int ssh_socket_set_nonblocking(socket_t fd) {
u_long nonblocking = 1;
@@ -759,12 +793,6 @@ int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bin
if(fd == SSH_INVALID_SOCKET)
return SSH_ERROR;
ssh_socket_set_fd(s,fd);
- s->state=SSH_SOCKET_CONNECTING;
- /* POLLOUT is the event to wait for in a nonblocking connect */
- ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT);
-#ifdef _WIN32
- ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM);
-#endif
return SSH_OK;
}