diff options
| -rw-r--r-- | Makefile | 18 | ||||
| -rw-r--r-- | buffer.c | 78 | ||||
| -rw-r--r-- | buffer.h | 20 | ||||
| -rw-r--r-- | data.h | 6 | ||||
| -rw-r--r-- | debug.h | 49 | ||||
| -rw-r--r-- | list.h | 248 | ||||
| -rw-r--r-- | main.c | 756 | ||||
| -rw-r--r-- | tcpstat.c | 41 | ||||
| -rw-r--r-- | xwrap.c | 23 | ||||
| -rw-r--r-- | xwrap.h | 17 |
10 files changed, 1256 insertions, 0 deletions
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 <stdlib.h> +#include <unistd.h> + +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 <sys/types.h> + +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 @@ -0,0 +1,6 @@ +#ifndef DATA_H +#define DATA_H + +#define UNIX_SOCKET_PATH "/tmp/tcpproxy.unix" + +#endif @@ -0,0 +1,49 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +#include <stdio.h> +#include <errno.h> + +#ifndef NOCOLOR +#define GREEN "[32m" +#define DEF "[0m" +#define RED "[31m" +#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 @@ -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 @@ -0,0 +1,756 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/wait.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> + +#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 <sys/socket.h> +#include <sys/un.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#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; +} + @@ -0,0 +1,23 @@ +#include <string.h> +#include <stdlib.h> + +#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); +} + @@ -0,0 +1,17 @@ +#ifndef XWRAP_H +#define XWRAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +void *xmalloc(size_t size); +char *xstrdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif |
