From 893c216b889f260378fb21a7f576c061f7ff2248 Mon Sep 17 00:00:00 2001 From: mikeos Date: Sat, 13 Oct 2012 23:20:14 +0400 Subject: initial --- Makefile | 18 ++ buffer.c | 78 +++++++ buffer.h | 20 ++ data.h | 6 + debug.h | 49 ++++ list.h | 248 +++++++++++++++++++++ main.c | 756 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tcpstat.c | 41 ++++ xwrap.c | 23 ++ xwrap.h | 17 ++ 10 files changed, 1256 insertions(+) create mode 100644 Makefile create mode 100644 buffer.c create mode 100644 buffer.h create mode 100644 data.h create mode 100644 debug.h create mode 100644 list.h create mode 100644 main.c create mode 100644 tcpstat.c create mode 100644 xwrap.c create mode 100644 xwrap.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b7738ce --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +PROG = $(shell pwd | xargs basename) +OBJECTS = main.o buffer.o xwrap.o +CFLAGS = -O2 -Wall -D_GNU_SOURCE -DNDEBUG -DNOCOLOR -DPROGNAME=\"$(PROG)\" + +all: $(PROG) tcpstat + +$(PROG): $(OBJECTS) + gcc -s -o $@ $(OBJECTS) + +tcpstat: tcpstat.o + gcc -s -o $@ $< + +%.o: %.c + gcc $(CFLAGS) -c -o $@ $< + +clean: + @echo cleaning... + @rm -f *.o *~ core.* diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..167db65 --- /dev/null +++ b/buffer.c @@ -0,0 +1,78 @@ +#include "buffer.h" +#include "xwrap.h" + +#include +#include + +void binit(struct buffer *b, size_t size) +{ + char *p; + + p = (char *) xmalloc(size); + b->addr = b->begin = b->end = p; + b->size = size; +} + +void bfree(struct buffer *b) +{ + if (b->addr != NULL) { + free(b->addr); + b->addr = b->begin = b->end = NULL; + b->size = 0; + } +} + +static inline int bavail_write(struct buffer *b) +{ + return b->end - b->begin; +} + +static inline int bavail_read(struct buffer *b) +{ + return b->addr + b->size - b->end; +} + +int bread(struct buffer *b, int fd) +{ + int size; + int n; + + size = bavail_read(b); + if (size == 0) + return 0; + + n = read(fd, b->end, size); + if (n < 1) + return -1; + b->end += n; + return n; +} + +int bwrite(struct buffer *b, int fd) +{ + int size; + int n; + + size = bavail_write(b); + if (size == 0) + return 0; + + n = write(fd, b->begin, size); + if (n < 1) + return -1; + b->begin += n; + if (b->begin == b->end) + b->begin = b->end = b->addr; + return n; +} + +int bcanfill(struct buffer *b) +{ + return bavail_read(b) > 0; +} + +int bhasdata(struct buffer *b) +{ + return bavail_write(b) > 0; +} + diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..7bc9d76 --- /dev/null +++ b/buffer.h @@ -0,0 +1,20 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include + +struct buffer { + char *addr; + char *begin; + char *end; + int size; +}; + +void binit(struct buffer *b, size_t size); +void bfree(struct buffer *b); +int bread(struct buffer *b, int fd); +int bwrite(struct buffer *b, int fd); +int bcanfill(struct buffer *b); +int bhasdata(struct buffer *b); + +#endif diff --git a/data.h b/data.h new file mode 100644 index 0000000..b0ecd2d --- /dev/null +++ b/data.h @@ -0,0 +1,6 @@ +#ifndef DATA_H +#define DATA_H + +#define UNIX_SOCKET_PATH "/tmp/tcpproxy.unix" + +#endif diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..540f200 --- /dev/null +++ b/debug.h @@ -0,0 +1,49 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +#include +#include + +#ifndef NOCOLOR +#define GREEN "" +#define DEF "" +#define RED "" +#else +#define GREEN +#define DEF +#define RED +#endif + +#ifndef NDEBUG +#define DEBUG_CODE(code) \ +do { \ + code; \ +} while (0) +#else +#define DEBUG_CODE(code) +#endif + +#define PROMPT_FMT(fmt) GREEN "%s" DEF ":" GREEN "%d" DEF ": " fmt + +#define msg_err(fmt, args...) \ + fprintf(stderr, PROMPT_FMT(fmt) ": " \ + RED "%s" DEF "\n", \ + __FILE__, __LINE__, ## args, strerror(errno)) + +#define msg_warn(fmt, args...) \ + fprintf(stderr, PROMPT_FMT(fmt) "\n", \ + __FILE__, __LINE__, ## args) + +#define sys_err(fmt, args...) \ +do { \ + msg_err(fmt, ## args); \ + exit(1); \ +} while (0) + +#define err_quit(fmt, args...) \ +do { \ + msg_warn(fmt, ## args); \ + exit(1); \ +} while (0) + +#endif diff --git a/list.h b/list.h new file mode 100644 index 0000000..25b20d8 --- /dev/null +++ b/list.h @@ -0,0 +1,248 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (void *) 0; + entry->prev = (void *) 0; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..c3d1e0e --- /dev/null +++ b/main.c @@ -0,0 +1,756 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "debug.h" +#include "xwrap.h" +#include "list.h" +#include "data.h" + +#define NELEM(p) (sizeof(p) / sizeof(p[0])) +#define BUFSIZE 0x1000 + +static struct list_head channels; +static struct list_head servers; +static int die_childs; +static pid_t main_pid; + +struct server { + int sock; + char *remote_addr; + struct sockaddr_in remote_sa; + int nchannel; + struct list_head serv_list; +}; + +struct channel { + struct buffer buf1; + struct buffer buf2; + int fd1; /* client socket */ + int fd2; /* remote socket */ + int connected; + char *addr; + struct server *server; + struct list_head chan_list; +}; + +static inline int max(int a, int b) +{ + return a > b ? a : b; +} + +static inline int tcp_socket(void); +static int tcp_server(struct sockaddr *sa, socklen_t addrlen); +static inline int unix_socket(void); +static int unix_server(const char *name); +static void set_nonblock(int desc); +static inline void shut(int *fd); + +static void usage(void); +static void sighandler(int signum); +static void init_handlers(void); +static void wait_childs(void); +static int isnumber(const char *str); +static void on_quit(void); +static void quit(void); + +static void makeaddr(struct sockaddr_in *sa, const char *host, const char *port); +static int portbyname(const char *name); +static char *addrstr(struct sockaddr_in *sa); + +static struct channel *chan_new(struct server *s, char *addr, int fd1, int fd2); +static void chan_free(struct channel *chan); + +static void serv_add_new(char *local_port, char *host, char *port); + +static void endless_loop(int unix_sock); + +static void read_config_file(char *name) +{ + static char *delim = " \r\t\n"; + + char *line = NULL; + size_t len = 0; + int nline = 1; + FILE *fp; + + if ((fp = fopen(name, "r")) == NULL) + sys_err("fopen %s", name); + + while (getline(&line, &len, fp) >= 0) { + char *local, *host, *port; + + local = host = port = NULL; + + if ((local = strtok(line, delim)) != NULL) { + if ((host = strtok(NULL, delim)) != NULL) + port = strtok(NULL, delim); + } + + if (port == NULL) + err_quit("%s: invalid line %d", name, nline); + + serv_add_new(local, host, port); + nline++; + } + + fclose(fp); + free(line); +} + +int main(int argc, char *argv[]) +{ + int unix_sock; + + if (argc < 2) + usage(); + + unix_sock = unix_server(UNIX_SOCKET_PATH); + + main_pid = getpid(); + atexit(on_quit); + + INIT_LIST_HEAD(&servers); + + setservent(1); + read_config_file(argv[1]); + endservent(); + + INIT_LIST_HEAD(&channels); + init_handlers(); + endless_loop(unix_sock); + + return 0; +} + +static inline void xsigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ + int ret; + + ret = sigprocmask(how, set, oldset); + if (ret < 0) + sys_err("sigprocmask"); +} + +static inline void xsigpending(sigset_t *set) +{ + if (sigpending(set) < 0) + sys_err("sigpending"); +} + +static void init_handlers(void) +{ + struct sigaction act; + int sigs[] = { SIGINT, SIGTERM }; + int n; + + memset(&act, 0, sizeof(act)); + act.sa_handler = sighandler; + act.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &act, NULL) < 0) + sys_err("sigaction"); + if (siginterrupt(SIGCHLD, 1) < 0) + sys_err("siginterrupt"); + + for (n = 0; n < NELEM(sigs); n++) + if (sigaction(sigs[n], &act, NULL) < 0) + sys_err("sigaction"); +} + +static void sighandler(int signum) +{ + if (signum == SIGCHLD) + die_childs++; + else + quit(); +} + +static void wait_childs(void) +{ + int status; + pid_t pid; + + for (;;) { + pid = waitpid(-1, &status, WNOHANG); + if (pid == 0) + goto out; + else if (pid == -1) + switch (errno) { + case ECHILD: + goto out; + case EINTR: + continue; + default: + sys_err("waitpid"); + } + + DEBUG_CODE( + fprintf(stderr, "child %d ", pid); + if (WIFEXITED(status)) + fprintf(stderr, "exited with status %d\n", + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + fprintf(stderr, "caught signal %d\n", + WTERMSIG(status)); + else + fprintf(stderr, "died by unknown reason\n"); + ); + } + +out: + die_childs = 0; +} + +static struct channel *chan_new(struct server *s, char *addr, int fd1, int fd2) +{ + struct channel *p; + + p = (struct channel *) xmalloc(sizeof(*p)); + binit(&p->buf1, BUFSIZE); + binit(&p->buf2, BUFSIZE); + p->fd1 = fd1; + p->fd2 = fd2; + p->addr = addr; + p->connected = 0; + p->server = s; + INIT_LIST_HEAD(&p->chan_list); + list_add(&p->chan_list, &channels); + + s->nchannel++; + + return p; +} + +static void chan_free(struct channel *chan) +{ + struct server *s = chan->server; + + bfree(&chan->buf1); + bfree(&chan->buf2); + + if (chan->fd1 >= 0) + shut(&chan->fd1); + if (chan->fd2 >= 0) + shut(&chan->fd2); + + free(chan->addr); + list_del(&chan->chan_list); + free(chan); + + s->nchannel--; +} + +static void serv_add_new(char *local_port, char *host, char *port) +{ + struct sockaddr_in sa; + struct server *s; + + s = (struct server *) xmalloc(sizeof(*s)); + s->nchannel = 0; + + makeaddr(&s->remote_sa, host, port); + s->remote_addr = addrstr(&s->remote_sa); + + memset(&sa, 0, sizeof(sa)); + makeaddr(&sa, "0.0.0.0", local_port); + s->sock = tcp_server((struct sockaddr *) &sa, sizeof(sa)); + + INIT_LIST_HEAD(&s->serv_list); + list_add(&s->serv_list, &servers); +} + +static inline int tcp_socket(void) +{ + int sock; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) + sys_err("socket"); + set_nonblock(sock); + + return sock; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s config_file\n", PROGNAME); + exit(1); +} + +static void on_quit(void) +{ + if (main_pid == getpid()) { + if (UNIX_SOCKET_PATH[0] != '\0' && unlink(UNIX_SOCKET_PATH) < 0) + sys_err("unlink"); + } +} + +static void quit(void) +{ + on_quit(); + exit(0); +} + +static int isnumber(const char *str) +{ + const char *p; + + for (p = str; *p != '\0'; p++) + if (isdigit(*p) == 0) + return 0; + + return 1; +} + +static int portbyname(const char *name) +{ + struct servent *se; + + if (isnumber(name)) + return htons(atoi(name)); + + se = getservbyname(name, "tcp"); + if (se == NULL) + err_quit("unknown service %s", name); + + return se->s_port; +} + +static void makeaddr(struct sockaddr_in *sa, const char *host, const char *port) +{ + memset(sa, 0, sizeof(*sa)); + if (inet_aton(host, &sa->sin_addr) == 0) + err_quit("inet_aton %s failed", host); + + sa->sin_family = AF_INET; + sa->sin_port = portbyname(port); +} + +static char *addrstr(struct sockaddr_in *sa) +{ + char buf[512]; + + snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(sa->sin_addr), + ntohs(sa->sin_port)); + return xstrdup(buf); +} + +static inline void shut(int *fd) +{ + shutdown(*fd, SHUT_RDWR); + close(*fd); + *fd = -1; +} + +static int tcp_server(struct sockaddr *sa, socklen_t addrlen) +{ + int flag; + int sock; + + sock = tcp_socket(); + flag = 1; + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) + sys_err("setsockopt SO_REUSEADDR failed"); + if (bind(sock, sa, addrlen) < 0) + sys_err("bind"); + if (listen(sock, 7) < 0) + sys_err("listen"); + + return sock; +} + +static inline int unix_socket(void) +{ + int sock; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + sys_err("socket PF_UNIX"); + + return sock; +} + +static int unix_server(const char *name) +{ + struct sockaddr_un sa; + int sock; + + sock = unix_socket(); + memset(&sa, 0, sizeof(sa)); + sa.sun_family = PF_UNIX; + strcpy(sa.sun_path, name); + if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) + sys_err("unix_server: bind failed"); + if (listen(sock, 7) < 0) + sys_err("unix_server: listen failed"); + + return sock; +} + +static void set_nonblock(int desc) +{ + int opts; + + opts = fcntl(desc, F_GETFL); + if (opts < 0) + sys_err("fcntl"); + opts |= O_NONBLOCK; + if (fcntl(desc, F_SETFL, opts) < 0) + sys_err("fcntl"); +} + +static int add_chans(fd_set *readfds, fd_set *writefds, fd_set *exceptfds) +{ + struct channel *chan; + struct buffer *b1, *b2; + int fd1, fd2; + int nfds = -1; + + list_for_each_entry(chan, &channels, chan_list) { + b1 = &chan->buf1; + b2 = &chan->buf2; + fd1 = chan->fd1; + fd2 = chan->fd2; + + if (chan->connected == 0) { + FD_SET(fd2, writefds); + nfds = max(nfds, fd2); + continue; + } + + if (fd1 >= 0) { + if (bcanfill(b1)) + FD_SET(fd1, readfds); + if (bhasdata(b2)) + FD_SET(fd1, writefds); + + FD_SET(fd1, exceptfds); + nfds = max(nfds, fd1); + } + + if (fd2 >= 0) { + if (bcanfill(b2)) + FD_SET(fd2, readfds); + if (bhasdata(b1)) + FD_SET(fd2, writefds); + + FD_SET(fd2, exceptfds); + nfds = max(nfds, fd2); + } + } + + return nfds; +} + +static int add_servs(fd_set *readfds) +{ + struct server *server; + int nfds = -1; + + list_for_each_entry(server, &servers, serv_list) { + int fd = server->sock; + + FD_SET(fd, readfds); + nfds = max(nfds, fd); + } + + return nfds; +} + +static inline int connected(int sock) +{ + int optval; + socklen_t optlen; + int ret; + + optlen = sizeof(optval); + ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); + if (ret < 0) + sys_err("getsockopt"); + if (optval == 0) + return 0; + + errno = optval; + return -1; +} + +static void test_chans(fd_set *readfds, fd_set *writefds, fd_set *exceptfds) +{ + struct channel *chan, *tmp; + struct buffer *b1, *b2; + int fd1, fd2; + int n; + + list_for_each_entry_safe(chan, tmp, &channels, chan_list) { + b1 = &chan->buf1; + b2 = &chan->buf2; + fd1 = chan->fd1; + fd2 = chan->fd2; + + if (chan->connected == 0) { + if (FD_ISSET(fd2, writefds) == 0) + ; + else if (connected(fd2) < 0) { + msg_err("connect"); + chan_free(chan); + } else + chan->connected = 1; + + continue; + } + + if (fd1 >= 0 && FD_ISSET(fd1, exceptfds)) { + char ch; + + ch = recv(fd1, &ch, 1, MSG_OOB); + if (ch < 0) + shut(&fd1); + else + send(fd1, &ch, 1, MSG_OOB); + } + + if (fd1 >= 0 && FD_ISSET(fd1, readfds)) { + if ((n = bread(b1, fd1)) < 0) + shut(&fd1); + else + DEBUG_CODE( + msg_warn("read from %s %d bytes", + chan->addr, n); + ); + } + + if (fd1 >= 0 && FD_ISSET(fd1, writefds)) { + if ((n = bwrite(b2, fd1)) < 0) + shut(&fd2); + else + DEBUG_CODE( + msg_warn("write to %s %d bytes", + chan->addr, n); + ); + } + + if (fd2 >= 0 && FD_ISSET(fd2, exceptfds)) { + char ch; + + ch = recv(fd2, &ch, 1, MSG_OOB); + if (ch < 1) + shut(&fd2); + else + send(fd2, &ch, 1, MSG_OOB); + } + + if (fd2 >= 0 && FD_ISSET(fd2, readfds)) { + if ((n = bread(b2, fd2)) < 0) + shut(&fd2); + else + DEBUG_CODE( + msg_warn("read from %s %d bytes", + remote_addr, n); + ); + } + + if (fd2 >= 0 && FD_ISSET(fd2, writefds)) { + if (bwrite(b1, fd2) < 0) + shut(&fd2); + else + DEBUG_CODE( + msg_warn("write to %s %d bytes", + remote_addr, n); + ); + } + + if (fd1 >= 0 && fd2 < 0 && bhasdata(b2) == 0) + shut(&fd1); + if (fd2 >= 0 && fd1 < 0 && bhasdata(b1) == 0) + shut(&fd2); + + chan->fd1 = fd1; + chan->fd2 = fd2; + + if (fd1 < 0 && fd2 < 0) + chan_free(chan); + } +} + +static void accept_client(struct server *server, int serv_sock) +{ + struct channel *chan; + struct sockaddr_in sa; + socklen_t salen; + int fd1, fd2; + int ret; + + salen = sizeof(sa); + fd1 = accept(serv_sock, (struct sockaddr *) &sa, &salen); + if (fd1 < 0) { + if (errno != EAGAIN) + msg_err("accept"); + return; + } + + fd2 = tcp_socket(); + chan = chan_new(server, addrstr(&sa), fd1, fd2); + ret = connect(fd2, (struct sockaddr *) &server->remote_sa, sizeof(struct sockaddr_in)); + if (ret == 0) + chan->connected++; + else if (errno != EINPROGRESS) + sys_err("connect"); + + fprintf(stderr, "accept: %s to %s\n", chan->addr, server->remote_addr); +} + +static int test_servs(fd_set *readfds) +{ + struct server *server; + int count = 0; + + list_for_each_entry(server, &servers, serv_list) { + int fd = server->sock; + + if (FD_ISSET(fd, readfds)) { + accept_client(server, fd); + count++; + } + } + + return count; +} + +static int store_info(char **ptr) +{ + struct channel *chan; + FILE *stream; + size_t size = 0; + int total = 0; + + stream = open_memstream(ptr, &size); + if (stream == NULL) + sys_err("open_memstream"); + list_for_each_entry(chan, &channels, chan_list) { + fprintf(stream, "%s => %s", chan->addr, chan->server->remote_addr); + if (chan->connected == 0) + fprintf(stream, ": not connected"); + fprintf(stream, "\n"); + total++; + } + + fprintf(stream, "total: %d\n", total); + fclose(stream); + + return size; +} + +void send_info(int sock) +{ + char *buf = NULL; + char *p; + size_t size; + int n; + + size = store_info(&buf); + p = buf; + while (size > 0) { + n = write(sock, p, size); + if (n < 0) + sys_err("write"); + size -= n; + p += n; + } + + free(buf); +} + +static void accept_unix(int unix_sock) +{ + pid_t pid; + int sock; + + sock = accept(unix_sock, NULL, NULL); + if (sock < 0) { + if (errno != EAGAIN) + msg_err("accept"); + return; + } + + pid = fork(); + if (pid < 0) { + msg_err("fork"); + return; + } + + if (pid > 0) { + close(sock); + return; + } + + send_info(sock); + close(sock); + exit(0); +} + +static void endless_loop(int unix_sock) +{ + fd_set rd, wr, er; + sigset_t set, pend_set; + int nfds; + int ret; + + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + + for (;;) { + if (die_childs) + wait_childs(); + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&er); + + FD_SET(unix_sock, &rd); + + nfds = max(add_servs(&rd), unix_sock); + nfds = max(add_chans(&rd, &wr, &er), nfds); + + ret = select(nfds + 1, &rd, &wr, &er, NULL); + if (ret < 0) { + if (errno == EINTR) + continue; + sys_err("select"); + } + + xsigprocmask(SIG_BLOCK, &set, NULL); + ret -= test_servs(&rd); + + if (ret > 0 && FD_ISSET(unix_sock, &rd)) { + accept_unix(unix_sock); + ret--; + } + + if (ret > 0) + test_chans(&rd, &wr, &er); + + xsigpending(&pend_set); + if (sigismember(&pend_set, SIGCHLD)) + wait_childs(); + + xsigprocmask(SIG_UNBLOCK, &set, NULL); + } +} + diff --git a/tcpstat.c b/tcpstat.c new file mode 100644 index 0000000..b76877f --- /dev/null +++ b/tcpstat.c @@ -0,0 +1,41 @@ +#include +#include + +#include +#include +#include +#include + +#include "debug.h" +#include "data.h" + +static char buf[0x1000]; + +int main(int argc, char *argv[]) +{ + struct sockaddr_un sa; + int sock; + int n; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + sys_err("socket"); + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, UNIX_SOCKET_PATH); + if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) + sys_err("connect"); + + while ((n = read(sock, buf, sizeof(buf))) > 0) { + buf[n] = 0; + fputs(buf, stdout); + } + + if (n < 0) + sys_err("read"); + + close(sock); + return 0; +} + diff --git a/xwrap.c b/xwrap.c new file mode 100644 index 0000000..fe158f5 --- /dev/null +++ b/xwrap.c @@ -0,0 +1,23 @@ +#include +#include + +#include "debug.h" + +void *xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (p == NULL) + sys_err("malloc %zd bytes", size); + return p; +} + +char *xstrdup(const char *s) +{ + int len; + + len = strlen(s) + 1; + return memcpy(xmalloc(len), s, len); +} + diff --git a/xwrap.h b/xwrap.h new file mode 100644 index 0000000..ee50cd5 --- /dev/null +++ b/xwrap.h @@ -0,0 +1,17 @@ +#ifndef XWRAP_H +#define XWRAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void *xmalloc(size_t size); +char *xstrdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3-70-g09d2