diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/jail.c | 9 | ||||
-rw-r--r-- | src/pevent.c | 95 | ||||
-rw-r--r-- | src/pevent.h | 13 | ||||
-rw-r--r-- | src/server.c | 298 |
4 files changed, 220 insertions, 195 deletions
@@ -21,11 +21,6 @@ typedef struct jail_prisoner_process { char *newroot; } jail_prisoner_process; -typedef enum connection_state { - CON_OK, CON_IN_TERMINATED, CON_OUT_TERMINATED, - CON_IN_ERROR, CON_OUT_ERROR -} connection_state; - static int jail_mainloop_epoll(int epoll_fd, jail_ctx *ctx[], size_t siz) __attribute__((noreturn)); static int jail_accept_client(jail_ctx *ctx[], @@ -33,7 +28,7 @@ static int jail_accept_client(jail_ctx *ctx[], static int jail_childfn(jail_prisoner_process *ctx) __attribute__((noreturn)); static int jail_socket_tty_epoll(jail_prisoner_process *ctx, int tty_fd); -static connection_state +static forward_state jail_socket_tty_io_epoll(struct epoll_event *ev, int dest_fd); @@ -390,7 +385,7 @@ static int jail_socket_tty_epoll(jail_prisoner_process *ctx, int tty_fd) return 0; } -static connection_state +static forward_state jail_socket_tty_io_epoll(struct epoll_event *ev, int dest_fd) { return CON_OK; diff --git a/src/pevent.c b/src/pevent.c index a8845d3..053564c 100644 --- a/src/pevent.c +++ b/src/pevent.c @@ -1,3 +1,4 @@ +#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> @@ -20,6 +21,13 @@ void event_init(event_ctx **ctx) (*ctx)->epoll_fd = -1; } +void event_free(event_ctx **ctx) +{ + close((*ctx)->epoll_fd); + free((*ctx)); + *ctx = NULL; +} + int event_setup(event_ctx *ctx) { assert(ctx); @@ -83,16 +91,91 @@ int event_loop(event_ctx *ctx, on_event_cb on_event, void *user_data) { E_STRERR("Event epoll for descriptor %d", ctx->events[i].data.fd); - close(ctx->events[i].data.fd); - continue; + ctx->active = 0; + break; } else { - if (on_event(ctx->events[i].data.fd, user_data)) - continue; - W2("Event callback failed: [fd: %d , npoll: %d]", - ctx->events[i].data.fd, n); + ctx->current_event = i; + if (!on_event(ctx, ctx->events[i].data.fd, user_data)) + W2("Event callback failed: [fd: %d , npoll: %d]", + ctx->events[i].data.fd, n); } + + if (!ctx->active) + break; } } return ctx->active == 0; } + +forward_state +event_forward_connection(event_ctx *ctx, int dest_fd) +{ + int data_avail = 1; + int has_input; + int saved_errno; + forward_state rc = CON_OK; + ssize_t siz; + char buf[BUFSIZ+sizeof(long)]; + struct epoll_event *ev; + + assert(ctx->current_event >= 0 && + ctx->current_event < POTD_MAXEVENTS); + ev = &ctx->events[ctx->current_event]; + + while (data_avail) { + has_input = 0; + saved_errno = 0; + siz = -1; + + if (ev->events & EPOLLIN) { + has_input = 1; + errno = 0; + siz = read(ev->data.fd, &buf[0], BUFSIZ); + saved_errno = errno; + } else break; + + if (saved_errno == EAGAIN) + break; + + switch (siz) { + case -1: + E_STRERR("Client read from fd %d", ev->data.fd); + rc = CON_IN_ERROR; + break; + case 0: + rc = CON_IN_TERMINATED; + break; + default: + buf[siz] = 0; + D2("Read %lu bytes from fd %d", siz, ev->data.fd); + break; + } + + if (rc != CON_OK) + break; + + if (has_input) { + siz = write(dest_fd, &buf[0], siz); + + switch (siz) { + case -1: + rc = CON_OUT_ERROR; + break; + case 0: + rc = CON_OUT_TERMINATED; + break; + default: + D2("Written %lu bytes from fd %d to fd %d", + siz, ev->data.fd, dest_fd); + break; + } + } + + if (rc != CON_OK) + break; + } + + D2("Connection state: %d", rc); + return rc; +} diff --git a/src/pevent.h b/src/pevent.h index da09f59..c7b61e1 100644 --- a/src/pevent.h +++ b/src/pevent.h @@ -8,17 +8,25 @@ #define POTD_MAXFD 32 #define POTD_MAXEVENTS 64 +typedef enum forward_state { + CON_OK, CON_IN_TERMINATED, CON_OUT_TERMINATED, + CON_IN_ERROR, CON_OUT_ERROR +} forward_state; + typedef struct event_ctx { int epoll_fd; int active; struct epoll_event events[POTD_MAXEVENTS]; + int current_event; } event_ctx; -typedef int (*on_event_cb) (int fd, void *user_data); +typedef int (*on_event_cb) (event_ctx *ev_ctx, int fd, void *user_data); void event_init(event_ctx **ctx); +void event_free(event_ctx **ctx); + int event_setup(event_ctx *ctx); int event_add_sock(event_ctx *ctx, psocket *sock); @@ -27,4 +35,7 @@ int event_add_fd(event_ctx *ctx, int fd); int event_loop(event_ctx *ctx, on_event_cb on_event, void *user_data); +forward_state +event_forward_connection(event_ctx *ctx, int dest_fd); + #endif diff --git a/src/server.c b/src/server.c index 4dd3a94..e0b8738 100644 --- a/src/server.c +++ b/src/server.c @@ -13,28 +13,30 @@ typedef struct client_thread_args { pthread_t self; - psocket client_psock; + psocket client_sock; char host_buf[NI_MAXHOST], service_buf[NI_MAXSERV]; const server_ctx *server_ctx; } client_thread_args; -typedef struct server_args { - server_ctx **srv_ctx; - size_t siz; -} server_args; +typedef struct server_event { + const server_ctx **srv_ctx; + const size_t siz; +} server_event; -typedef enum connection_state { - CON_OK, CON_IN_TERMINATED, CON_OUT_TERMINATED, - CON_IN_ERROR, CON_OUT_ERROR -} connection_state; +typedef struct client_event { + const psocket *fwd_sock; + const client_thread_args *client_args; +} client_event; +static forward_state +fwd_state_string(const forward_state c_state, const client_thread_args *args, + const psocket *fwd); static int -server_mainloop(event_ctx *ev_ctx, server_ctx *srv_ctx[], size_t siz); -static int server_accept_client(int fd, void *user_data); +server_mainloop(event_ctx *ev_ctx, const server_ctx *srv_ctx[], size_t siz); +static int server_accept_client(event_ctx *ev_ctx, int fd, void *user_data); static void * -client_mainloop_epoll(void *arg); -static connection_state -client_io_epoll(struct epoll_event *ev, int dest_fd); +client_mainloop(void *arg); +static int client_io(event_ctx *ev_ctx, int src_fd, void *user_data); void server_init_ctx(server_ctx **ctx, forward_ctx *fwd_ctx) @@ -92,6 +94,7 @@ int server_setup_event(server_ctx *srv_ctx[], size_t siz, event_ctx **ev_ctx) assert(srv_ctx && ev_ctx); assert(siz > 0 && siz < POTD_MAXFD); + event_init(ev_ctx); if (event_setup(*ev_ctx)) return 1; @@ -141,7 +144,7 @@ pid_t server_daemonize(event_ctx *ev_ctx, server_ctx *srv_ctx[], size_t siz) return -1; case 0: N("%s", "Server daemon mainloop"); - server_mainloop(ev_ctx, srv_ctx, siz); + server_mainloop(ev_ctx, (const server_ctx **) srv_ctx, siz); break; } D2("Server daemon pid: %d", p); @@ -149,34 +152,73 @@ pid_t server_daemonize(event_ctx *ev_ctx, server_ctx *srv_ctx[], size_t siz) return p; } -static int server_mainloop(event_ctx *ev_ctx, server_ctx *srv_ctx[], size_t siz) +static forward_state +fwd_state_string(const forward_state c_state, const client_thread_args *args, + const psocket *fwd) { - server_args srv_args = { srv_ctx, siz }; + switch (c_state) { + case CON_OK: + break; + case CON_IN_ERROR: + N("Lost connection to %s:%s: %d", + args->host_buf, args->service_buf, + args->client_sock.fd); + break; + case CON_IN_TERMINATED: + N("Connection terminated: %s:%s: %d", + args->host_buf, args->service_buf, + args->client_sock.fd); + break; + case CON_OUT_ERROR: + N("Lost forward connection to %s:%s: %d", + args->server_ctx->fwd_ctx->host_buf, + args->server_ctx->fwd_ctx->service_buf, + fwd->fd); + break; + case CON_OUT_TERMINATED: + N("Forward connection terminated: %s:%s: %d", + args->server_ctx->fwd_ctx->host_buf, + args->server_ctx->fwd_ctx->service_buf, + fwd->fd); + break; + } + + return c_state; +} + +static int server_mainloop(event_ctx *ev_ctx, const server_ctx *srv_ctx[], size_t siz) +{ + int rc; + server_event ev_srv = { srv_ctx, siz }; set_procname("[potd] server"); assert( set_child_sighandler() == 0 ); - return event_loop(ev_ctx, server_accept_client, &srv_args); + rc = event_loop(ev_ctx, server_accept_client, &ev_srv); + event_free(&ev_ctx); + + return rc; } -static int server_accept_client(int fd, void *user_data) +static int server_accept_client(event_ctx *ev_ctx, int fd, void *user_data) { size_t i; int s; - server_args *srv_args = (server_args *) user_data; + server_event *ev_srv = (server_event *) user_data; client_thread_args *args; - server_ctx *srv_ctx; + const server_ctx *srv_ctx; - assert(srv_args); + (void) ev_ctx; + assert(ev_srv); - for (i = 0; i < srv_args->siz; ++i) { - srv_ctx = srv_args->srv_ctx[i]; + for (i = 0; i < ev_srv->siz; ++i) { + srv_ctx = ev_srv->srv_ctx[i]; if (srv_ctx->sock.fd == fd) { args = (client_thread_args *) calloc(1, sizeof(*args)); assert(args); if (socket_accept_in(&srv_ctx->sock, - &args->client_psock)) + &args->client_sock)) { E_STRERR("Could not accept client connection on fd %d", srv_ctx->sock.fd); @@ -184,7 +226,7 @@ static int server_accept_client(int fd, void *user_data) } args->server_ctx = srv_ctx; - s = socket_addrtostr_in(&args->client_psock, + s = socket_addrtostr_in(&args->client_sock, args->host_buf, args->service_buf); if (s) { E_GAIERR(s, "Convert socket address to string"); @@ -193,20 +235,20 @@ static int server_accept_client(int fd, void *user_data) N2("New connection from %s:%s to %s:%s: %d", args->host_buf, args->service_buf, srv_ctx->host_buf, srv_ctx->service_buf, - args->client_psock.fd); + args->client_sock.fd); if (pthread_create(&args->self, NULL, - client_mainloop_epoll, args)) + client_mainloop, args)) { E_STRERR("Thread creation for %s:%s on fd %d", args->host_buf, args->service_buf, - args->client_psock.fd); + args->client_sock.fd); goto error; } return 1; error: - socket_close(&args->client_psock); + socket_close(&args->client_sock); free(args); return 0; } @@ -216,25 +258,21 @@ error: } static void * -client_mainloop_epoll(void *arg) +client_mainloop(void *arg) { client_thread_args *args; - int s, epoll_fd, dest_fd, active = 1; - struct epoll_event event = {0,{0}}; - struct epoll_event *events; - sigset_t eset; - connection_state cs; + client_event ev_cli; + int s; + event_ctx *ev_ctx = NULL; psocket fwd; assert(arg); args = (client_thread_args *) arg; pthread_detach(args->self); - events = (struct epoll_event *) calloc(POTD_MAXEVENTS, sizeof(*events)); - assert(events); - epoll_fd = epoll_create1(0); - if (epoll_fd < 0) { - E_STRERR("Client Epoll descriptor creation for server fd %d", + event_init(&ev_ctx); + if (event_setup(ev_ctx)) { + E_STRERR("Client event descriptor creation for server fd %d", args->server_ctx->sock.fd); goto finish; } @@ -250,11 +288,8 @@ client_mainloop_epoll(void *arg) args->server_ctx->fwd_ctx->host_buf, args->server_ctx->fwd_ctx->service_buf, fwd.fd); - event.data.fd = fwd.fd; - event.events = EPOLLIN | EPOLLET; - s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fwd.fd, &event); - if (s) { - E_STRERR("Forward Epoll descriptor add to %s:%s forward fd %d", + if (event_add_sock(ev_ctx, &fwd)) { + E_STRERR("Forward event descriptor add to %s:%s forward fd %d", args->server_ctx->fwd_ctx->host_buf, args->server_ctx->fwd_ctx->service_buf, fwd.fd); goto finish; @@ -264,160 +299,61 @@ client_mainloop_epoll(void *arg) * We got the client socket from our main thread, so fd flags like * O_NONBLOCK are not inherited! */ - s = socket_nonblock(&args->client_psock); + s = socket_nonblock(&args->client_sock); if (s) { E_STRERR("Socket non blocking mode to %s:%s forward fd %d", args->server_ctx->fwd_ctx->host_buf, args->server_ctx->fwd_ctx->service_buf, fwd.fd); goto finish; } - event.data.fd = args->client_psock.fd; - event.events = EPOLLIN | EPOLLET; - - s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->client_psock.fd, &event); - if (s) { - E_STRERR("Forward Epoll descriptor add to %s:%s forward fd %d", + if (event_add_sock(ev_ctx, &args->client_sock)) { + E_STRERR("Forward event descriptor add to %s:%s forward fd %d", args->server_ctx->fwd_ctx->host_buf, args->server_ctx->fwd_ctx->service_buf, fwd.fd); goto finish; } - sigemptyset(&eset); - - while (active) { - int n, i; - - n = epoll_pwait(epoll_fd, events, POTD_MAXEVENTS, -1, &eset); - if (n < 0) - break; - - for (i = 0; i < n; ++i) { - if ((events[i].events & EPOLLERR) || - (events[i].events & EPOLLHUP) || - (!(events[i].events & EPOLLIN))) - { - E_STRERR("Epoll for descriptor %d", events[i].data.fd); - active = 0; - break; - } else { - if (events[i].data.fd == fwd.fd) { - dest_fd = args->client_psock.fd; - } else if (events[i].data.fd == args->client_psock.fd) { - dest_fd = fwd.fd; - } else continue; - - cs = client_io_epoll(&events[i], dest_fd); - if (cs == CON_OK) - continue; - - switch (cs) { - case CON_OK: - break; - case CON_IN_ERROR: - N("Lost connection to %s:%s: %d", - args->host_buf, args->service_buf, - args->client_psock.fd); - active = 0; - break; - case CON_IN_TERMINATED: - N("Connection terminated: %s:%s: %d", - args->host_buf, args->service_buf, - args->client_psock.fd); - active = 0; - break; - case CON_OUT_ERROR: - N("Lost forward connection to %s:%s: %d", - args->server_ctx->fwd_ctx->host_buf, - args->server_ctx->fwd_ctx->service_buf, - fwd.fd); - active = 0; - break; - case CON_OUT_TERMINATED: - N("Forward connection terminated: %s:%s: %d", - args->server_ctx->fwd_ctx->host_buf, - args->server_ctx->fwd_ctx->service_buf, - fwd.fd); - active = 0; - break; - } - if (!active) - break; - } - W2("I/O forwarder failed: [fd: %d , npoll: %d]", events[i].data.fd, n); - } - } + ev_cli.client_args = args; + ev_cli.fwd_sock = &fwd; + event_loop(ev_ctx, client_io, &ev_cli); finish: - close(epoll_fd); + event_free(&ev_ctx); socket_close(&fwd); - socket_close(&args->client_psock); - free(events); + socket_close(&args->client_sock); free(args); return NULL; } -static connection_state -client_io_epoll(struct epoll_event *ev, int dest_fd) +static int +client_io(event_ctx *ev_ctx, int src_fd, void *user_data) { - int data_avail = 1; - int has_input; - int saved_errno; - connection_state rc = CON_OK; - ssize_t siz; - char buf[BUFSIZ+sizeof(long)]; - - while (data_avail) { - has_input = 0; - saved_errno = 0; - siz = -1; - - if (ev->events & EPOLLIN) { - has_input = 1; - errno = 0; - siz = read(ev->data.fd, &buf[0], BUFSIZ); - saved_errno = errno; - } else break; - if (saved_errno == EAGAIN) - break; - - switch (siz) { - case -1: - E_STRERR("Client read from fd %d", ev->data.fd); - rc = CON_IN_ERROR; - break; - case 0: - rc = CON_IN_TERMINATED; - break; - default: - buf[siz] = 0; - D2("Read %lu bytes from fd %d", siz, ev->data.fd); - break; - } - - if (rc != CON_OK) - break; - - if (has_input) { - siz = write(dest_fd, &buf[0], siz); - - switch (siz) { - case -1: - rc = CON_OUT_ERROR; - break; - case 0: - rc = CON_OUT_TERMINATED; - break; - default: - D2("Written %lu bytes from fd %d to fd %d", - siz, ev->data.fd, dest_fd); - break; - } - } - - if (rc != CON_OK) - break; + int dest_fd; + client_event *ev_cli = (client_event *) user_data; + const psocket *client_sock = &ev_cli->client_args->client_sock; + forward_state fwd_state; + + if (src_fd == ev_cli->fwd_sock->fd) { + dest_fd = client_sock->fd; + } else if (src_fd == client_sock->fd) { + dest_fd = ev_cli->fwd_sock->fd; + } else return 0; + + fwd_state = event_forward_connection(ev_ctx, dest_fd); + + switch (fwd_state_string(fwd_state, ev_cli->client_args, + ev_cli->fwd_sock)) + { + case CON_IN_TERMINATED: + case CON_OUT_TERMINATED: + ev_ctx->active = 0; + case CON_OK: + return 1; + case CON_IN_ERROR: + case CON_OUT_ERROR: + ev_ctx->active = 0; + return 0; } - D2("Connection state: %d", rc); - return rc; + return 1; } |