├── .gitignore ├── README.txt ├── pidfile.h ├── daemonize.h ├── hashtable.h ├── mux.h ├── ssl.h ├── heapq.h ├── die.h ├── hashtable.c ├── pidfile.c ├── loop.h ├── Makefile ├── socket.h ├── ssl.c ├── heapq.c ├── daemonize.c ├── main.c ├── mux-kqueue.c ├── mux-select.c ├── buf.h ├── mux-epoll.c ├── buf.c ├── loop.c └── socket.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | libloop: Synchronous I/O Multiplexing and Timer Management 2 | 3 | demo see main.c 4 | 5 | License: 6 | Public Domain 7 | -------------------------------------------------------------------------------- /pidfile.h: -------------------------------------------------------------------------------- 1 | #ifndef __PIDFILE_H__ 2 | #define __PIDFILE_H__ 3 | 4 | int 5 | pidfile(const char *file); 6 | 7 | #endif /* __PIDFILE_H__ */ 8 | -------------------------------------------------------------------------------- /daemonize.h: -------------------------------------------------------------------------------- 1 | #ifndef __DAEMONIZE_H__ 2 | #define __DAEMONIZE_H__ 3 | 4 | int 5 | daemonize(int nochdir, int noclose); 6 | 7 | #endif /* __DAEMONIZE_H__ */ 8 | -------------------------------------------------------------------------------- /hashtable.h: -------------------------------------------------------------------------------- 1 | #ifndef __HASHTABLE_H__ 2 | #define __HASHTABLE_H__ 3 | 4 | int 5 | hashtable_set(void *table[], int nel, void *item, int (*hash)(const void *), 6 | int (*compare)(const void *, const void *)); 7 | 8 | void * 9 | hashtable_get(void *table[], int nel, void *item, int (*hash)(const void *), 10 | int (*compare)(const void *, const void *)); 11 | 12 | #endif /* __HASHTABLE_H__ */ 13 | -------------------------------------------------------------------------------- /mux.h: -------------------------------------------------------------------------------- 1 | #ifndef __MUX_H__ 2 | #define __MUX_H__ 3 | 4 | #include "loop.h" 5 | 6 | int 7 | mux_open(loop_t *loop, int event_max); 8 | 9 | void 10 | mux_close(loop_t *loop); 11 | 12 | int 13 | mux_set_event(loop_t *loop, int fd, int tag, int type); 14 | 15 | int 16 | mux_del_event(loop_t *loop, int fd, int type); 17 | 18 | int 19 | mux_polling(loop_t *loop, void *timer); 20 | 21 | #endif /* __MUX_H__ */ 22 | -------------------------------------------------------------------------------- /ssl.h: -------------------------------------------------------------------------------- 1 | #ifndef __SSL_H__ 2 | #define __SSL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct ssl_ctx * 9 | ssl_ctx_init(char *certificate, char *private); 10 | 11 | void 12 | ssl_ctx_deinit(struct ssl_ctx *ctx); 13 | 14 | struct ssl * 15 | ssl_open(struct ssl_ctx *ctx, int fd); 16 | 17 | int 18 | ssl_accept(struct ssl *ssl); 19 | 20 | void 21 | ssl_close(struct ssl *ssl); 22 | 23 | ssize_t 24 | ssl_read(struct ssl *ssl, void *buf, size_t n); 25 | 26 | ssize_t 27 | ssl_write(struct ssl *ssl, void *buf, size_t n); 28 | 29 | int 30 | ssl_error(); 31 | 32 | #endif /* __SSL_H__ */ 33 | -------------------------------------------------------------------------------- /heapq.h: -------------------------------------------------------------------------------- 1 | #ifndef __HEAPQ_H__ 2 | #define __HEAPQ_H__ 3 | 4 | #include 5 | 6 | void 7 | heapq_push(void *base, size_t nel, size_t width, void *item, void *args, 8 | int (*swap)(void *, void *, void *), 9 | int (*compare)(const void *, const void *)); 10 | 11 | void 12 | heapq_pop(void *base, size_t nel, size_t width, void *item, void *args, 13 | int (*swap)(void *, void *, void *), 14 | int (*compare)(const void *, const void *)); 15 | 16 | void 17 | heapq_heapify(void *base, size_t nel, size_t width, void *args, 18 | int (*swap)(void *, void *, void *), 19 | int (*compare)(const void *, const void *)); 20 | 21 | #endif /* __HEAPQ_H__ */ 22 | -------------------------------------------------------------------------------- /die.h: -------------------------------------------------------------------------------- 1 | #ifndef __DIE_H__ 2 | #define __DIE_H__ 3 | 4 | #include 5 | 6 | #define die(fmt, ...) do { \ 7 | fprintf(stderr, "%s:%d "fmt"\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 8 | exit(0); \ 9 | } while(0) 10 | 11 | #define die_errno(s) do { \ 12 | die("%s: %s", (s), strerror(errno)); \ 13 | } while(0) 14 | 15 | #define die_unless(a) do { \ 16 | if (!(a)) { \ 17 | die("%s", #a); \ 18 | } \ 19 | } while (0) 20 | 21 | #define die_errno_unless(a) do { \ 22 | if (!(a)) { \ 23 | die_errno(#a); \ 24 | } \ 25 | } while (0) 26 | 27 | #endif /* __DIE_H__ */ 28 | -------------------------------------------------------------------------------- /hashtable.c: -------------------------------------------------------------------------------- 1 | #include "hashtable.h" 2 | 3 | #include 4 | #include 5 | 6 | int 7 | hashtable_set(void *table[], int nel, void *item, int (*hash)(const void *), 8 | int (*compare)(const void *, const void *)) 9 | { 10 | int i; 11 | 12 | for (i = hash(item) % nel; table[i] != NULL; i = (i + 1) % nel) { 13 | if (compare(table[i], item) == 0) { 14 | break; 15 | } 16 | } 17 | 18 | table[i] = item; 19 | 20 | return 1; 21 | } 22 | 23 | void * 24 | hashtable_get(void *table[], int nel, void *item, int (*hash)(const void *), 25 | int (*compare)(const void *, const void *)) 26 | { 27 | int i; 28 | 29 | for (i = hash(item) % nel; table[i] != NULL; i = (i + 1) % nel) { 30 | if (compare(table[i], item) == 0) { 31 | return table[i]; 32 | } 33 | } 34 | 35 | return NULL; 36 | } 37 | -------------------------------------------------------------------------------- /pidfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | pidfile(const char *file) 10 | { 11 | int fd; 12 | int flags; 13 | int pidlen; 14 | char pid[32]; 15 | struct flock lock; 16 | 17 | fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 18 | if (fd == -1) { 19 | return -1; 20 | } 21 | 22 | flags = fcntl(fd, F_GETFD); 23 | if (flags == -1) { 24 | return -1; 25 | } 26 | 27 | flags |= FD_CLOEXEC; 28 | if (fcntl(fd, F_SETFD, flags) == -1) { 29 | return -1; 30 | } 31 | 32 | /* lock file */ 33 | lock.l_len = 0; 34 | lock.l_start = 0; 35 | lock.l_type = F_WRLCK; 36 | lock.l_whence = SEEK_SET; 37 | if (fcntl(fd, F_SETLK, &lock) == -1) { 38 | return -1; 39 | } 40 | 41 | if (ftruncate(fd, 0) == -1) { 42 | return -1; 43 | } 44 | 45 | pidlen = snprintf(pid, sizeof(pid), "%ld\n", (long)getpid()); 46 | if (write(fd, pid, pidlen) != pidlen) { 47 | return -1; 48 | } 49 | 50 | return fd; 51 | } 52 | -------------------------------------------------------------------------------- /loop.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOOP_H__ 2 | #define __LOOP_H__ 3 | 4 | enum { 5 | LOOP_OK = 0, 6 | LOOP_ERR = -1 7 | }; 8 | 9 | enum { 10 | LOOP_EOF = 1 << 0, 11 | LOOP_READ = 1 << 1, 12 | LOOP_WRITE = 1 << 2, 13 | LOOP_ERROR = 1 << 3, 14 | }; 15 | 16 | enum { 17 | LOOP_FLAGS_REPEAT = 1 << 0, 18 | LOOP_FLAGS_ABSOLUTE = 1 << 1, 19 | LOOP_FLAGS_INVALID = 1 << 2, 20 | }; 21 | 22 | typedef struct loop loop_t; 23 | 24 | typedef void loop_proc_t(loop_t *loop, int id, int tag, int type, int flag, void *args); 25 | 26 | loop_t * 27 | loop_open(int event_max, int timer_max); 28 | 29 | void 30 | loop_close(loop_t *loop); 31 | 32 | int 33 | loop_set_event(loop_t *loop, int fd, int tag, int type, int flag, 34 | loop_proc_t *proc, void *args); 35 | 36 | int 37 | loop_del_event(loop_t *loop, int fd, int type); 38 | 39 | int 40 | loop_set_timer(loop_t *loop, int id, int tag, int type, int flag, 41 | long seconds, int nanoseconds, loop_proc_t *proc, void *args); 42 | 43 | int 44 | loop_del_timer(loop_t *loop, int id, int tag, int type); 45 | 46 | int 47 | loop_start(loop_t *loop); 48 | 49 | int 50 | loop_stop(loop_t *loop); 51 | 52 | #endif /* __LOOP_H__ */ 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | 3 | system_name := $(shell sh -c 'uname -s 2>/dev/null || echo not') 4 | 5 | CFLAGS += -O2 6 | CFLAGS += -Wall 7 | CFLAGS += -Werror 8 | 9 | LDFLAGS += -lssl 10 | LDFLAGS += -lcrypto 11 | LDFLAGS += -lpthread 12 | 13 | SRC += buf.c 14 | SRC += ssl.c 15 | SRC += loop.c 16 | SRC += heapq.c 17 | SRC += socket.c 18 | SRC += hashtable.c 19 | 20 | OBJS = $(SRC:.c=.o) 21 | 22 | ifeq ($(system_name),Linux) 23 | CFLAGS += -DUSE_EPOLL 24 | CFLAGS += -D__USE_GNU 25 | CFLAGS += -D_GNU_SOURCE 26 | else ifeq ($(system_name),Darwin) 27 | CFLAGS += -DHAVE_SA_LEN 28 | CFLAGS += -DHAVE_SIN_LEN 29 | CFLAGS += -DUSE_KQUEUE 30 | CFLAGS += -D__APPLE_USE_RFC_2292 31 | CFLAGS += -D_GNU_SOURCE 32 | CFLAGS += -I/usr/local/opt/openssl/include # Homebrew Version 33 | LDFLAGS += -L/usr/local/opt/openssl/lib # Homebrew Version 34 | else ifeq ($(system_name),FreeBSD) 35 | CFLAGS += -DUSE_KQUEUE 36 | else 37 | CFLAGS += -DUSE_SELECT 38 | endif 39 | 40 | all: main 41 | 42 | main: main.o $(OBJS) Makefile 43 | @$(CC) main.o $(OBJS) -o $@ $(LDFLAGS) 44 | @echo CC main 45 | 46 | loop.o: loop.c mux-epoll.c mux-kqueue.c mux-select.c Makefile 47 | @$(CC) $(CFLAGS) -c $< -o $@ 48 | @echo CC $< 49 | 50 | %.o: %.c Makefile 51 | @$(CC) $(CFLAGS) -c $< -o $@ 52 | @echo CC $< 53 | 54 | clean: 55 | @rm main main.o $(OBJS) 56 | @echo clean main main.o $(OBJS) 57 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | #ifndef __SOCKET_H__ 2 | #define __SOCKET_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define SOCKET_ADDR_STRLEN INET6_ADDRSTRLEN 8 | 9 | typedef struct { 10 | socklen_t addrlen; 11 | union { 12 | struct sockaddr sa; 13 | struct sockaddr_in si; 14 | struct sockaddr_in6 s6; 15 | struct sockaddr_storage ss; 16 | } addr; 17 | } socket_addr_t; 18 | 19 | int 20 | socket_addr(socket_addr_t *addr, char *hostname, int port); 21 | 22 | int 23 | socket_addr_str(socket_addr_t *addr, char *addrstr, int *port); 24 | 25 | int 26 | socket_open(int domain, int type, int protocol); 27 | 28 | int 29 | socket_set_nonblock(int sockfd); 30 | 31 | int 32 | socket_set_reuseaddr(int sockfd); 33 | 34 | int 35 | socket_set_tcp_nodelay(int sockfd); 36 | 37 | int 38 | socket_set_tcp_nopush(int sockfd); 39 | 40 | int 41 | socket_set_reuseport(int sockfd); 42 | 43 | int 44 | socket_set_pktinfo(int sockfd); 45 | 46 | int 47 | socket_bind(int sockfd, socket_addr_t *addr); 48 | 49 | int 50 | socket_listen(int sockfd, int backlog); 51 | 52 | int 53 | socket_accept(int sockfd, socket_addr_t *addr); 54 | 55 | int 56 | socket_connect(int sockfd, socket_addr_t *addr); 57 | 58 | ssize_t 59 | socket_recvfromto(int sockfd, void *buf, size_t len, int flags, 60 | socket_addr_t *src, socket_addr_t *dst); 61 | 62 | ssize_t 63 | socket_sendtofrom(int sockfd, void *buf, size_t len, int flags, 64 | socket_addr_t *dst, socket_addr_t *src); 65 | 66 | int 67 | socket_close(int sockfd); 68 | 69 | #endif /* __SOCKET_H__ */ 70 | -------------------------------------------------------------------------------- /ssl.c: -------------------------------------------------------------------------------- 1 | #include "ssl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct ssl_ctx { 8 | SSL_CTX *ctx; 9 | }; 10 | 11 | struct ssl { 12 | SSL *ssl; 13 | }; 14 | 15 | struct ssl_ctx * 16 | ssl_ctx_init(char *certificate, char *private) 17 | { 18 | struct ssl_ctx *ctx; 19 | 20 | ctx = malloc(sizeof(struct ssl_ctx)); 21 | 22 | SSL_load_error_strings(); 23 | SSL_library_init(); 24 | OpenSSL_add_all_algorithms(); 25 | 26 | ctx->ctx = SSL_CTX_new(SSLv23_server_method()); 27 | SSL_CTX_set_options(ctx->ctx, SSL_OP_SINGLE_DH_USE); 28 | SSL_CTX_use_certificate_file(ctx->ctx, certificate, SSL_FILETYPE_PEM); 29 | SSL_CTX_use_PrivateKey_file(ctx->ctx, private, SSL_FILETYPE_PEM); 30 | /* 31 | SSL_CTX_set_cipher_list(ctx->ctx, "ALL"); 32 | */ 33 | 34 | return ctx; 35 | } 36 | 37 | void 38 | ssl_ctx_deinit(struct ssl_ctx *ctx) 39 | { 40 | ERR_free_strings(); 41 | EVP_cleanup(); 42 | 43 | free(ctx); 44 | } 45 | 46 | struct ssl * 47 | ssl_open(struct ssl_ctx *ctx, int fd) 48 | { 49 | struct ssl *ssl; 50 | ssl = malloc(sizeof(struct ssl)); 51 | 52 | ssl->ssl = SSL_new(ctx->ctx); 53 | SSL_set_fd(ssl->ssl, fd); 54 | 55 | return ssl; 56 | } 57 | 58 | int 59 | ssl_accept(struct ssl *ssl) 60 | { 61 | return SSL_accept(ssl->ssl); 62 | } 63 | 64 | void 65 | ssl_close(struct ssl *ssl) 66 | { 67 | SSL_shutdown(ssl->ssl); 68 | SSL_free(ssl->ssl); 69 | free(ssl); 70 | } 71 | 72 | ssize_t 73 | ssl_read(struct ssl *ssl, void *buf, size_t n) 74 | { 75 | return SSL_read(ssl->ssl, buf, n); 76 | } 77 | 78 | ssize_t 79 | ssl_write(struct ssl *ssl, void *buf, size_t n) 80 | { 81 | return SSL_write(ssl->ssl, buf, n); 82 | } 83 | 84 | int 85 | ssl_error(struct ssl *ssl, int ret) 86 | { 87 | return SSL_get_error(ssl->ssl, ret); 88 | } 89 | -------------------------------------------------------------------------------- /heapq.c: -------------------------------------------------------------------------------- 1 | #include "heapq.h" 2 | 3 | #include 4 | #include 5 | 6 | static void 7 | shiftdown(void *base, int i, int j, int len, int width, void *args, 8 | int (*swap)(void *, void *, void *), 9 | int (*compare)(const void *, const void *)) 10 | { 11 | assert(i < len); 12 | assert(j < len); 13 | 14 | while (j > i) { 15 | int k = (j - 1) / 2; 16 | 17 | void *a = (char *)base + j * width; 18 | void *b = (char *)base + k * width; 19 | 20 | if (compare(a, b) < 0) { 21 | swap(args, a, b); 22 | } else { 23 | break; 24 | } 25 | j = k; 26 | } 27 | } 28 | 29 | static void 30 | shiftup(int *base, int j, int len, int width, void *args, 31 | int (*swap)(void *, void *, void *), 32 | int (*compare)(const void *, const void *)) 33 | { 34 | int i = j; 35 | int k = j * 2 + 1; 36 | int l = len - 1; 37 | 38 | assert(j < len); 39 | 40 | while (k < l) { 41 | int r = k + 1; 42 | 43 | if ((r < l) && (compare((char *)base + k * width, 44 | (char *)base + r * width) >= 0)) 45 | { 46 | k = r; 47 | } 48 | 49 | swap(args, (char *)base + j * width, (char *)base + k * width); 50 | 51 | j = k; 52 | k = j * 2 + 1; 53 | } 54 | shiftdown(base, i, j, len, width, args, swap, compare); 55 | } 56 | 57 | void 58 | heapq_push(void *base, size_t nel, size_t width, void *item, void *args, 59 | int (*swap)(void *, void *, void *), 60 | int (*compare)(const void *, const void *)) 61 | { 62 | memmove((char *)base + (nel - 1) * width, item, width); 63 | 64 | shiftdown(base, 0, nel - 1, nel, width, args, swap, compare); 65 | } 66 | 67 | void 68 | heapq_pop(void *base, size_t nel, size_t width, void *item, void *args, 69 | int (*swap)(void *, void *, void *), 70 | int (*compare)(const void *, const void *)) 71 | { 72 | 73 | memmove(item, base, width); 74 | memmove(base, (char *)base + (nel - 1) * width, width); 75 | 76 | shiftup(base, 0, nel, width, args, swap, compare); 77 | } 78 | 79 | void 80 | heapq_heapify(void *base, size_t nel, size_t width, void *args, 81 | int (*swap)(void *, void *, void *), 82 | int (*compare)(const void *, const void *)) 83 | { 84 | int i; 85 | for (i = nel / 2 - 1; i >= 0; i--) { 86 | shiftup(base, i, nel, width, args, swap, compare); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /daemonize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * ref: 14 | * http://0pointer.de/public/systemd-man/daemon.html 15 | */ 16 | int 17 | daemonize(int nochdir, int noclose) 18 | { 19 | int sig; 20 | pid_t pid; 21 | sigset_t set; 22 | struct rlimit limit; 23 | 24 | if (!noclose) { 25 | /* 26 | * Close all open file descriptors except STDIN, STDOUT, STDERR 27 | * (i.e. the first three file descriptors 0, 1, 2). 28 | * This ensures that no accidentally passed file descriptor stays around 29 | * in the daemon process. 30 | */ 31 | if (getrlimit(RLIMIT_NOFILE, &limit) == -1) { 32 | return -1; 33 | } 34 | 35 | for(; limit.rlim_cur > 3; limit.rlim_cur--) { 36 | close(limit.rlim_cur - 1); 37 | } 38 | } 39 | 40 | /* Reset all signal handlers to their default. */ 41 | for (sig = 1; sig <= NSIG; sig++) { 42 | signal(sig, SIG_DFL); 43 | } 44 | 45 | /* Reset the signal mask using sigprocmask(). */ 46 | sigemptyset(&set); 47 | sigprocmask(SIG_SETMASK, &set, NULL); 48 | 49 | /* Call fork(), to create a background process. */ 50 | pid = fork(); 51 | if (pid == -1) { 52 | return -1; 53 | } else if (pid != 0) { 54 | _exit(0); 55 | } 56 | 57 | /* Call setsid() to detach from any terminal and create an independent session. */ 58 | if (setsid() == -1) { 59 | return -1; 60 | } 61 | 62 | /* Call fork() again, to ensure that the daemon can never re-acquire a terminal again. */ 63 | signal(SIGHUP, SIG_IGN); 64 | pid = fork(); 65 | if (pid == -1) { 66 | return -1; 67 | } else if (pid != 0) { 68 | _exit(0); 69 | } 70 | 71 | /* connect /dev/null to STDIN, STDOUT, STDERR. */ 72 | close(STDIN_FILENO); 73 | close(STDOUT_FILENO); 74 | close(STDERR_FILENO); 75 | 76 | if (open("/dev/null", O_RDONLY) == -1) { 77 | return -1; 78 | } 79 | if (open("/dev/null", O_WRONLY) == -1) { 80 | return -1; 81 | } 82 | if (open("/dev/null", O_RDWR) == -1) { 83 | return -1; 84 | } 85 | 86 | /* 87 | * reset the umask to 0, so that the file modes passed to 88 | * open(), mkdir() and suchlike directly control the access mode 89 | * of the created files and directories. 90 | */ 91 | umask(0); 92 | 93 | if (!nochdir) { 94 | /* 95 | * change the current directory to the root directory (/), 96 | * in order to avoid that the daemon involuntarily blocks 97 | * mount points from being unmounted. 98 | */ 99 | if (chdir("/") == -1) { 100 | return -1; 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "die.h" 2 | #include "buf.h" 3 | #include "loop.h" 4 | #include "socket.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void 15 | tcp_handler(loop_t *loop, int fd, int tag, int type, int flag, void *args) 16 | { 17 | int len; 18 | char request[4096]; 19 | char response[] = "HTTP/1.1 200 OK\r\n" 20 | "Server: DemoWebserver\r\n" 21 | "Connection: keep-alive\r\n" 22 | "Content-Type: text/plain\r\n" 23 | "Content-Length: 11\r\n\r\n" 24 | "HelloWorld"; 25 | 26 | len = recv(fd, request, sizeof(request), 0); 27 | if (len <= 0) { 28 | goto err; 29 | } 30 | 31 | len = send(fd, response, sizeof(response), 0); 32 | if (len <= 0) { 33 | goto err; 34 | } 35 | return; 36 | err: 37 | loop_del_event(loop, fd, LOOP_READ | LOOP_WRITE); 38 | close(fd); 39 | } 40 | 41 | void 42 | tcp_acceptor(loop_t *loop, int fd, int tag, int type, int flag, void *args) 43 | { 44 | int port; 45 | int acceptfd; 46 | socket_addr_t addr; 47 | char addrstr[SOCKET_ADDR_STRLEN]; 48 | 49 | acceptfd = socket_accept(fd, &addr); 50 | socket_set_nonblock(acceptfd); 51 | socket_addr_str(&addr, addrstr, &port); 52 | printf("accept: %s:%d on fd %d\n", addrstr, port, acceptfd); 53 | 54 | loop_set_event(loop, acceptfd, 0, LOOP_READ, 0, tcp_handler, NULL); 55 | } 56 | 57 | void 58 | timer_func(loop_t *loop, int id, int tag, int type, int flag, void *args) 59 | { 60 | printf("timer: %d\n", id); 61 | } 62 | 63 | int 64 | main(void) 65 | { 66 | int i; 67 | int fd; 68 | int err; 69 | 70 | loop_t *loop; 71 | socket_addr_t addr; 72 | 73 | signal(SIGPIPE, SIG_IGN); 74 | 75 | loop = loop_open(1024, 1024); 76 | die_unless(loop != NULL); 77 | 78 | fd = socket_open(AF_INET6, SOCK_STREAM, 0); 79 | die_unless(fd != -1); 80 | 81 | err = socket_set_nonblock(fd); 82 | die_unless(err == 0); 83 | 84 | err = socket_set_reuseaddr(fd); 85 | die_unless(err == 0); 86 | 87 | err = socket_set_reuseport(fd); 88 | die_unless(err == 0); 89 | 90 | err = socket_set_tcp_nodelay(fd); 91 | die_unless(err == 0); 92 | 93 | err = socket_set_tcp_nopush(fd); 94 | die_unless(err == 0); 95 | 96 | err = socket_addr(&addr, "::", 8080); 97 | die_unless(err == 0); 98 | 99 | err = socket_bind(fd, &addr); 100 | die_unless(err == 0); 101 | 102 | err = socket_listen(fd, 32); 103 | die_unless(err == 0); 104 | 105 | err = loop_set_event(loop, fd, 0, LOOP_READ, 0, tcp_acceptor, NULL); 106 | die_unless(err == 0); 107 | 108 | for (i = 0; i < 1024; i++) { 109 | err = loop_set_timer(loop, i, 1, 0, 0, i, 0, timer_func, NULL); 110 | die_unless(err == 0); 111 | } 112 | 113 | err = loop_start(loop); 114 | die_errno_unless(err == 0); 115 | 116 | loop_close(loop); 117 | 118 | goto err; 119 | err: 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /mux-kqueue.c: -------------------------------------------------------------------------------- 1 | #include "mux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct mux { 11 | int fd; 12 | }; 13 | 14 | int 15 | mux_open(loop_t *loop, int event_max) 16 | { 17 | loop->mux = malloc(sizeof(struct mux)); 18 | if (loop->mux == NULL) { 19 | goto err; 20 | } 21 | 22 | loop->mux->fd = kqueue(); 23 | if (loop->mux->fd == -1) { 24 | goto err; 25 | } 26 | 27 | return LOOP_OK; 28 | err: 29 | free(loop->mux); 30 | 31 | return LOOP_ERROR; 32 | } 33 | 34 | void 35 | mux_close(loop_t *loop) 36 | { 37 | free(loop->mux); 38 | } 39 | 40 | int 41 | mux_set_event(loop_t *loop, int fd, int tag, int type) 42 | { 43 | struct kevent event; 44 | 45 | if (type & LOOP_WRITE) { 46 | EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 47 | } 48 | 49 | if (type & LOOP_READ) { 50 | EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 51 | } 52 | 53 | if (kevent(loop->mux->fd, &event, 1, NULL, 0, NULL) == -1) { 54 | return LOOP_ERR; 55 | } 56 | 57 | return LOOP_OK; 58 | } 59 | 60 | int 61 | mux_del_event(loop_t *loop, int fd, int type) 62 | { 63 | struct kevent event; 64 | 65 | if (type & LOOP_WRITE) { 66 | EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 67 | } 68 | 69 | if (type & LOOP_READ) { 70 | EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 71 | } 72 | 73 | if (kevent(loop->mux->fd, &event, 1, NULL, 0, NULL) == -1) { 74 | return LOOP_ERR; 75 | } 76 | 77 | return LOOP_OK; 78 | } 79 | 80 | #define EVENTS_NR 128 81 | 82 | int 83 | mux_polling(loop_t *loop, void *timer) 84 | { 85 | int i; 86 | int n; 87 | int fd; 88 | 89 | struct timespec *timeout; 90 | struct timespec timespec; 91 | 92 | struct kevent events[EVENTS_NR]; 93 | 94 | if (timer == NULL) { 95 | timeout = NULL; 96 | } else { 97 | timespec.tv_sec = ((timer_t *)timer)->seconds; 98 | timespec.tv_nsec = ((timer_t *)timer)->nanoseconds; 99 | 100 | timeout = ×pec; 101 | } 102 | 103 | n = kevent(loop->mux->fd, NULL, 0, events, EVENTS_NR, timeout); 104 | if (n == -1) { 105 | if (errno == EINTR) { 106 | n = 0; 107 | } else { 108 | return LOOP_ERR; 109 | } 110 | } 111 | 112 | loop_timer_dispatch(loop); 113 | 114 | for (i = 0; i < n; i++) { 115 | int type; 116 | struct event *event; 117 | loop_proc_t *proc; 118 | 119 | fd = events[i].ident; 120 | event = &loop->event[fd]; 121 | 122 | if ((proc = event->proc) == NULL) { 123 | continue; 124 | } 125 | 126 | type = 0; 127 | if (events[i].flags & EV_EOF) { 128 | type = LOOP_EOF; 129 | } else { 130 | switch (events[i].filter) { 131 | case EVFILT_READ: 132 | type = LOOP_READ; 133 | break; 134 | case EVFILT_WRITE: 135 | type = LOOP_WRITE; 136 | break; 137 | default: 138 | continue; 139 | } 140 | } 141 | 142 | proc(loop, fd, event->tag, type, event->flag, event->args); 143 | } 144 | return LOOP_OK; 145 | } 146 | -------------------------------------------------------------------------------- /mux-select.c: -------------------------------------------------------------------------------- 1 | #include "mux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct mux { 9 | int maxfd; 10 | fd_set readfds; 11 | fd_set writefds; 12 | }; 13 | 14 | int 15 | mux_open(loop_t *loop, int event_max) 16 | { 17 | if (event_max > FD_SETSIZE) { 18 | goto err; 19 | } 20 | 21 | loop->mux = malloc(sizeof(struct mux)); 22 | if (loop->mux == NULL) { 23 | goto err; 24 | } 25 | 26 | return LOOP_OK; 27 | err: 28 | free(loop->mux); 29 | 30 | return LOOP_ERROR; 31 | } 32 | 33 | void 34 | mux_close(loop_t *loop) 35 | { 36 | free(loop->mux); 37 | } 38 | 39 | int 40 | mux_set_event(loop_t *loop, int fd, int tag, int type) 41 | { 42 | if (type & LOOP_WRITE) { 43 | FD_SET(fd, &loop->mux->writefds); 44 | } 45 | 46 | if (type & LOOP_READ) { 47 | FD_SET(fd, &loop->mux->readfds); 48 | } 49 | 50 | if (fd > loop->mux->maxfd) { 51 | loop->mux->maxfd = fd; 52 | } 53 | 54 | return LOOP_OK; 55 | } 56 | 57 | int 58 | mux_del_event(loop_t *loop, int fd, int type) 59 | { 60 | if (type & LOOP_WRITE) { 61 | FD_CLR(fd, &loop->mux->writefds); 62 | } 63 | 64 | if (type & LOOP_READ) { 65 | FD_CLR(fd, &loop->mux->readfds); 66 | } 67 | 68 | if (loop->event[fd].type == 0) { 69 | memset(&loop->event[fd], 0, sizeof(struct event)); 70 | if (fd >= loop->mux->maxfd) { 71 | while (FD_ISSET(fd, &loop->mux->readfds) == 0 && 72 | FD_ISSET(fd, &loop->mux->writefds) == 0) 73 | { 74 | fd--; 75 | } 76 | loop->mux->maxfd = fd; 77 | } 78 | } 79 | 80 | return LOOP_OK; 81 | } 82 | 83 | int 84 | mux_polling(loop_t *loop, void *timer) 85 | { 86 | int n; 87 | int fd; 88 | 89 | struct timeval *timeout; 90 | struct timeval timeval; 91 | 92 | fd_set readfds; 93 | fd_set writefds; 94 | 95 | if (timer == NULL) { 96 | timeout = NULL; 97 | } else { 98 | timeval.tv_sec = ((timer_t *)timer)->seconds; 99 | timeval.tv_usec = ((timer_t *)timer)->nanoseconds / NSEC_PER_USEC; 100 | 101 | timeout = &timeval; 102 | } 103 | memcpy(&readfds, &loop->mux->readfds, sizeof(readfds)); 104 | memcpy(&writefds, &loop->mux->writefds, sizeof(writefds)); 105 | 106 | n = select(loop->mux->maxfd + 1, &readfds, &writefds, NULL, timeout); 107 | if (n == -1) { 108 | return LOOP_ERR; 109 | } 110 | 111 | loop_timer_dispatch(loop); 112 | 113 | for (fd = 0; fd <= loop->mux->maxfd && n > 0; fd++) { 114 | int type; 115 | struct event *event; 116 | loop_proc_t *proc; 117 | 118 | event = &loop->event[fd]; 119 | 120 | if ((proc = event->proc) == NULL) { 121 | continue; 122 | } 123 | 124 | type = 0; 125 | if (FD_ISSET(fd, &writefds)) { 126 | n--; 127 | type |= LOOP_WRITE; 128 | } 129 | 130 | if (FD_ISSET(fd, &readfds)) { 131 | n--; 132 | type |= LOOP_READ; 133 | } 134 | if (type == 0) { 135 | continue; 136 | } 137 | proc(loop, fd, event->tag, type, event->flag, event->args); 138 | } 139 | return LOOP_OK; 140 | } 141 | -------------------------------------------------------------------------------- /buf.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUF_H__ 2 | #define __BUF_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* TODO: add a sbuf_ctx_t and cbuf_ctx_t for allocator */ 10 | /* FIXME: cbuf is not well tested do not use */ 11 | 12 | /* should reset sbuf when off == len to save memory */ 13 | typedef struct sbuf { /* stream buffer */ 14 | int off; /* increase by write */ 15 | int len; /* increase by read */ 16 | int max; /* off < len <= max */ 17 | char *buf; 18 | } sbuf_t; 19 | 20 | typedef struct cbuf { /* chained buffer */ 21 | int off; /* not byte */ 22 | int len; /* not byte */ 23 | int max; /* off < len <= max */ 24 | sbuf_t *buf; 25 | } cbuf_t; 26 | 27 | int 28 | sbuf_alloc(sbuf_t *sbuf, int max); 29 | 30 | void 31 | sbuf_release(sbuf_t *sbuf); 32 | 33 | /* 34 | * sbuf->off = 0; 35 | * sbuf->len = 0; 36 | * keep sbuf->max and sbuf->buf 37 | */ 38 | void 39 | sbuf_reset(sbuf_t *sbuf); 40 | 41 | char * 42 | sbuf_detach(sbuf_t *sbuf); 43 | 44 | /* 45 | * free sbuf->buf 46 | * sbuf->buf = buf; 47 | * sbuf->max = len; 48 | */ 49 | void 50 | sbuf_attach(sbuf_t *sbuf, char *buf, int len); 51 | 52 | /* 53 | * make buf->max >= buf->len + len 54 | */ 55 | int 56 | sbuf_extend(sbuf_t *sbuf, int len); 57 | 58 | int 59 | sbuf_prepend(sbuf_t *sbuf, void *buf, int len); 60 | 61 | int 62 | sbuf_append(sbuf_t *sbuf, void *buf, int len); 63 | 64 | /* 65 | * send until empty the sbuf or will block or full 66 | * return > 0 success 67 | * return <= 0 fail 68 | */ 69 | ssize_t 70 | sbuf_send(const int fd, sbuf_t *buf, ssize_t *snd); 71 | 72 | /* 73 | * recv until full the sbuf or will block 74 | * return > 0 success 75 | * return <= 0 fail 76 | */ 77 | ssize_t 78 | sbuf_recv(const int fd, sbuf_t *buf, ssize_t *rcv); 79 | 80 | /* TODO: add sbuf interface to cbuf */ 81 | 82 | int 83 | cbuf_alloc(cbuf_t *cbuf, int max); 84 | 85 | void 86 | cbuf_release(cbuf_t *cbuf); 87 | 88 | /* bytes offset */ 89 | long 90 | cbuf_get_off(cbuf_t *cbuf); 91 | 92 | void 93 | cbuf_set_off(cbuf_t *cbuf, long off); 94 | 95 | /* bytes length */ 96 | long 97 | cbuf_get_len(cbuf_t *cbuf); 98 | 99 | void 100 | cbuf_set_len(cbuf_t *cbuf, long off); 101 | 102 | /* 103 | * bytes max 104 | * and there is no set_max 105 | */ 106 | long 107 | cbuf_get_max(cbuf_t *cbuf); 108 | 109 | int 110 | cbuf_extend(cbuf_t *cbuf, int len); 111 | 112 | /* 113 | * If type is 0, the offset is from start (0 -> len). 114 | * If type is 1, the offset is from current location (off -> len). 115 | * If type is 2, the offset is from end location (len -> max). 116 | */ 117 | void 118 | cbuf_iovec(cbuf_t *cbuf, struct iovec *iov, int iovcnt, int type); 119 | 120 | /* 121 | * send until empty the cbuf or will block or full 122 | * return > 0 success 123 | * return <= 0 fail 124 | */ 125 | ssize_t 126 | cbuf_send(const int fd, cbuf_t *buf, ssize_t *snd); 127 | 128 | /* 129 | * recv until full the cbuf or will block 130 | * return > 0 success 131 | * return <= 0 fail 132 | */ 133 | ssize_t 134 | cbuf_recv(const int fd, cbuf_t *buf, ssize_t *rcv); 135 | 136 | #endif /* __BUF_H__ */ 137 | -------------------------------------------------------------------------------- /mux-epoll.c: -------------------------------------------------------------------------------- 1 | #include "loop.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct mux { 12 | int fd; 13 | }; 14 | 15 | int 16 | mux_open(loop_t *loop, int event_max) 17 | { 18 | loop->mux = malloc(sizeof(struct mux)); 19 | if (loop->mux == NULL) { 20 | goto err; 21 | } 22 | 23 | loop->mux->fd = epoll_create(event_max); 24 | if (loop->mux->fd == -1) { 25 | goto err; 26 | } 27 | 28 | return LOOP_OK; 29 | err: 30 | free(loop->mux); 31 | 32 | return LOOP_ERR; 33 | } 34 | 35 | void 36 | mux_close(loop_t *loop) 37 | { 38 | free(loop->mux); 39 | } 40 | 41 | int 42 | mux_set_event(loop_t *loop, int fd, int tag, int type) 43 | { 44 | uint32_t events; 45 | struct epoll_event event; 46 | 47 | events = 0; 48 | memset(&event, 0, sizeof(event)); 49 | if (type & LOOP_WRITE) { 50 | events |= EPOLLOUT; 51 | } 52 | 53 | if (type & LOOP_READ) { 54 | events |= EPOLLIN; 55 | } 56 | 57 | event.events = events; 58 | event.data.fd = fd; 59 | if (loop->event[fd].type == 0) { 60 | if (epoll_ctl(loop->mux->fd, EPOLL_CTL_ADD, fd, &event) == -1) { 61 | return LOOP_ERR; 62 | } 63 | } else { 64 | if (epoll_ctl(loop->mux->fd, EPOLL_CTL_MOD, fd, &event) == -1) { 65 | return LOOP_ERR; 66 | } 67 | } 68 | 69 | return LOOP_OK; 70 | } 71 | 72 | int 73 | mux_del_event(loop_t *loop, int fd, int type) 74 | { 75 | uint32_t events; 76 | struct epoll_event event; 77 | 78 | if ((type & LOOP_WRITE) && (type & LOOP_READ)) { 79 | if (epoll_ctl(loop->mux->fd, EPOLL_CTL_DEL, fd, NULL) == -1) { 80 | return LOOP_ERR; 81 | } 82 | 83 | return LOOP_OK; 84 | } 85 | 86 | events = 0; 87 | if (type & LOOP_WRITE) { 88 | events |= EPOLLOUT; 89 | } 90 | 91 | if (type & LOOP_READ) { 92 | events |= EPOLLIN; 93 | } 94 | 95 | event.events = events; 96 | event.data.fd = fd; 97 | if (epoll_ctl(loop->mux->fd, EPOLL_CTL_MOD, fd, &event) == -1) { 98 | return LOOP_ERR; 99 | } 100 | 101 | return LOOP_OK; 102 | } 103 | 104 | #define EVENTS_NR 64 105 | 106 | int 107 | mux_polling(loop_t *loop, void *timer) 108 | { 109 | int i; 110 | int n; 111 | int fd; 112 | int timeout; 113 | 114 | struct epoll_event events[EVENTS_NR]; 115 | 116 | if (timer == NULL) { 117 | timeout = -1; 118 | } else { 119 | timeout = ((timer_t *)timer)->seconds * USEC_PER_SEC; 120 | timeout += ((timer_t *)timer)->seconds / NSEC_PER_USEC; 121 | } 122 | 123 | n = epoll_wait(loop->mux->fd, events, EVENTS_NR, timeout); 124 | if (n == -1) { 125 | if (errno == EINTR) { 126 | n = 0; 127 | } else { 128 | return LOOP_ERR; 129 | } 130 | } 131 | loop_timer_dispatch(loop); 132 | 133 | for (i = 0; i < n; i++) { 134 | int type; 135 | struct event *event; 136 | loop_proc_t *proc; 137 | 138 | fd = events[i].data.fd; 139 | event = &loop->event[fd]; 140 | 141 | if ((proc = event->proc) == NULL) { 142 | continue; 143 | } 144 | 145 | type = 0; 146 | if (events[i].events & EPOLLERR) { 147 | type = LOOP_ERROR; 148 | } else { 149 | if (events[i].events & EPOLLIN) { 150 | type |= LOOP_READ; 151 | } 152 | if (events[i].events & EPOLLOUT) { 153 | type |= LOOP_WRITE; 154 | } 155 | } 156 | 157 | proc(loop, fd, event->tag, type, event->flag, event->args); 158 | } 159 | return LOOP_OK; 160 | } 161 | -------------------------------------------------------------------------------- /buf.c: -------------------------------------------------------------------------------- 1 | #include "buf.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* can't bigger than UIO_MAXIOV */ 10 | #define IOVCNT_DEFAULT 16 11 | 12 | int 13 | sbuf_alloc(sbuf_t *sbuf, int max) 14 | { 15 | sbuf->off = 0; 16 | sbuf->len = 0; 17 | 18 | if ((sbuf->max = max) > 0 && 19 | (sbuf->buf = malloc(max)) != NULL) 20 | { 21 | return 0; 22 | } 23 | sbuf->max = 0; 24 | 25 | return -1; 26 | } 27 | 28 | void 29 | sbuf_release(sbuf_t *sbuf) 30 | { 31 | sbuf->off = 0; 32 | sbuf->len = 0; 33 | sbuf->max = 0; 34 | free(sbuf->buf); 35 | sbuf->buf = NULL; 36 | } 37 | 38 | void 39 | sbuf_reset(sbuf_t *sbuf) 40 | { 41 | sbuf->off = 0; 42 | sbuf->len = 0; 43 | } 44 | 45 | char * 46 | sbuf_detach(sbuf_t *sbuf) 47 | { 48 | char *buf; 49 | 50 | buf = sbuf->buf; 51 | 52 | sbuf->off = 0; 53 | sbuf->len = 0; 54 | sbuf->max = 0; 55 | sbuf->buf = NULL; 56 | 57 | return buf; 58 | } 59 | 60 | void 61 | sbuf_attach(sbuf_t *sbuf, char *buf, int len) 62 | { 63 | sbuf_release(sbuf); 64 | 65 | sbuf->buf = buf; 66 | sbuf->len = len; 67 | sbuf->max = len; 68 | } 69 | 70 | int 71 | sbuf_extend(sbuf_t *sbuf, int len) 72 | { 73 | char *buf; 74 | 75 | if ((sbuf->len + len) < sbuf->max) { 76 | return 0; 77 | } 78 | 79 | if (sbuf->buf != NULL) { 80 | buf = realloc(sbuf->buf, sbuf->max + len); 81 | } else { 82 | buf = malloc(len); 83 | } 84 | if (buf != NULL) { 85 | sbuf->buf = buf; 86 | sbuf->max += len; 87 | } else { 88 | return -1; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | int 95 | sbuf_prepend(sbuf_t *sbuf, void *buf, int len) 96 | { 97 | sbuf_extend(sbuf, len); 98 | 99 | memmove(sbuf->buf + len, sbuf->buf, len); 100 | memcpy(sbuf->buf + sbuf->off, buf, len); 101 | 102 | return 0; 103 | } 104 | 105 | int 106 | sbuf_append(sbuf_t *sbuf, void *buf, int len) 107 | { 108 | sbuf_extend(sbuf, len); 109 | 110 | memcpy(sbuf->buf + sbuf->len, buf, len); 111 | sbuf->len += len; 112 | 113 | return 0; 114 | } 115 | 116 | ssize_t 117 | sbuf_send(const int fd, sbuf_t *buf, ssize_t *snd) 118 | { 119 | ssize_t len; 120 | 121 | assert(buf->len <= buf->max); 122 | assert(buf->off < buf->len); 123 | 124 | *snd = 0; 125 | do { 126 | len = send(fd, buf->buf + buf->off, buf->len - buf->off, 0); 127 | if (len > 0) { 128 | *snd += len; 129 | buf->off += len; 130 | } 131 | } while (buf->off > 0 && buf->off < buf->len && len > 0); 132 | 133 | if (len == -1 && errno == EWOULDBLOCK) { 134 | len = 1; /* don't error when send block */ 135 | } 136 | 137 | return len; 138 | } 139 | 140 | ssize_t 141 | sbuf_recv(const int fd, sbuf_t *buf, ssize_t *rcv) 142 | { 143 | ssize_t len; 144 | 145 | assert(buf->len < buf->max); 146 | 147 | *rcv = 0; 148 | do { 149 | len = recv(fd, buf->buf + buf->len, buf->max - buf->len, 0); 150 | if (len > 0) { 151 | *rcv += len; 152 | buf->len += len; 153 | } 154 | } while (buf->len > 0 && buf->len < buf->max && len > 0); 155 | 156 | if (len == -1 && errno == EWOULDBLOCK) { 157 | len = 1; /* don't error when recv block */ 158 | } 159 | 160 | return len; 161 | } 162 | 163 | int 164 | cbuf_alloc(cbuf_t *cbuf, int max) 165 | { 166 | cbuf->off = 0; 167 | cbuf->len = 0; 168 | 169 | if ((cbuf->max = max) > 0 && 170 | (cbuf->buf = malloc(max * sizeof(sbuf_t))) != NULL) 171 | { 172 | return 0; 173 | } 174 | cbuf->max = 0; 175 | 176 | return -1; 177 | } 178 | 179 | void 180 | cbuf_release(cbuf_t *cbuf) 181 | { 182 | cbuf->off = 0; 183 | cbuf->len = 0; 184 | cbuf->max = 0; 185 | free(cbuf->buf); 186 | cbuf->buf = NULL; 187 | } 188 | 189 | long 190 | cbuf_get_off(cbuf_t *cbuf) 191 | { 192 | int i; 193 | long off; 194 | 195 | off = 0; 196 | for (i = 0; i < cbuf->off; i++) { 197 | off += cbuf->buf[i].len; 198 | } 199 | 200 | off += cbuf->buf[cbuf->off].off; 201 | 202 | return off; 203 | } 204 | 205 | void 206 | cbuf_set_off(cbuf_t *cbuf, long off) 207 | { 208 | int i; 209 | 210 | for (i = 0; i < cbuf->len; i++) { 211 | if (cbuf->buf[i].len > off) { 212 | cbuf->buf[i].off = off; 213 | 214 | break; 215 | } else { 216 | off -= cbuf[i].len; 217 | } 218 | } 219 | } 220 | 221 | long 222 | cbuf_get_len(cbuf_t *cbuf) 223 | { 224 | int i; 225 | long len; 226 | 227 | len = 0; 228 | for (i = 0; i < cbuf->len; i++) { 229 | len += cbuf->buf[i].len; 230 | } 231 | 232 | return len; 233 | } 234 | 235 | void 236 | cbuf_set_len(cbuf_t *cbuf, long off) 237 | { 238 | int i; 239 | long len; 240 | 241 | len = 0; 242 | for (i = 0; i < cbuf->max; i++) { 243 | if (cbuf->buf[i].max > len) { 244 | cbuf->buf[i].len = len; 245 | 246 | break; 247 | } else { 248 | len -= cbuf->buf[i].max; 249 | } 250 | } 251 | } 252 | 253 | long 254 | cbuf_get_max(cbuf_t *cbuf) 255 | { 256 | int i; 257 | long max; 258 | 259 | max = 0; 260 | for (i = 0; i < cbuf->max; i++) { 261 | max += cbuf->buf[i].max; 262 | } 263 | 264 | return max; 265 | } 266 | 267 | int 268 | cbuf_extend(cbuf_t *cbuf, int len) 269 | { 270 | sbuf_t *buf; 271 | 272 | if ((cbuf->len + len) < cbuf->max) { 273 | return 0; 274 | } 275 | 276 | if (cbuf->buf != NULL) { 277 | buf = realloc(cbuf->buf, (cbuf->max + len) * sizeof(sbuf_t)); 278 | } else { 279 | buf = malloc(len * sizeof(sbuf_t)); 280 | } 281 | if (buf != NULL) { 282 | cbuf->buf = buf; 283 | cbuf->max += len; 284 | } else { 285 | return -1; 286 | } 287 | 288 | return 0; 289 | } 290 | 291 | void 292 | cbuf_iovec(cbuf_t *cbuf, struct iovec *iov, int iovcnt, int type) 293 | { 294 | int i; 295 | int off; 296 | int len; 297 | 298 | assert((type == 0) || (type == 1) || (type == 2)); 299 | 300 | switch (type) { 301 | case 0: 302 | off = 0; 303 | len = cbuf->len; 304 | 305 | break; 306 | case 1: 307 | off = cbuf->off; 308 | len = cbuf->len; 309 | 310 | break; 311 | case 2: 312 | off = cbuf->len; 313 | len = cbuf->max; 314 | 315 | break; 316 | } 317 | 318 | for (i = 0; i < iovcnt && off < len; i++, off++) { 319 | iov[i].iov_len = cbuf->buf[off].len; 320 | iov[i].iov_base = cbuf->buf[off].buf + cbuf->buf[off].off; 321 | } 322 | } 323 | 324 | ssize_t 325 | cbuf_send(const int fd, cbuf_t *buf, ssize_t *snd) 326 | { 327 | long off; 328 | ssize_t n; 329 | 330 | int iovcnt; 331 | struct iovec iov[IOVCNT_DEFAULT]; 332 | 333 | 334 | assert(buf->len <= buf->max); 335 | assert(buf->off < buf->len); 336 | 337 | *snd = 0; 338 | iovcnt = IOVCNT_DEFAULT; 339 | off = cbuf_get_off(buf); 340 | do { 341 | if ((buf->len - buf->off) < iovcnt) { 342 | iovcnt = buf->len - buf->off; 343 | } 344 | cbuf_iovec(buf, iov, iovcnt, 1); 345 | 346 | n = writev(fd, iov, iovcnt); 347 | if (n > 0) { 348 | *snd += n; 349 | off += n; 350 | } 351 | } while (off > 0 && off < cbuf_get_len(buf) && n > 0); 352 | 353 | cbuf_set_off(buf, off); 354 | 355 | if (n == -1 && errno == EWOULDBLOCK) { 356 | n = 1; /* don't error when writev block */ 357 | } 358 | 359 | return n; 360 | } 361 | 362 | ssize_t 363 | cbuf_recv(const int fd, cbuf_t *buf, ssize_t *rcv) 364 | { 365 | long len; 366 | ssize_t n; 367 | 368 | int iovcnt; 369 | struct iovec iov[IOVCNT_DEFAULT]; 370 | 371 | assert(buf->len < buf->max); 372 | 373 | *rcv = 0; 374 | iovcnt = IOVCNT_DEFAULT; 375 | len = cbuf_get_len(buf); 376 | do { 377 | if ((buf->len - buf->off) < iovcnt) { 378 | iovcnt = buf->len - buf->off; 379 | } 380 | cbuf_iovec(buf, iov, iovcnt, 2); 381 | 382 | n = readv(fd, iov, iovcnt); 383 | if (n > 0) { 384 | *rcv += n; 385 | len += n; 386 | } 387 | } while (n > 0 && len < cbuf_get_max(buf) && n > 0); 388 | 389 | cbuf_set_len(buf, len); 390 | 391 | if (n == -1 && errno == EWOULDBLOCK) { 392 | n = 1; /* don't error when readv block */ 393 | } 394 | 395 | return n; 396 | } 397 | -------------------------------------------------------------------------------- /loop.c: -------------------------------------------------------------------------------- 1 | #include "loop.h" 2 | 3 | #include "heapq.h" 4 | #include "hashtable.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define USEC_PER_SEC 1000000 12 | #define NSEC_PER_SEC 1000000000 13 | #define NSEC_PER_USEC 1000 14 | 15 | #include "mux.h" 16 | 17 | typedef struct event { 18 | int id; 19 | int tag; 20 | int type; 21 | int flag; 22 | 23 | void *args; 24 | loop_proc_t *proc; 25 | } event_t; 26 | 27 | typedef struct timer { 28 | event_t event; 29 | long seconds; 30 | int nanoseconds; 31 | } timer_t; 32 | 33 | struct loop { 34 | int stop; 35 | 36 | int event_len; 37 | int event_max; 38 | event_t *event; 39 | 40 | int timer_len; 41 | int timer_max; 42 | timer_t *timer; 43 | 44 | void **timer_table; 45 | 46 | struct mux *mux; 47 | }; 48 | 49 | static int 50 | timer_hash(const void *a) 51 | { 52 | const timer_t *timer = a; 53 | return (timer->event.id + timer->event.tag + timer->event.type); 54 | } 55 | 56 | static int 57 | timer_compare(const void *a, const void *b) 58 | { 59 | const timer_t *timera = a; 60 | const timer_t *timerb = b; 61 | 62 | if (timera->event.id == timerb->event.id && 63 | timera->event.tag == timerb->event.tag && 64 | timera->event.type == timerb->event.type) 65 | { 66 | return 0; 67 | } 68 | 69 | if (timera->seconds == timerb->seconds) { 70 | if (timera->nanoseconds > timerb->nanoseconds) { 71 | return 1; 72 | } 73 | if (timera->nanoseconds < timerb->nanoseconds) { 74 | return -1; 75 | } 76 | return 1; 77 | } 78 | if (timera->seconds > timerb->seconds) { 79 | return 1; 80 | } 81 | if (timera->seconds < timerb->seconds) { 82 | return -1; 83 | } 84 | 85 | return 1; 86 | } 87 | 88 | int 89 | timer_swap(void *args, void *a, void *b) 90 | { 91 | timer_t timer; 92 | 93 | timer_t *timera = a; 94 | timer_t *timerb = b; 95 | 96 | loop_t *loop = args; 97 | 98 | timer = *timera; 99 | *timera = *timerb; 100 | *timerb = timer; 101 | 102 | hashtable_set(loop->timer_table, loop->timer_max * 2, timera, 103 | timer_hash, timer_compare); 104 | 105 | hashtable_set(loop->timer_table, loop->timer_max * 2, timerb, 106 | timer_hash, timer_compare); 107 | 108 | return 0; 109 | } 110 | 111 | int 112 | loop_push_timer(loop_t *loop, timer_t *timer) 113 | { 114 | if (loop->timer_len == loop->timer_max) { 115 | return LOOP_ERR; 116 | } 117 | 118 | loop->timer_len += 1; 119 | heapq_push(loop->timer, loop->timer_len, sizeof(*timer), timer, 120 | loop, timer_swap, timer_compare); 121 | 122 | hashtable_set(loop->timer_table, loop->timer_max * 2, timer, 123 | timer_hash, timer_compare); 124 | 125 | return LOOP_OK; 126 | } 127 | 128 | int 129 | loop_pop_timer(loop_t *loop, timer_t *timer) 130 | { 131 | while (loop->timer_len) { 132 | heapq_pop(loop->timer, loop->timer_len--, sizeof(*timer), timer, 133 | loop, timer_swap, timer_compare); 134 | if (!(timer->event.flag & LOOP_FLAGS_INVALID)) { 135 | return LOOP_OK; 136 | } 137 | } 138 | 139 | return LOOP_ERR; 140 | } 141 | 142 | int 143 | loop_set_timer(loop_t *loop, int id, int tag, int type, int flag, 144 | long sec, int nsec, loop_proc_t *proc, void *args) 145 | { 146 | if (loop->timer_len == loop->timer_max) { 147 | return LOOP_ERR; 148 | } 149 | 150 | timer_t *timer; 151 | struct timeval now; 152 | 153 | gettimeofday(&now, NULL); 154 | 155 | timer = &loop->timer[loop->timer_len]; 156 | timer->event.id = id; 157 | timer->event.tag = tag; 158 | timer->event.type = type; 159 | timer->event.flag = flag; 160 | timer->event.proc = proc; 161 | timer->event.args = args; 162 | timer->seconds = now.tv_sec + sec; 163 | timer->nanoseconds = now.tv_usec * NSEC_PER_USEC + nsec; 164 | 165 | if (timer->nanoseconds >= NSEC_PER_SEC) { 166 | timer->nanoseconds -= NSEC_PER_SEC; 167 | timer->seconds++; 168 | } 169 | 170 | return loop_push_timer(loop, timer); 171 | } 172 | 173 | int 174 | loop_del_timer(loop_t *loop, int id, int tag, int type) 175 | { 176 | timer_t timer; 177 | timer_t *p; 178 | 179 | timer.event.id = id; 180 | timer.event.tag = tag; 181 | timer.event.type = type; 182 | 183 | p = hashtable_get(loop->timer_table, loop->timer_max * 2, &timer, 184 | timer_hash, timer_compare); 185 | 186 | if (p == NULL) { 187 | return LOOP_ERR; 188 | } 189 | 190 | p->event.flag |= LOOP_FLAGS_INVALID; 191 | 192 | return LOOP_OK; 193 | } 194 | 195 | int 196 | loop_timer_nearest(loop_t *loop, timer_t *timer) 197 | { 198 | if (loop->timer_len > 0) { 199 | *timer = loop->timer[0]; 200 | 201 | return LOOP_OK; 202 | } 203 | 204 | return LOOP_ERR; 205 | } 206 | 207 | int 208 | loop_timer_dispatch(loop_t *loop) 209 | { 210 | int i; 211 | timer_t timer; 212 | struct timeval now; 213 | 214 | for (i = 0;;i++) { 215 | if (loop_pop_timer(loop, &timer) != LOOP_OK) { 216 | break; 217 | } 218 | 219 | gettimeofday(&now, NULL); 220 | if (now.tv_sec > timer.seconds || 221 | (now.tv_sec == timer.seconds && 222 | now.tv_usec >= timer.nanoseconds / NSEC_PER_USEC)) 223 | { 224 | timer.event.proc(loop, 225 | timer.event.id, 226 | timer.event.tag, 227 | timer.event.type, 228 | timer.event.flag, 229 | timer.event.args); 230 | } else { 231 | loop_push_timer(loop, &timer); 232 | 233 | break; 234 | } 235 | } 236 | 237 | return i; 238 | } 239 | 240 | loop_t * 241 | loop_open(int event_max, int timer_max) 242 | { 243 | loop_t *loop = NULL; 244 | 245 | if (event_max < 0 || timer_max < 0) { 246 | return NULL; 247 | } 248 | 249 | loop = malloc(sizeof(loop_t)); 250 | if (loop == NULL) { 251 | return NULL; 252 | } 253 | 254 | memset(loop, 0, sizeof(loop_t)); 255 | 256 | loop->event = calloc(event_max, sizeof(event_t)); 257 | loop->event_max = event_max; 258 | if (loop->event == NULL) { 259 | goto err; 260 | } 261 | 262 | loop->timer = calloc(timer_max, sizeof(timer_t)); 263 | loop->timer_max = timer_max; 264 | if (loop->timer == NULL) { 265 | goto err; 266 | } 267 | 268 | loop->timer_table = calloc(timer_max * 2, sizeof(void *)); 269 | if (loop->timer_table == NULL) { 270 | goto err; 271 | } 272 | 273 | if (mux_open(loop, event_max) != LOOP_OK) { 274 | goto err; 275 | } 276 | 277 | return loop; 278 | err: 279 | free(loop->event); 280 | free(loop->timer); 281 | free(loop->timer_table); 282 | mux_close(loop); 283 | free(loop); 284 | 285 | return NULL; 286 | } 287 | 288 | void 289 | loop_close(loop_t *loop) 290 | { 291 | free(loop->event); 292 | free(loop->timer); 293 | free(loop->timer_table); 294 | mux_close(loop); 295 | free(loop); 296 | } 297 | 298 | int 299 | loop_set_event(loop_t *loop, int fd, int tag, int type, int flag, 300 | loop_proc_t *proc, void *args) 301 | { 302 | if (fd >= loop->event_max) { 303 | return LOOP_ERR; 304 | } 305 | 306 | if ((type & LOOP_WRITE) == 0 && (type & LOOP_READ) == 0) { 307 | return LOOP_ERR; 308 | } 309 | 310 | loop->event[fd].id = fd; 311 | loop->event[fd].tag = tag; 312 | loop->event[fd].flag = flag; 313 | loop->event[fd].args = args; 314 | loop->event[fd].proc = proc; 315 | 316 | if (mux_set_event(loop, fd, tag, type) != LOOP_OK) { 317 | return LOOP_ERR; 318 | } 319 | loop->event[fd].type |= type; 320 | 321 | return LOOP_OK; 322 | } 323 | 324 | int 325 | loop_del_event(loop_t *loop, int fd, int type) 326 | { 327 | if ((type & LOOP_WRITE) == 0 && (type & LOOP_READ) == 0) { 328 | return LOOP_ERR; 329 | } 330 | 331 | if (mux_del_event(loop, fd, type) != LOOP_OK) { 332 | return LOOP_ERR; 333 | } 334 | 335 | loop->event[fd].type &= ~type; 336 | 337 | return LOOP_OK; 338 | } 339 | 340 | int 341 | loop_start(loop_t *loop) 342 | { 343 | timer_t *timeout; 344 | timer_t timer; 345 | 346 | struct timeval now; 347 | 348 | loop->stop = 0; 349 | for (;!loop->stop;) { 350 | timeout = NULL; 351 | if (loop_timer_nearest(loop, &timer) == LOOP_OK) { 352 | gettimeofday(&now, NULL); 353 | 354 | if (now.tv_sec > timer.seconds || 355 | (now.tv_sec == timer.seconds && 356 | now.tv_usec >= timer.nanoseconds / NSEC_PER_USEC)) 357 | { 358 | loop_timer_dispatch(loop); 359 | 360 | continue; 361 | } 362 | timer.nanoseconds -= now.tv_usec * NSEC_PER_USEC; 363 | if (timer.nanoseconds < 0) { 364 | timer.nanoseconds += NSEC_PER_SEC; 365 | timer.seconds--; 366 | } 367 | if (timer.seconds > now.tv_sec) { 368 | timer.seconds -= now.tv_sec; 369 | } else { 370 | timer.seconds = 0; 371 | } 372 | timeout = &timer; 373 | } 374 | if (mux_polling(loop, timeout) != LOOP_OK) { 375 | return LOOP_ERR; 376 | } 377 | } 378 | 379 | /* loop stoped */ 380 | return LOOP_OK; 381 | } 382 | 383 | int 384 | loop_stop(loop_t *loop) 385 | { 386 | loop->stop = 1; 387 | 388 | return LOOP_OK; 389 | } 390 | 391 | #if defined(USE_KQUEUE) 392 | #include "mux-kqueue.c" 393 | #elif defined(USE_EPOLL) 394 | #include "mux-epoll.c" 395 | #else 396 | #include "mux-select.c" 397 | #endif 398 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int 17 | socket_addr(socket_addr_t *addr, char *hostname, int port) 18 | { 19 | char portstr[15]; 20 | 21 | struct addrinfo *res; 22 | struct addrinfo hints; 23 | 24 | snprintf(portstr, sizeof(portstr), "%d", port); 25 | portstr[sizeof(portstr) - 1] = 0; 26 | 27 | memset(&hints, 0, sizeof(hints)); 28 | 29 | hints.ai_flags = AI_PASSIVE; 30 | hints.ai_socktype = SOCK_STREAM; 31 | 32 | if(getaddrinfo(hostname, portstr, &hints, &res) == -1 || res == NULL) { 33 | return -1; 34 | } 35 | 36 | if (res->ai_addrlen > sizeof(addr->addr)) { 37 | return -1; 38 | } 39 | 40 | addr->addrlen = res->ai_addrlen; 41 | memcpy(&addr->addr, res->ai_addr, res->ai_addrlen); 42 | 43 | freeaddrinfo(res); 44 | 45 | return 0; 46 | } 47 | 48 | int 49 | socket_addr_str(socket_addr_t *addr, char *addrstr, int *port) 50 | { 51 | switch (addr->addr.sa.sa_family) { 52 | case AF_INET: 53 | *port = ntohs(addr->addr.si.sin_port); 54 | if (inet_ntop(addr->addr.sa.sa_family, 55 | &addr->addr.si.sin_addr, 56 | addrstr, 57 | SOCKET_ADDR_STRLEN) != NULL) 58 | { 59 | return 0; 60 | } 61 | break; 62 | case AF_INET6: 63 | *port = ntohs(addr->addr.s6.sin6_port); 64 | if (inet_ntop(addr->addr.sa.sa_family, 65 | &addr->addr.s6.sin6_addr, 66 | addrstr, 67 | SOCKET_ADDR_STRLEN) != NULL) 68 | { 69 | return 0; 70 | } 71 | break; 72 | } 73 | return -1; 74 | } 75 | 76 | int 77 | socket_open(int domain, int type, int protocol) 78 | { 79 | return socket(domain, type, protocol); 80 | } 81 | 82 | int 83 | socket_set_nonblock(int sockfd) 84 | { 85 | return fcntl(sockfd, F_SETFL, O_NONBLOCK); 86 | } 87 | 88 | int 89 | socket_set_reuseaddr(int sockfd) 90 | { 91 | int reuseaddr; 92 | reuseaddr = 1; 93 | return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); 94 | } 95 | 96 | int 97 | socket_set_reuseport(int sockfd) 98 | { 99 | #ifdef SO_REUSEPORT 100 | int reuseport; 101 | reuseport = 1; 102 | return setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(reuseport)); 103 | #else 104 | # warning socket_set_reuseport require SO_REUSEPORT 105 | #endif /* SO_REUSEPORT */ 106 | return -1; 107 | } 108 | 109 | int 110 | socket_set_tcp_nodelay(int sockfd) 111 | { 112 | int tcp_nodelay; 113 | tcp_nodelay = 1; 114 | return setsockopt(sockfd, SOL_SOCKET, TCP_NODELAY, &tcp_nodelay, sizeof(tcp_nodelay)); 115 | } 116 | 117 | int 118 | socket_set_tcp_nopush(int sockfd) 119 | { 120 | int tcp_nopush; 121 | tcp_nopush = 1; 122 | #ifdef TCP_NOPUSH 123 | return setsockopt(sockfd, SOL_SOCKET, TCP_NOPUSH, &tcp_nopush, sizeof(tcp_nopush)); 124 | #elif defined(TCP_CORK) 125 | return setsockopt(sockfd, SOL_SOCKET, TCP_CORK, &tcp_nopush, sizeof(tcp_nopush)); 126 | #else 127 | # warning socket_set_tcp_nopush require TCP_NOPUSH or TCP_CORK 128 | #endif 129 | } 130 | 131 | int 132 | socket_set_pktinfo(int sockfd) 133 | { 134 | int rc; 135 | int pktinfo; 136 | socket_addr_t addr; 137 | 138 | addr.addrlen = sizeof(addr.addr); 139 | if ((rc = getsockname(sockfd, &addr.addr.sa, &addr.addrlen)) == -1) 140 | return rc; 141 | 142 | pktinfo = 1; 143 | 144 | switch (addr.addr.sa.sa_family) { 145 | case AF_INET: 146 | #ifdef IP_RECVDSTADDR 147 | return setsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR, &pktinfo, sizeof(pktinfo)); 148 | #elif defined(IP_RECVPKTINFO) 149 | return setsockopt(sockfd, IPPROTO_IP, IP_RECVPKTINFO, &pktinfo, sizeof(pktinfo)); 150 | #elif defined(IP_PKTINFO) 151 | return setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo)); 152 | #else 153 | # warning socket_set_pktinfo require IP_RECVDSTADDR, IP_RECVPKTINFO or IP_PKTINFO 154 | #endif 155 | 156 | case AF_INET6: 157 | #ifdef IPV6_RECVPKTINFO 158 | return setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &pktinfo, sizeof(pktinfo)); 159 | #elif defined(IPV6_PKTINFO) 160 | return setsockopt(sockfd, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, sizeof(pktinfo)); 161 | #else 162 | # warning socket_set_pktinfo require IPV6_RECVDSTADDR or IPV6_PKTINFO 163 | #endif 164 | 165 | } 166 | return -1; 167 | } 168 | 169 | int 170 | socket_bind(int sockfd, socket_addr_t *addr) 171 | { 172 | return bind(sockfd, &addr->addr.sa, addr->addrlen); 173 | } 174 | 175 | int 176 | socket_listen(int sockfd, int backlog) 177 | { 178 | return listen(sockfd, backlog); 179 | } 180 | 181 | int 182 | socket_connect(int sockfd, socket_addr_t *addr) 183 | { 184 | return connect(sockfd, &addr->addr.sa, addr->addrlen); 185 | } 186 | 187 | int 188 | socket_accept(int sockfd, socket_addr_t *addr) 189 | { 190 | addr->addrlen = sizeof(addr->addr); 191 | return accept(sockfd, &addr->addr.sa, &addr->addrlen); 192 | } 193 | 194 | ssize_t 195 | socket_recvfromto(int sockfd, void *buf, size_t len, int flags, 196 | socket_addr_t *src, socket_addr_t *dst) 197 | { 198 | #ifdef IP_PKTINFO 199 | struct iovec iov; 200 | struct msghdr msg; 201 | struct cmsghdr *cmsg; 202 | #ifdef IPV6_PKTINFO 203 | char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; 204 | #else /* IPV6_PKTINFO */ 205 | char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))]; 206 | #endif /* IPV6_PKTINFO */ 207 | ssize_t recvlen; 208 | 209 | iov.iov_len = len; 210 | iov.iov_base = buf; 211 | 212 | src->addrlen = sizeof(src->addr); 213 | memset(&msg, 0, sizeof(msg)); 214 | msg.msg_name = &src->addr; 215 | msg.msg_namelen = src->addrlen; 216 | msg.msg_iov = &iov; 217 | msg.msg_iovlen = 1; 218 | msg.msg_control = cmsgbuf; 219 | msg.msg_controllen = sizeof(cmsgbuf); 220 | 221 | recvlen = recvmsg(sockfd, &msg, flags); 222 | if (recvlen < 0) { 223 | return recvlen; 224 | } 225 | 226 | memset(dst, 0, sizeof(*dst)); 227 | 228 | /* local port */ 229 | dst->addrlen = sizeof(dst->addr); 230 | getsockname(sockfd, &dst->addr.sa, &dst->addrlen); 231 | 232 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 233 | if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { 234 | struct in_pktinfo *pktinfo; 235 | 236 | pktinfo = (struct in_pktinfo *)(CMSG_DATA(cmsg)); 237 | 238 | dst->addrlen = sizeof(dst->addr.s6); 239 | dst->addr.si.sin_family = AF_INET; 240 | #ifdef HAVE_SIN_LEN 241 | dst->addr.si.sin_len = sizeof(dst->addr.si); 242 | #endif 243 | dst->addr.si.sin_addr = pktinfo->ipi_addr; 244 | 245 | break; 246 | } 247 | #ifdef IPV6_PKTINFO 248 | if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { 249 | struct in6_pktinfo *pktinfo; 250 | 251 | pktinfo = (struct in6_pktinfo *)(CMSG_DATA(cmsg)); 252 | 253 | dst->addrlen = sizeof(dst->addr.s6); 254 | dst->addr.s6.sin6_family = AF_INET6; 255 | #ifdef HAVE_SIN6_LEN 256 | dst->addr.s6.sin6_len = sizeof(dst->addr.s6); 257 | #endif 258 | dst->addr.s6.sin6_addr = pktinfo->ipi6_addr; 259 | if (IN6_IS_ADDR_LINKLOCAL(&pktinfo->ipi6_addr)) 260 | dst->addr.s6.sin6_scope_id = pktinfo->ipi6_ifindex; 261 | 262 | break; 263 | } 264 | #endif /* IPV6_PKTINFO */ 265 | } 266 | return recvlen; 267 | #else /* IP_PKTINFO */ 268 | memset(dst, 0, sizeof(*dst)); 269 | return recvfrom(sockfd, buffer, length, flags, &src->addr.sa, src->addrlen); 270 | #endif /* IP_PKTINFO */ 271 | } 272 | 273 | ssize_t 274 | socket_sendtofrom(int sockfd, void *buf, size_t len, int flags, 275 | socket_addr_t *dst, socket_addr_t *src) 276 | { 277 | #ifdef IP_PKTINFO 278 | struct iovec iov; 279 | struct msghdr msg; 280 | struct cmsghdr *cmsg; 281 | #ifdef IPV6_PKTINFO 282 | char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; 283 | #else /* IPV6_PKTINFO */ 284 | char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))]; 285 | #endif /* IPV6_PKTINFO */ 286 | 287 | iov.iov_len = len; 288 | iov.iov_base = buf; 289 | 290 | memset(&msg, 0, sizeof(msg)); 291 | memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 292 | 293 | msg.msg_name = &dst->addr; 294 | #ifdef HAVE_SA_LEN 295 | msg.msg_namelen = dst->addr.sa.sa_len; 296 | #else 297 | msg.msg_namelen = sizeof(dst->addr); 298 | #endif 299 | msg.msg_iov = &iov; 300 | msg.msg_iovlen = 1; 301 | msg.msg_control = cmsgbuf; 302 | msg.msg_controllen = sizeof(cmsgbuf); 303 | msg.msg_flags = flags; 304 | 305 | cmsg = (struct cmsghdr *)cmsgbuf; 306 | switch (dst->addr.sa.sa_family) { 307 | case AF_INET: 308 | { 309 | struct in_pktinfo *pktinfo; 310 | 311 | cmsg->cmsg_level = IPPROTO_IP; 312 | cmsg->cmsg_type = IP_PKTINFO; 313 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 314 | pktinfo = (struct in_pktinfo *)(CMSG_DATA(cmsg)); 315 | pktinfo->ipi_spec_dst = src->addr.si.sin_addr; 316 | 317 | break; 318 | } 319 | #ifdef IPV6_PKTINFO 320 | case AF_INET6: 321 | { 322 | int ifindex; 323 | struct in6_pktinfo *pktinfo; 324 | 325 | if (IN6_IS_ADDR_LINKLOCAL(&src->addr.s6.sin6_addr) || 326 | IN6_IS_ADDR_MULTICAST(&src->addr.s6.sin6_addr)) 327 | { 328 | ifindex = src->addr.s6.sin6_scope_id; 329 | } else { 330 | ifindex = 0; 331 | } 332 | 333 | msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 334 | cmsg->cmsg_level = IPPROTO_IPV6; 335 | cmsg->cmsg_type = IPV6_PKTINFO; 336 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 337 | pktinfo = (struct in6_pktinfo *)(CMSG_DATA(cmsg)); 338 | pktinfo->ipi6_addr = src->addr.s6.sin6_addr; 339 | pktinfo->ipi6_ifindex = ifindex; 340 | 341 | break; 342 | } 343 | } 344 | #endif /* IPV6_PKTINFO */ 345 | return sendmsg(sockfd, &msg, flags); 346 | #else /* IP_PKTINFO */ 347 | memset(src, 0, sizeof(*src)); 348 | return sendto(sockfd, buffer, length, flags, &dst->addr.sa, dst->addrlen); 349 | #endif /* IP_PKTINFO */ 350 | } 351 | 352 | int 353 | socket_close(int sockfd) 354 | { 355 | return close(sockfd); 356 | } 357 | --------------------------------------------------------------------------------