static int stream_send__drain (evcom_stream *stream) { if (stream->on_drain) { stream->on_drain(stream); } if (!GOT_CLOSE(stream)) { stream->send_action = stream_send__wait_for_buf; return OKAY; } #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { stream->send_action = stream_send__gnutls_bye; return OKAY; } #endif if (DUPLEX(stream)) { stream->send_action = stream_send__shutdown; return OKAY; } stream->send_action = stream_send__close_one; return OKAY; }
static ssize_t pull (gnutls_transport_ptr_t data, void* buf, size_t len) { evcom_stream *stream = (evcom_stream*)data; assert(SECURE(stream)); return read(stream->recvfd, buf, len); }
static ssize_t nosigpipe_push (gnutls_transport_ptr_t data, const void *buf, size_t len) { evcom_stream *stream = (evcom_stream*)data; assert(SECURE(stream)); return nosigpipe_stream_send(stream, buf, len); }
void closeInterface(Interface * interface) { assert(interface->open); interface->open = 0; while(fclose(interface->fp) && errno==EINTR); if(interface->commandList) freeList(interface->commandList); if(interface->bufferList) freeList(interface->bufferList); free(interface->outBuffer); SECURE("interface %i: closed\n",interface->num); }
void openAInterface(int fd, struct sockaddr * addr) { int i; for(i=0;i<interface_max_connections && interfaces[i].open;i++); if(i==interface_max_connections) { ERROR("Max Connections Reached!\n"); while(close(fd) && errno==EINTR); } else { SECURE("interface %i: opened from ",i); switch(addr->sa_family) { case AF_INET: { char * host = inet_ntoa( ((struct sockaddr_in *)addr)-> sin_addr); if(host) { SECURE("%s\n",host); } else { SECURE("error getting ipv4 address\n"); } } break; #ifdef HAVE_IPV6 case AF_INET6: { char host[INET6_ADDRSTRLEN+1]; memset(host,0,INET6_ADDRSTRLEN+1); if(inet_ntop(AF_INET6,(void *) &(((struct sockaddr_in6 *)addr)-> sin6_addr),host,INET6_ADDRSTRLEN)) { SECURE("%s\n",host); } else { SECURE("error getting ipv6 address\n"); } } break; #endif case AF_UNIX: SECURE("local connection\n"); break; default: SECURE("unknown\n"); } openInterface(&(interfaces[i]),fd); } }
void evcom_stream_assign_fds (evcom_stream *stream, int recvfd, int sendfd) { assert(recvfd >= 0); assert(sendfd >= 0); if (recvfd == sendfd) stream->flags |= EVCOM_DUPLEX; if (set_nonblock(recvfd) != 0) { evcom_perror("set_nonblock(recvfd)", errno); } if (set_nonblock(sendfd) != 0) { evcom_perror("set_nonblock(sendfd)", errno); } #ifdef SO_NOSIGPIPE if (DUPLEX(stream)) { int flags = 1; int r = setsockopt(sendfd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof(flags)); if (r < 0) { evcom_perror("setsockopt(SO_NOSIGPIPE)", errno); } } #endif ev_io_set(&stream->read_watcher, recvfd, EV_READ); ev_io_set(&stream->write_watcher, sendfd, EV_WRITE); stream->recvfd = recvfd; stream->sendfd = sendfd; stream->send_action = stream__connection_established; stream->recv_action = stream__connection_established; stream->flags |= EVCOM_READABLE; stream->flags |= EVCOM_WRITABLE; #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { gnutls_transport_set_lowat(stream->session, 0); gnutls_transport_set_push_function(stream->session, nosigpipe_push); gnutls_transport_set_pull_function(stream->session, pull); gnutls_transport_set_ptr2(stream->session, stream, stream); } #endif }
static int stream__handshake (evcom_stream *stream) { assert(SECURE(stream)); int r = gnutls_handshake(stream->session); if (gnutls_error_is_fatal(r)) { stream->gnutls_errorno = r; stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } else { ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } assert(stream->recv_action == stream__handshake); assert(stream->send_action == stream__handshake); return AGAIN; } assert(!CONNECTED(stream)); stream->flags |= EVCOM_CONNECTED; if (stream->on_connect) stream->on_connect(stream); /* evcom_stream_force_close might have been called. */ if (stream->recvfd >= 0 && stream->sendfd >= 0) { ev_io_start(D_LOOP_(stream) &stream->read_watcher); ev_io_start(D_LOOP_(stream) &stream->write_watcher); stream->send_action = stream_send__data; stream->recv_action = stream_recv__data; } return OKAY; }
static int stream__connection_established (evcom_stream *stream) { assert(!CONNECTED(stream)); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { stream->send_action = stream__handshake; stream->recv_action = stream__handshake; } else #endif { stream->flags |= EVCOM_CONNECTED; if (stream->on_connect) stream->on_connect(stream); stream->send_action = stream_send__data; stream->recv_action = stream_recv__data; } ev_io_start(D_LOOP_(stream) &stream->write_watcher); ev_io_start(D_LOOP_(stream) &stream->read_watcher); return OKAY; }
static int stream_send__gnutls_bye (evcom_stream *stream) { assert(SECURE(stream)); int r = gnutls_bye(stream->session, GNUTLS_SHUT_WR); if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { assert(1 == gnutls_record_get_direction((stream)->session)); assert(stream->send_action == stream_send__gnutls_bye); return AGAIN; } if (gnutls_error_is_fatal(r)) { stream->gnutls_errorno = r; stream->send_action = stream_send__close; return OKAY; } stream->flags &= ~EVCOM_WRITABLE; stream->send_action = stream_send__wait_for_eof; return OKAY; }
static int stream_send__data (evcom_stream *stream) { ssize_t sent; while (!evcom_queue_empty(&stream->out)) { assert(WRITABLE(stream)); evcom_queue *q = evcom_queue_last(&stream->out); evcom_buf *buf = evcom_queue_data(q, evcom_buf, queue); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, buf->base + buf->written, buf->len - buf->written); if (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls send: unexpected switch direction!\n"); ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; stream->send_action = stream_send__close; return OKAY; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, buf->base + buf->written, buf->len - buf->written); } if (sent <= 0) { switch (errno) { case EAGAIN: case EINTR: assert(stream->send_action == stream_send__data); return AGAIN; default: stream->errorno = errno; evcom_perror("send()", errno); /* pass through */ case EPIPE: stream->send_action = stream_send__close; return OKAY; } } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(sent >= 0); buf->written += sent; if (buf->written == buf->len) { evcom_queue_remove(q); if (buf->release) buf->release(buf); } } assert(evcom_queue_empty(&stream->out)); stream->send_action = stream_send__drain; return OKAY; }
static int stream_recv__data (evcom_stream *stream) { char buf[EVCOM_CHUNKSIZE]; size_t buf_size = EVCOM_CHUNKSIZE; ssize_t recved; while (READABLE(stream)) { assert(CONNECTED(stream)); if (PAUSED(stream)) { stream->recv_action = stream_recv__wait_for_resume; return OKAY; } #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { recved = gnutls_record_recv(stream->session, buf, buf_size); if (gnutls_error_is_fatal(recved)) { stream->gnutls_errorno = recved; stream->recv_action = stream_recv__close; return OKAY; } if (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) { if (1 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls recv: unexpected switch direction!\n"); ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } /* A server may also receive GNUTLS_E_REHANDSHAKE when a client has * initiated a andshake. In that case the server can only initiate a * handshake or terminate the connection. */ if (recved == GNUTLS_E_REHANDSHAKE) { assert(WRITABLE(stream)); stream->recv_action = stream__handshake; stream->send_action = stream__handshake; return OKAY; } } else #endif /* EVCOM_HAVE_GNUTLS */ { recved = read(stream->recvfd, buf, buf_size); } if (recved < 0) { if (errno == EAGAIN || errno == EINTR) { assert(stream->recv_action == stream_recv__data); return AGAIN; } if (errno != ECONNRESET) { evcom_perror("recv()", stream->errorno); } stream->errorno = errno; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(recved >= 0); if (recved == 0) { stream->flags &= ~EVCOM_READABLE; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); stream->recv_action = stream_recv__wait_for_close; } /* NOTE: EOF is signaled with recved == 0 on callback */ if (stream->on_read) stream->on_read(stream, buf, recved); if (recved == 0) { return OKAY; } } return AGAIN; }
/* Returns the number of bytes flushed to the buffer */ ssize_t evcom_stream_write (evcom_stream *stream, const char *str, size_t len) { if (!WRITABLE(stream) || GOT_CLOSE(stream)) { assert(0 && "Do not write to a closed stream"); return -1; } ssize_t sent = 0; if ( stream->send_action == stream_send__wait_for_buf && evcom_queue_empty(&stream->out) ) { assert(CONNECTED(stream)); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, str, len); if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; goto close; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, str, len); } if (sent < 0) { switch (errno) { case EPIPE: goto close; case EINTR: case EAGAIN: sent = 0; break; default: stream->errorno = errno; evcom_perror("send()", stream->errorno); goto close; } } } /* TODO else { memcpy to last buffer on head } */ assert(sent >= 0); if ((size_t)sent == len) return sent; /* sent the whole buffer */ len -= sent; str += sent; evcom_buf *b = evcom_buf_new(str, len); evcom_queue_insert_head(&stream->out, &b->queue); b->written = 0; assert(stream->sendfd >= 0); if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return sent; close: stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return -1; }