#include #include #include #include #include #include #include #include #include #include "glougloud.h" #include "imsgev.h" #define USER_TIMEOUT 300 #define USERTIMER 10 struct server { struct imsgev iev; struct event ev; struct event usrtimer_ev; struct timeval usrtimer_tv; time_t time; }; enum user_status { USER_REQUESTED, USER_ACCEPTED, USER_REFUSED }; struct user { LIST_ENTRY(user) entry; struct sockaddr_in addr; enum user_status status; time_t lastseen; }; static void imsgev_main(struct imsgev *, int, struct imsg *); static void receive(int, short, void *); static void ev_usrtimer(int, short, void *); struct server *srv; LIST_HEAD(, user) usr_list; static void sig_handler(int sig, short why, void *data) { log_info("server: got signal %d", sig); if (sig == SIGINT || sig == SIGTERM) event_loopexit(NULL); } int server_init(int fd[2]) { struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; int pid; pid = fork(); if (pid < 0) fatal("server fork"); if (pid > 0) return pid; setproctitle("server"); event_init(); close(fd[0]); signal_set(&ev_sigint, SIGINT, sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL); signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL); signal_set(&ev_sighup, SIGHUP, sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_sigchld, NULL); signal_add(&ev_sighup, NULL); signal(SIGPIPE, SIG_IGN); srv = xmalloc(sizeof(struct server)); imsgev_init(&srv->iev, fd[1], NULL, &imsgev_main); srv->time = time(NULL); event_set(&srv->ev, net_socket, EV_READ|EV_PERSIST, receive, NULL); event_add(&srv->ev, NULL); srv->usrtimer_tv.tv_sec = USERTIMER; srv->usrtimer_tv.tv_usec = 0; evtimer_set(&srv->usrtimer_ev, ev_usrtimer, NULL); if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1) fatal("server: event_add usrtimr failed: %s", strerror(errno)); droppriv(); log_info("server: entering event loop"); event_dispatch(); log_info("server: exiting"); exit(0); } static struct user * finduser(struct sockaddr_in *remote) { struct user *usr; struct sockaddr_in *u; LIST_FOREACH(usr, &usr_list, entry) { u = &usr->addr; if (u->sin_addr.s_addr == remote->sin_addr.s_addr && u->sin_port == remote->sin_port) return usr; } return NULL; } static void srvconn(struct imsgev *iev, struct imsg *imsg) { struct imsg_srvconn *res; struct user *usr; res = imsg->data; usr = finduser(&res->addr); if (!usr || usr->status != USER_REQUESTED) fatal("server: received srvconn response for inexistant connection"); if (res->ok) { usr->status = USER_ACCEPTED; } else { usr->status = USER_REFUSED; } } static void imsgev_main(struct imsgev *iev, int code, struct imsg *imsg) { switch (code) { case IMSGEV_IMSG: log_debug("server: %s got imsg %i on fd %i", __func__, imsg->hdr.type, iev->ibuf.fd); switch (imsg->hdr.type) { case IMSG_SRVCONN_RES: srvconn(iev, imsg); break; default: fatal("%s, unexpected imsg %d", __func__, imsg->hdr.type); } break; case IMSGEV_EREAD: case IMSGEV_EWRITE: case IMSGEV_EIMSG: fatal("imsgev read/write error"); /* NOTREACHED */ break; case IMSGEV_DONE: event_loopexit(NULL); /* NOTREACHED */ break; } } static void receive(int fd, short why, void *data) { struct sockaddr_in remote; struct imsg_srvconn req; socklen_t len; char buf[16384]; struct user *usr; int rv; len = sizeof(remote); rv = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&remote, &len); if (rv < 0) { log_warn("server: recvfrom failed"); return; } usr = finduser(&remote); if (usr) { switch (usr->status) { case USER_ACCEPTED: log_tmp("server: data for existing user"); break; case USER_REQUESTED: log_warn("server: data for not yet accepted user"); break; case USER_REFUSED: log_warn("server: data for banned user !"); break; } } else { log_debug("server: new user request"); usr = xmalloc(sizeof(struct user)); addrcpy(&usr->addr, &remote); usr->status = USER_REQUESTED; usr->lastseen = srv->time; LIST_INSERT_HEAD(&usr_list, usr, entry); addrcpy(&req.addr, &remote); req.deco = 0; imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1, &req, sizeof(req)); } } static void ev_usrtimer(int fd, short why, void *data) { struct user *usr; struct imsg_srvconn req; srv->time = time(NULL); LIST_FOREACH(usr, &usr_list, entry) { if (srv->time > usr->lastseen + USER_TIMEOUT) { addrcpy(&req.addr, &usr->addr); req.deco = 1; imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1, &req, sizeof(req)); LIST_REMOVE(usr, entry); free(usr); } } if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1) fatal("server: event_add usrtimer failed : %s", strerror(errno)); }