├── .coverage.sh ├── .gitignore ├── .lint.sh ├── .travis.yml ├── COPYING ├── Makefile.am ├── README.md ├── configure.ac ├── contrib ├── archlinux │ └── PKGBUILD └── systemd │ ├── ioclient@.service │ ├── ioredir@.service │ └── ioserver@.service ├── man └── iosocks.8 ├── src ├── Makefile.am ├── async_connect.c ├── async_connect.h ├── async_resolv.c ├── async_resolv.h ├── conf.c ├── conf.h ├── crypto.c ├── crypto.h ├── ev.c ├── ev.h ├── ev_epoll.c ├── ev_kqueue.c ├── ev_poll.c ├── ev_port.c ├── ev_select.c ├── ev_vars.h ├── ev_win32.c ├── ev_wrap.h ├── ioclient.c ├── ioredir.c ├── ioserver.c ├── log.c ├── log.h ├── md5.c ├── md5.h ├── relay.c ├── relay.h ├── socks5.c ├── socks5.h ├── utils.c └── utils.h └── test ├── test.conf └── test.py /.coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export CC=gcc 6 | ./configure --enable-debug --enable-coverage 7 | make 8 | 9 | sudo iptables -t nat -N iosocks 10 | sudo iptables -t nat -A iosocks -d 0.0.0.0/8 -j RETURN 11 | sudo iptables -t nat -A iosocks -d 10.0.0.0/8 -j RETURN 12 | sudo iptables -t nat -A iosocks -d 127.0.0.0/8 -j RETURN 13 | sudo iptables -t nat -A iosocks -d 169.254.0.0/16 -j RETURN 14 | sudo iptables -t nat -A iosocks -d 172.16.0.0/12 -j RETURN 15 | sudo iptables -t nat -A iosocks -d 192.168.0.0/16 -j RETURN 16 | sudo iptables -t nat -A iosocks -d 224.0.0.0/4 -j RETURN 17 | sudo iptables -t nat -A iosocks -d 240.0.0.0/4 -j RETURN 18 | sudo iptables -t nat -A iosocks -p tcp -j REDIRECT --to-ports 1081 19 | sudo iptables -t nat -A OUTPUT -p tcp --sport 2000 -j iosocks 20 | sudo iptables -t nat -A OUTPUT -p tcp --sport 2001 -j iosocks 21 | 22 | test/test.py 23 | 24 | sudo iptables -t nat -D OUTPUT -p tcp --sport 2000 -j iosocks 25 | sudo iptables -t nat -D OUTPUT -p tcp --sport 2001 -j iosocks 26 | sudo iptables -t nat -F iosocks 27 | sudo iptables -t nat -X iosocks 28 | 29 | cd src 30 | rm -f *.html 31 | gcov *.c 32 | gcovr -r . --html --html-details -o index.html 33 | cd .. 34 | 35 | COVERAGE=$(gcovr -r . | grep TOTAL | awk '{print $4}' | cut -c 1-2) 36 | if [ ${COVERAGE} -lt 60 ]; then 37 | URL="https://img.shields.io/badge/coverage-${COVERAGE}%-red.svg?style=flat" 38 | elif [ ${COVERAGE} -lt 80 ]; then 39 | URL="https://img.shields.io/badge/coverage-${COVERAGE}%-yellow.svg?style=flat" 40 | else 41 | URL="https://img.shields.io/badge/coverage-${COVERAGE}%-green.svg?style=flat" 42 | fi 43 | curl -s -o .coverage.svg ${URL} 44 | 45 | make distclean 46 | rm -f src/*.gcov src/*.gcda src/*.gcno 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | auto/ 2 | autom4te.cache/ 3 | contrib/archlinux/iosocks/ 4 | .deps/ 5 | .lint/ 6 | aclocal.m4 7 | config.* 8 | configure 9 | Makefile 10 | Makefile.in 11 | src/Makefile 12 | src/Makefile.in 13 | src/ioserver 14 | src/ioclient 15 | src/ioredir 16 | stamp-h1 17 | *~ 18 | *.gcda 19 | *.gcno 20 | *.gcov 21 | *.html 22 | *.o 23 | *.pro 24 | *.pro.user 25 | *.svg 26 | *.tar.gz 27 | *.tar.xz 28 | -------------------------------------------------------------------------------- /.lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export CC=/usr/lib/clang-analyzer/scan-build/ccc-analyzer 6 | ./configure --enable-debug 7 | 8 | rm -rf .lint 9 | scan-build -o .lint -analyze-headers --use-cc=clang make 10 | cd .lint 11 | DIR=$(ls) 12 | if [[ ${DIR} ]]; then 13 | mv ${DIR}/* ./ 14 | rmdir ${DIR} 15 | fi 16 | cd .. 17 | 18 | if [ -f .lint/index.html ]; then 19 | BUG=$(cat .lint/index.html | grep 'All Bugs' | tr '><' '\n' | grep '[0-9]') 20 | else 21 | echo 'No bugs found.' > .lint/index.html 22 | BUG=0 23 | fi 24 | if [ ${BUG} -lt 3 ]; then 25 | COLOR=brightgreen 26 | elif [ ${BUG} -lt 6 ]; then 27 | COLOR=yellow 28 | else 29 | COLOR=red 30 | fi 31 | if [ ${BUG} -lt 1 ]; then 32 | BUG="${BUG}%20bug" 33 | else 34 | BUG="${BUG}%20bugs" 35 | fi 36 | curl -s -o .lint.svg "https://img.shields.io/badge/lint-${BUG}-${COLOR}.svg?style=flat" 37 | 38 | make distclean 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | cache: apt 3 | compiler: 4 | - gcc 5 | - clang 6 | before_install: 7 | - sudo apt-get update -qq 8 | install: 9 | - sudo apt-get install -y libev-dev 10 | before_script: 11 | - "autoreconf -i" 12 | script: 13 | - "./configure --prefix=/usr" 14 | - "make" 15 | - "make DESTDIR=/tmp/dest install" 16 | branches: 17 | only: 18 | - master 19 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | EXTRA_DIST = man/iosocks.8 contrib/systemd/ioserver@.service \ 4 | contrib/systemd/ioclient@.service \ 5 | contrib/systemd/ioredir@.service 6 | 7 | man_MANS = man/iosocks.8 8 | 9 | systemddir=$(libdir)/systemd/system 10 | systemd_DATA=contrib/systemd/ioserver@.service \ 11 | contrib/systemd/ioclient@.service \ 12 | contrib/systemd/ioredir@.service 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iosocks # 2 | 3 | [![Release](https://img.shields.io/github/release/XiaoxiaoPu/iosocks.svg?style=flat)](https://github.com/XiaoxiaoPu/iosocks/releases/latest) 4 | [![License](https://img.shields.io/badge/license-GPL%203-blue.svg?style=flat)](http://www.gnu.org/licenses/gpl.html) 5 | [![Build Status](https://travis-ci.org/XiaoxiaoPu/iosocks.svg?branch=master)](https://travis-ci.org/XiaoxiaoPu/iosocks) 6 | [![Build Status](https://jenkins.xiaoxiao.im/buildStatus/icon?job=iosocks)](https://jenkins.xiaoxiao.im/job/iosocks/) 7 | 8 | A lightweight tunnel proxy, provides a SOCKS5 proxy and a transparent TCP proxy. 9 | 10 | ## Build ## 11 | 12 | 1. install libev (optional) 13 | 14 | ```bash 15 | # Archlinux 16 | sudo pacman -S libev 17 | # CentOS 18 | sudo yum install libev-devel 19 | # Debian/Ubuntu 20 | sudo apt-get install libev-dev 21 | ``` 22 | 23 | 2. configure and make 24 | 25 | ```bash 26 | autoreconf -if 27 | ./configure --prefix=/usr --sysconfdir=/etc 28 | make 29 | ``` 30 | 31 | 3. install 32 | 33 | ```bash 34 | sudo make install 35 | ``` 36 | 37 | ## Cross compile ## 38 | 39 | 1. setup cross compile tool chain 40 | 41 | 2. build 42 | 43 | ```bash 44 | autoreconf -if 45 | ./configure --host=arm-unknown-linux-gnueabihf \ 46 | --prefix=/usr --sysconfdir=/etc 47 | ``` 48 | 49 | ## Usage ## 50 | 51 | See man:iosocks(8). 52 | 53 | ## Advanced usage ## 54 | 55 | ioredir prorides a transpant TCP proxy. This feature requires Linux netfilter's NAT function. 56 | 57 | ```bash 58 | iptables -t nat -N iosocks 59 | iptables -t nat -A iosocks -d ${server} -j RETURN 60 | iptables -t nat -A iosocks -d 0.0.0.0/8 -j RETURN 61 | iptables -t nat -A iosocks -d 10.0.0.0/8 -j RETURN 62 | iptables -t nat -A iosocks -d 127.0.0.0/8 -j RETURN 63 | iptables -t nat -A iosocks -d 169.254.0.0/16 -j RETURN 64 | iptables -t nat -A iosocks -d 172.16.0.0/12 -j RETURN 65 | iptables -t nat -A iosocks -d 192.168.0.0/16 -j RETURN 66 | iptables -t nat -A iosocks -d 224.0.0.0/4 -j RETURN 67 | iptables -t nat -A iosocks -d 240.0.0.0/4 -j RETURN 68 | iptables -t nat -A iosocks -p tcp -j REDIRECT --to-ports 1081 69 | iptables -t nat -A OUTPUT -p tcp -j iosocks 70 | iptables -t nat -A PREROUTING -p tcp -j iosocks 71 | ``` 72 | 73 | ## License ## 74 | 75 | Copyright (C) 2014 - 2015, Xiaoxiao 76 | 77 | This program is free software: you can redistribute it and/or modify 78 | it under the terms of the GNU General Public License as published by 79 | the Free Software Foundation, either version 3 of the License, or 80 | (at your option) any later version. 81 | 82 | This program is distributed in the hope that it will be useful, 83 | but WITHOUT ANY WARRANTY; without even the implied warranty of 84 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 85 | GNU General Public License for more details. 86 | 87 | You should have received a copy of the GNU General Public License 88 | along with this program. If not, see . 89 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_INIT([iosocks], [1.3.0], [https://github.com/XiaoxiaoPu/iosocks/issues], [iosocks], [https://github.com/XiaoxiaoPu/iosocks]) 5 | AC_CONFIG_SRCDIR([iosocks]) 6 | AC_CONFIG_SRCDIR([config.h.in]) 7 | AC_CONFIG_HEADERS([config.h]) 8 | 9 | AC_CONFIG_AUX_DIR(auto) 10 | AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 11 | 12 | # Checks for programs. 13 | AC_PROG_CC_C99 14 | AC_PROG_INSTALL 15 | 16 | # Custom options 17 | AC_ARG_ENABLE( 18 | [debug], 19 | [AS_HELP_STRING([--enable-debug], [build with additional debugging code])], 20 | [CFLAGS="$CFLAGS -g -DDEBUG -O0"]) 21 | AM_CONDITIONAL(DEBUG, test x"$debug" = x"true") 22 | AC_ARG_ENABLE( 23 | [coverage], 24 | [AS_HELP_STRING([--enable-coverage], [enable coverage analysis])], 25 | [CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"]) 26 | 27 | # Checks for libraries. 28 | LIB_ANL="-lanl" 29 | LIB_EV="-lev" 30 | AC_CHECK_LIB([anl], [getaddrinfo_a], [AC_SUBST(LIB_ANL)]) 31 | AC_CHECK_LIB([ev], [ev_default_loop], [AC_SUBST(LIB_EV)], [libev=no]) 32 | AM_CONDITIONAL(BUILD_EV, test x"$libev" = x"no") 33 | 34 | # Checks for header files. 35 | AC_HEADER_ASSERT 36 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h grp.h netdb.h netinet/in.h pwd.h stddef.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) 37 | AC_CHECK_HEADERS([linux/if.h linux/netfilter_ipv4.h linux/netfilter_ipv6/ip6_tables.h], 38 | [], [AC_MSG_ERROR([Missing netfilter headers])], 39 | [[ 40 | #if HAVE_LIMITS_H 41 | #include 42 | #endif 43 | /* Netfilter ip(6)tables v1.4.0 has broken headers */ 44 | #if HAVE_NETINET_IN_H 45 | #include 46 | #endif 47 | #if HAVE_LINUX_IF_H 48 | #include 49 | #endif 50 | #if HAVE_SYS_SOCKET_H 51 | #include 52 | #endif 53 | ]]) 54 | 55 | # Checks for typedefs, structures, and compiler characteristics. 56 | AC_C_INLINE 57 | AC_TYPE_SIZE_T 58 | AC_TYPE_SSIZE_T 59 | AC_TYPE_UINT8_T 60 | AC_TYPE_UINT16_T 61 | AC_TYPE_UINT32_T 62 | AC_TYPE_UINT64_T 63 | AC_TYPE_UID_T 64 | 65 | # Checks for library functions. 66 | AC_FUNC_MALLOC 67 | AC_CHECK_FUNCS([bzero popen pclose setresuid setreuid socket strchr strdup strerror]) 68 | 69 | # libev 70 | case $libev in 71 | no) 72 | case $host in 73 | *-mingw*) 74 | AH_VERBATIM([FD_SETSIZE], 75 | [#ifdef FD_SETSIZE 76 | # undef FD_SETSIZE 77 | #endif]) 78 | AC_DEFINE([FD_SETSIZE], [2048], [Reset max file descriptor size.]) 79 | AC_DEFINE([EV_FD_TO_WIN32_HANDLE(fd)], [(fd)], [Override libev default fd conversion macro.]) 80 | AC_DEFINE([EV_WIN32_HANDLE_TO_FD(handle)], [(handle)], [Override libev default handle conversion macro.]) 81 | AC_DEFINE([EV_WIN32_CLOSE_FD(fd)], [closesocket(fd)], [Override libev default fd close macro.]) 82 | AC_MSG_CHECKING([for select in ws2_32]) 83 | AC_TRY_LINK( 84 | [#ifdef HAVE_WINSOCK2_H 85 | #ifndef WIN32_LEAN_AND_MEAN 86 | #define WIN32_LEAN_AND_MEAN 87 | #endif 88 | #include 89 | #endif 90 | ],[ 91 | select(0,(fd_set *)NULL,(fd_set *)NULL,(fd_set *)NULL,(struct timeval *)NULL); 92 | ],[ 93 | AC_MSG_RESULT([yes]) 94 | HAVE_SELECT="1" 95 | AC_DEFINE_UNQUOTED(HAVE_SELECT, 1, 96 | [Define to 1 if you have the `select' function.]) 97 | HAVE_SYS_SELECT_H="1" 98 | AC_DEFINE_UNQUOTED(HAVE_SYS_SELECT_H, 1, 99 | [Define to 1 if you have the header file.]) 100 | ],[ 101 | AC_MSG_ERROR([no]) 102 | ]) 103 | ;; 104 | *) 105 | ;; 106 | esac 107 | AC_CHECK_HEADERS(sys/inotify.h sys/epoll.h sys/event.h port.h poll.h sys/select.h sys/eventfd.h sys/signalfd.h) 108 | AC_CHECK_FUNCS(inotify_init epoll_ctl kqueue port_create poll select eventfd signalfd) 109 | AC_CHECK_FUNCS(clock_gettime, [], [ 110 | # on linux, try syscall wrapper first 111 | if test $(uname) = Linux; then 112 | AC_MSG_CHECKING(for clock_gettime syscall) 113 | AC_LINK_IFELSE([AC_LANG_PROGRAM( 114 | [#include 115 | #include 116 | #include ], 117 | [struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])], 118 | [ac_have_clock_syscall=1 119 | AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime) 120 | AC_MSG_RESULT(yes)], 121 | [AC_MSG_RESULT(no)]) 122 | fi 123 | if test -z "$LIBEV_M4_AVOID_LIBRT" && test -z "$ac_have_clock_syscall"; then 124 | AC_CHECK_LIB(rt, clock_gettime) 125 | unset ac_cv_func_clock_gettime 126 | AC_CHECK_FUNCS(clock_gettime) 127 | fi 128 | ]) 129 | AC_CHECK_FUNCS(nanosleep, [], [ 130 | if test -z "$LIBEV_M4_AVOID_LIBRT"; then 131 | AC_CHECK_LIB(rt, nanosleep) 132 | unset ac_cv_func_nanosleep 133 | AC_CHECK_FUNCS(nanosleep) 134 | fi 135 | ]) 136 | if test -z "$LIBEV_M4_AVOID_LIBM"; then 137 | LIBM=m 138 | fi 139 | AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)]) 140 | ;; 141 | *) 142 | ;; 143 | esac 144 | 145 | AC_CONFIG_FILES([Makefile 146 | src/Makefile]) 147 | AC_OUTPUT 148 | -------------------------------------------------------------------------------- /contrib/archlinux/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Xiaoxiao Pu 2 | 3 | pkgname=iosocks-git 4 | pkgver=r134.8969844 5 | pkgrel=1 6 | pkgdesc="A lightweight tunnel proxy" 7 | arch=('i686' 'x86_64') 8 | url="https://github.com/XiaoxiaoPu/iosocks" 9 | license=('GPL3') 10 | depends=('libev') 11 | makedepends=('git') 12 | provides=('iosocks') 13 | source=("git+https://github.com/XiaoxiaoPu/iosocks.git") 14 | sha256sums=('SKIP') 15 | 16 | pkgver() { 17 | cd "${srcdir}/iosocks" 18 | printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 19 | } 20 | 21 | build() { 22 | cd "${srcdir}/iosocks" 23 | autoreconf -if 24 | ./configure --prefix=/usr --sysconfdir=/etc 25 | make 26 | } 27 | 28 | package() { 29 | cd "${srcdir}/iosocks" 30 | make DESTDIR="${pkgdir}/" install 31 | } -------------------------------------------------------------------------------- /contrib/systemd/ioclient@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=IoSocks Client Service 3 | After=network.target 4 | Documentation=man:iosocks(8) 5 | 6 | [Service] 7 | Type=forking 8 | KillSignal=SIGINT 9 | ExecStart=/usr/bin/ioclient -d -c /etc/iosocks/%i.conf --pidfile /run/ioclient@%i.pid --logfile /var/log/ioclient@%i.log 10 | PIDFile=/run/ioclient@%i.pid 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /contrib/systemd/ioredir@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=IoSocks Redirecter Service 3 | After=network.target 4 | Documentation=man:iosocks(8) 5 | 6 | [Service] 7 | Type=forking 8 | KillSignal=SIGINT 9 | ExecStart=/usr/bin/ioredir -d -c /etc/iosocks/%i.conf --pidfile /run/ioredir@%i.pid --logfile /var/log/ioredir@%i.log 10 | PIDFile=/run/ioredir@%i.pid 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /contrib/systemd/ioserver@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=IoSocks Server Service 3 | After=network.target 4 | Documentation=man:iosocks(8) 5 | 6 | [Service] 7 | Type=forking 8 | KillSignal=SIGINT 9 | ExecStart=/usr/bin/ioserver -d -c /etc/iosocks/%i.conf --pidfile /run/ioserver@%i.pid --logfile /var/log/ioserver@%i.log 10 | PIDFile=/run/ioserver@%i.pid 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /man/iosocks.8: -------------------------------------------------------------------------------- 1 | .TH IOSOCKS 8 "Apr 03, 2015" 2 | .SH NAME 3 | iosocks \- A lightweight tunnel proxy 4 | 5 | .SH SYNOPSIS 6 | \fBioserver\fR|\fBioclient\fR|\fBioredir\fR 7 | \-h, \-\-help 8 | \-c, \-\-config config file 9 | \-d, \-\-daemon daemonize after initialization 10 | \-p, \-\-pidfile PID file 11 | \-\-logfile log file 12 | 13 | .SH DESCRIPTION 14 | \fBiosocks\fR is a lightweight tunnel proxy, provides a SOCKS5 proxy and a transparent TCP proxy. 15 | .PP 16 | \fBiosocks\fR consists of 3 components. \fBioserver\fR runs on a remote server to provide secured tunnel service. \fBioclient\fR is a standard SOCKS5 proxy that trasmits all data to \fBioserver\fR. \fBioredir\fR is a transparent TCP proxy and requires netfilter's NAT function. 17 | .PP 18 | 19 | .SH OPTIONS 20 | .TP 21 | .B \-h, \-\-help 22 | print a help message and exit. 23 | .TP 24 | .B \-c, \-\-config \fI\fR 25 | config file. 26 | .TP 27 | .B \-d, \-\-daemon 28 | daemonize after startup. 29 | .TP 30 | .B \-p, \-\-pidfile \fI\fR 31 | PID file. 32 | .TP 33 | .B \-\-logfile \fI\fR 34 | log file. 35 | 36 | .SH CONFIG FILE 37 | The syntax of the config file is inspired by Microsoft Windows \fI.ini\fP files. 38 | 39 | .SS GLOBAL 40 | .TP 41 | [global] section defines common settings. 42 | .TP 43 | \fIuser=\fR 44 | .br 45 | user to set privilege to, default: nobody 46 | 47 | .SS SERVER 48 | .TP 49 | [server] section defines a remote server. This section can appear more than once. 50 | .TP 51 | \fIaddress=\fR 52 | .br 53 | server address, default: 0.0.0.0 54 | .TP 55 | \fIport=\fR 56 | .br 57 | server port, default: 1205 58 | .TP 59 | \fIkey=\fR 60 | .br 61 | secret key for crypto 62 | 63 | .SS LOCAL 64 | [local] section defines a local client. 65 | .TP 66 | .B \fIaddress=\fR 67 | local address, default: 127.0.0.1 68 | .br 69 | .TP 70 | .B \fIport=\fR 71 | .br 72 | local port, default: 1080 73 | 74 | .SS REDIR 75 | [redir] section define a transparent TCP proxy 76 | .TP 77 | .B \fIaddress=\fR 78 | local address, default: 127.0.0.1 79 | .br 80 | .TP 81 | .B \fIport=\fR 82 | .br 83 | local port, default: 1081 84 | 85 | .SH EXAMPLE 86 | Here is a sample config file: 87 | 88 | [global] 89 | user=nobody 90 | 91 | [server] 92 | address=192.168.1.1 93 | port=1205, 80, 443 94 | key=testkey 95 | 96 | [server] 97 | address=192.168.1.2 98 | port=1205, 80, 443 99 | key=testkey2 100 | 101 | [local] 102 | address=127.0.0.1 103 | port=1080 104 | 105 | [redir] 106 | address=127.0.0.1 107 | port=1081 108 | 109 | .SH AUTHOR 110 | .PP 111 | This manual page was written by Xiaoxiao Pu . 112 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS=-pipe -W -Wall -fno-strict-aliasing -D_GNU_SOURCE 2 | 3 | bin_PROGRAMS = ioserver ioclient ioredir 4 | 5 | EXTRA_ioserver_SOURCES = ev.c ev.h ev_vars.h ev_wrap.h ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c 6 | 7 | ioserver_SOURCES = \ 8 | async_connect.c async_resolv.c conf.c crypto.c log.c md5.c relay.c utils.c ioserver.c \ 9 | async_connect.h async_resolv.h conf.h crypto.h log.h md5.h relay.h utils.h 10 | ioserver_LDADD = $(LIB_ANL) 11 | 12 | ioclient_SOURCES = \ 13 | async_connect.c conf.c crypto.c log.c md5.c relay.c socks5.c utils.c ioclient.c \ 14 | async_connect.h conf.h crypto.h log.h md5.h relay.h socks5.h utils.h 15 | ioclient_LDADD = 16 | 17 | ioredir_SOURCES = \ 18 | async_connect.c conf.c crypto.c log.c md5.c relay.c socks5.c utils.c ioredir.c \ 19 | async_connect.h conf.h crypto.h log.h md5.h relay.h socks5.h utils.h 20 | ioredir_LDADD = 21 | 22 | if BUILD_EV 23 | ioserver_SOURCES += ev.c ev.h 24 | ioclient_SOURCES += ev.c ev.h 25 | ioredir_SOURCES += ev.c ev.h 26 | else 27 | ioserver_LDADD += $(LIB_EV) 28 | ioclient_LDADD += $(LIB_EV) 29 | ioredir_LDADD += $(LIB_EV) 30 | endif -------------------------------------------------------------------------------- /src/async_connect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * async_connect.h - async connect 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "async_connect.h" 29 | #include "log.h" 30 | #include "utils.h" 31 | 32 | #define UNUSED(x) do {(void)(x);} while (0) 33 | 34 | extern struct ev_loop *loop; 35 | 36 | typedef struct 37 | { 38 | void (*cb)(int, void *); 39 | void *data; 40 | ev_io w; 41 | } ctx_t; 42 | 43 | static void connect_cb(EV_P_ ev_io *w, int revents) 44 | { 45 | ctx_t *ctx = (ctx_t *)(w->data); 46 | 47 | UNUSED(revents); 48 | assert(ctx != NULL); 49 | 50 | ev_io_stop(EV_A_ w); 51 | 52 | if (getsockerror(w->fd) == 0) 53 | { 54 | // 连接成功 55 | (ctx->cb)(w->fd, ctx->data); 56 | free(ctx); 57 | } 58 | else 59 | { 60 | // 连接失败 61 | LOG("connect failed"); 62 | close(w->fd); 63 | (ctx->cb)(-1, ctx->data); 64 | free(ctx); 65 | } 66 | } 67 | 68 | void async_connect(const struct sockaddr *addr, socklen_t addrlen, 69 | void (*cb)(int, void *), void *data) 70 | { 71 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 72 | if (ctx == NULL) 73 | { 74 | LOG("out of memory"); 75 | (cb)(-1, data); 76 | return; 77 | } 78 | ctx->cb = cb; 79 | ctx->data = data; 80 | 81 | int sock = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); 82 | if (sock < 0) 83 | { 84 | ERROR("socket"); 85 | free(ctx); 86 | (cb)(-1, data); 87 | return; 88 | } 89 | setnonblock(sock); 90 | settimeout(sock); 91 | setkeepalive(sock); 92 | if (connect(sock, addr, addrlen) != 0) 93 | { 94 | if (errno != EINPROGRESS) 95 | { 96 | // 连接失败 97 | LOG("connect failed"); 98 | close(sock); 99 | free(ctx); 100 | (cb)(-1, data); 101 | return; 102 | } 103 | } 104 | ev_io_init(&(ctx->w), connect_cb, sock, EV_WRITE); 105 | ctx->w.data = (void *)ctx; 106 | ev_io_start(loop, &(ctx->w)); 107 | } 108 | -------------------------------------------------------------------------------- /src/async_connect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * async_connect.h - async connect 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef ASYNC_CONNECT_H 21 | #define ASYNC_CONNECT_H 22 | 23 | #include 24 | 25 | extern void async_connect(const struct sockaddr *addr, socklen_t addrlen, 26 | void (*cb)(int, void *), void *data); 27 | 28 | #endif // ASYNC_CONNECT_H 29 | -------------------------------------------------------------------------------- /src/async_resolv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * async_resolv.c - async resolv 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "async_resolv.h" 26 | #include "log.h" 27 | 28 | #define UNUSED(x) do {(void)(x);} while (0) 29 | 30 | // 最大域名解析次数 31 | #define MAX_TRY 3 32 | 33 | typedef struct 34 | { 35 | struct gaicb req; 36 | struct addrinfo hints; 37 | struct addrinfo *res; 38 | void (*cb)(struct addrinfo *, void *); 39 | void *data; 40 | int tried; 41 | char host[257]; 42 | char port[15]; 43 | } ctx_t; 44 | 45 | static void resolv_cb(int signo, siginfo_t *info, void *context); 46 | 47 | int resolv_init(void) 48 | { 49 | // SIGIO 信号 50 | struct sigaction sa; 51 | sa.sa_handler = (void(*) (int))resolv_cb; 52 | sigemptyset(&sa.sa_mask); 53 | sa.sa_flags = SA_RESTART | SA_SIGINFO; 54 | if (sigaction(SIGIO, &sa, NULL) != 0) 55 | { 56 | LOG("failed to setup SIGIO handler"); 57 | return -1; 58 | } 59 | else 60 | { 61 | return 0; 62 | } 63 | } 64 | 65 | void async_resolv(const char *host, const char *port, 66 | void (*cb)(struct addrinfo *, void *), 67 | void *data) 68 | { 69 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 70 | 71 | if (ctx == NULL) 72 | { 73 | LOG("out of memory"); 74 | (cb)(NULL, data); 75 | return; 76 | } 77 | 78 | bzero(&(ctx->hints), sizeof(ctx->hints)); 79 | ctx->hints.ai_family = AF_UNSPEC; 80 | ctx->hints.ai_socktype = SOCK_STREAM; 81 | strcpy(ctx->host, host); 82 | strcpy(ctx->port, port); 83 | ctx->req.ar_name = ctx->host; 84 | ctx->req.ar_service = ctx->port; 85 | ctx->req.ar_request = &(ctx->hints); 86 | ctx->req.ar_result = NULL; 87 | ctx->cb = cb; 88 | ctx->data = data; 89 | ctx->tried = 0; 90 | 91 | struct gaicb *req_ptr = &(ctx->req); 92 | struct sigevent sevp; 93 | sevp.sigev_notify = SIGEV_SIGNAL; 94 | sevp.sigev_signo = SIGIO; 95 | sevp.sigev_value.sival_ptr = (void *)ctx; 96 | if (getaddrinfo_a(GAI_NOWAIT, &req_ptr, 1, &sevp) != 0) 97 | { 98 | ERROR("getaddrinfo_a"); 99 | (ctx->cb)(NULL, ctx->data); 100 | free(ctx); 101 | return; 102 | } 103 | ctx->tried++; 104 | } 105 | 106 | static void resolv_cb(int signo, siginfo_t *info, void *context) 107 | { 108 | ctx_t *ctx = (ctx_t *)info->si_value.sival_ptr; 109 | 110 | UNUSED(context); 111 | assert(signo == SIGIO); 112 | assert(ctx != NULL); 113 | 114 | if (gai_error(&(ctx->req)) == 0) 115 | { 116 | // 域名解析成功 117 | (ctx->cb)(ctx->req.ar_result, ctx->data); 118 | free(ctx); 119 | } 120 | else 121 | { 122 | // 域名解析失败 123 | if (ctx->tried < MAX_TRY) 124 | { 125 | LOG("failed to resolv host: %s, try again", ctx->host); 126 | struct gaicb *req_ptr = &(ctx->req); 127 | struct sigevent sevp; 128 | sevp.sigev_notify = SIGEV_SIGNAL; 129 | sevp.sigev_signo = SIGIO; 130 | sevp.sigev_value.sival_ptr = (void *)ctx; 131 | if (getaddrinfo_a(GAI_NOWAIT, &req_ptr, 1, &sevp) != 0) 132 | { 133 | ERROR("getaddrinfo_a"); 134 | (ctx->cb)(NULL, ctx->data); 135 | free(ctx); 136 | return; 137 | } 138 | ctx->tried++; 139 | } 140 | else 141 | { 142 | LOG("failed to resolv host: %s, abort", ctx->host); 143 | (ctx->cb)(NULL, ctx->data); 144 | free(ctx); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/async_resolv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * async_resolv.h - async resolv 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef ASYNC_RESOLV_H 21 | #define ASYNC_RESOLV_H 22 | 23 | #include 24 | 25 | extern int resolv_init(void); 26 | extern void async_resolv(const char *host, const char *port, 27 | void (*cb)(struct addrinfo *, void *), 28 | void *data); 29 | 30 | #endif // ASYNC_RESOLV_H 31 | -------------------------------------------------------------------------------- /src/conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * conf.c - parse config file 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "conf.h" 25 | #include "md5.h" 26 | 27 | #define MAX_LINE 1024 28 | 29 | static void help(const char *s) 30 | { 31 | printf("usage: %s\n" 32 | " -h, --help show this help\n" 33 | " -c, --config config file\n" 34 | " -d, --daemon daemonize after initialization\n" 35 | " -p, --pidfile PID file\n" 36 | " --logfile log file\n", 37 | s); 38 | } 39 | 40 | #define my_strcpy(dest, src) _strncpy(dest, src, sizeof(dest)) 41 | static void _strncpy(char *dest, const char *src, size_t n) 42 | { 43 | char *end = dest + n; 44 | while ((dest < end) && ((*dest = *src) != '\0')) 45 | { 46 | dest++; 47 | src++; 48 | } 49 | *(end - 1) = '\0'; 50 | } 51 | 52 | static int read_conf(const char *file, conf_t *conf) 53 | { 54 | FILE *f = fopen(file, "rb"); 55 | if (f == NULL) 56 | { 57 | fprintf(stderr, "failed to read conf file\n"); 58 | return -1; 59 | } 60 | 61 | int line_num = 0; 62 | char buf[MAX_LINE]; 63 | enum 64 | { 65 | null = 0, 66 | global, 67 | server, 68 | local, 69 | redir 70 | } section = null; 71 | 72 | while (!feof(f)) 73 | { 74 | char *line = fgets(buf, MAX_LINE, f); 75 | if (line == NULL) 76 | { 77 | break; 78 | } 79 | line_num++; 80 | // 跳过行首空白符 81 | while (isspace(*line)) 82 | { 83 | line++; 84 | } 85 | // 去除行尾的空白符 86 | char *end = line + strlen(line) - 1; 87 | while ((end >= line) && (isspace(*end))) 88 | { 89 | *end = '\0'; 90 | end--; 91 | } 92 | // 跳过注释和空白行 93 | if ((*line == ';') || (*line == '#') ||(*line == '\0')) 94 | { 95 | continue; 96 | } 97 | if (*line == '[') 98 | { 99 | // 新的 section 100 | if (strcmp(line, "[global]") == 0) 101 | { 102 | section = global; 103 | } 104 | else if (strcmp(line, "[server]") == 0) 105 | { 106 | conf->server_num++; 107 | section = server; 108 | } 109 | else if (strcmp(line, "[local]") == 0) 110 | { 111 | section = local; 112 | } 113 | else if (strcmp(line, "[redir]") == 0) 114 | { 115 | section = redir; 116 | } 117 | else 118 | { 119 | fprintf(stderr, "line %d: wrong section\n", line_num); 120 | fclose(f); 121 | return -1; 122 | } 123 | } 124 | else 125 | { 126 | char *p = strchr(line, '='); 127 | if (p == NULL) 128 | { 129 | fprintf(stderr, "line %d: no \'=\\ found\n", line_num); 130 | fclose(f); 131 | return -1; 132 | } 133 | *p = '\0'; 134 | char *name = line; 135 | char *value = p + 1; 136 | if (section == global) 137 | { 138 | if (strcmp(name, "user") == 0) 139 | { 140 | my_strcpy(conf->user, value); 141 | } 142 | } 143 | else if (section == server) 144 | { 145 | if (conf->server_num > MAX_SERVER) 146 | { 147 | continue; 148 | } 149 | if (strcmp(name, "address") == 0) 150 | { 151 | my_strcpy(conf->server[conf->server_num - 1].address, value); 152 | } 153 | else if (strcmp(name, "port") == 0) 154 | { 155 | my_strcpy(conf->server[conf->server_num - 1].port, value); 156 | } 157 | else if (strcmp(name, "key") == 0) 158 | { 159 | md5(conf->server[conf->server_num - 1].key, value, strlen(value)); 160 | } 161 | } 162 | else if (section == local) 163 | { 164 | if (strcmp(name, "address") == 0) 165 | { 166 | my_strcpy(conf->local.address, value); 167 | } 168 | else if (strcmp(name, "port") == 0) 169 | { 170 | my_strcpy(conf->local.port, value); 171 | } 172 | } 173 | else if (section == redir) 174 | { 175 | if (strcmp(name, "address") == 0) 176 | { 177 | my_strcpy(conf->redir.address, value); 178 | } 179 | else if (strcmp(name, "port") == 0) 180 | { 181 | my_strcpy(conf->redir.port, value); 182 | } 183 | } 184 | else 185 | { 186 | fprintf(stderr, "line %d: no section set\n", line_num); 187 | fclose(f); 188 | return -1; 189 | } 190 | } 191 | } 192 | fclose(f); 193 | 194 | if (conf->user[0] == '\0') 195 | { 196 | strcpy(conf->user, "nobody"); 197 | } 198 | if (conf->server_num == 0) 199 | { 200 | fprintf(stderr, "no server set in config file\n"); 201 | return -1; 202 | } 203 | for (int i = 0; i < conf->server_num; i++) 204 | { 205 | if (conf->server[i].address[0] == '\0') 206 | { 207 | strcpy(conf->server[i].address, "0.0.0.0"); 208 | } 209 | if (conf->server[i].port[0] == '\0') 210 | { 211 | strcpy(conf->server[i].port, "1205"); 212 | } 213 | else 214 | { 215 | char *p1 = strchr(conf->server[i].port, ','); 216 | if (p1 != NULL) 217 | { 218 | *p1 = '\0'; 219 | } 220 | while (p1 != NULL) 221 | { 222 | char *p2 = strchr(p1 + 1, ','); 223 | if (p2 != NULL) 224 | { 225 | *p2 = '\0'; 226 | } 227 | if (conf->server_num < MAX_SERVER) 228 | { 229 | my_strcpy(conf->server[conf->server_num].address, 230 | conf->server[i].address); 231 | my_strcpy(conf->server[conf->server_num].port, p1 + 1); 232 | my_strcpy(conf->server[conf->server_num].key, 233 | conf->server[i].key); 234 | conf->server_num++; 235 | } 236 | p1 = p2; 237 | } 238 | } 239 | } 240 | if (conf->local.address[0] == '\0') 241 | { 242 | strcpy(conf->local.address, "127.0.0.1"); 243 | } 244 | if (conf->local.port[0] == '\0') 245 | { 246 | strcpy(conf->local.port, "1080"); 247 | } 248 | if (conf->redir.address[0] == '\0') 249 | { 250 | strcpy(conf->redir.address, "127.0.0.1"); 251 | } 252 | if (conf->redir.port[0] == '\0') 253 | { 254 | strcpy(conf->redir.port, "1081"); 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | int parse_args(int argc, char **argv, conf_t *conf) 261 | { 262 | const char *conf_file = NULL; 263 | 264 | bzero(conf, sizeof(conf_t)); 265 | 266 | for (int i = 1; i < argc; i++) 267 | { 268 | if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) 269 | { 270 | help(argv[0]); 271 | return -1; 272 | } 273 | else if ((strcmp(argv[i], "-c") == 0) || (strcmp(argv[i], "--config") == 0)) 274 | { 275 | if (i + 2 > argc) 276 | { 277 | fprintf(stderr, "missing filename after '%s'\n", argv[i]); 278 | return 1; 279 | } 280 | conf_file = argv[i + 1]; 281 | i++; 282 | } 283 | else if ((strcmp(argv[i], "-d") == 0) || (strcmp(argv[i], "--daemon") == 0)) 284 | { 285 | conf->daemon = 1; 286 | } 287 | else if ((strcmp(argv[i], "-p") == 0) || (strcmp(argv[i], "--pidfile") == 0)) 288 | { 289 | if (i + 2 > argc) 290 | { 291 | fprintf(stderr, "missing filename after '%s'\n", argv[i]); 292 | return 1; 293 | } 294 | my_strcpy(conf->pidfile, argv[i + 1]); 295 | i++; 296 | } 297 | else if (strcmp(argv[i], "--logfile") == 0) 298 | { 299 | if (i + 2 > argc) 300 | { 301 | fprintf(stderr, "missing filename after '%s'\n", argv[i]); 302 | return 1; 303 | } 304 | my_strcpy(conf->logfile, argv[i + 1]); 305 | i++; 306 | } 307 | else 308 | { 309 | fprintf(stderr, "invalid option: %s\n", argv[i]); 310 | return 1; 311 | } 312 | } 313 | if (conf_file == NULL) 314 | { 315 | help(argv[0]); 316 | return -1; 317 | } 318 | if (conf->daemon) 319 | { 320 | if (conf->pidfile[0] == '\0') 321 | { 322 | fprintf(stderr, "no pidfile specified\n"); 323 | return -1; 324 | } 325 | if (conf->logfile[0] == '\0') 326 | { 327 | fprintf(stderr, "no logfile specified\n"); 328 | return -1; 329 | } 330 | } 331 | if (read_conf(conf_file, conf) != 0) 332 | { 333 | return -1; 334 | } 335 | for (int i = 0; i < conf->server_num; i++) 336 | { 337 | if (conf->server[i].key[0] == '\0') 338 | { 339 | help(argv[0]); 340 | return -1; 341 | } 342 | } 343 | 344 | return 0; 345 | } 346 | -------------------------------------------------------------------------------- /src/conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * conf.h - parse config file 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef CONF_H 21 | #define CONF_H 22 | 23 | // 最大服务器数 24 | #define MAX_SERVER 16 25 | 26 | typedef struct 27 | { 28 | int server_num; 29 | int daemon; 30 | char user[16]; 31 | char pidfile[64]; 32 | char logfile[64]; 33 | struct 34 | { 35 | char address[128]; 36 | char port[128]; 37 | char key[16]; 38 | } server[MAX_SERVER]; 39 | struct 40 | { 41 | char address[128]; 42 | char port[16]; 43 | } local; 44 | struct 45 | { 46 | char address[128]; 47 | char port[16]; 48 | } redir; 49 | } conf_t; 50 | 51 | extern int parse_args(int argc, char **argv, conf_t *conf); 52 | 53 | #endif // CONF_H 54 | -------------------------------------------------------------------------------- /src/crypto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * encrypt.c - encryption and decryption 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "crypto.h" 24 | #include "md5.h" 25 | 26 | #define SWAP(x, y) do {register uint8_t tmp = (x); (x) = (y); (y) = tmp; } while (0) 27 | 28 | static void rc4_init(rc4_evp_t *evp, const void *key, size_t key_len) 29 | { 30 | register int i, j; 31 | register uint8_t *s = evp->s; 32 | for (i = 0; i < 256; i++) 33 | { 34 | s[i] = (uint8_t)i; 35 | } 36 | for (i = 0, j = 0; i < 256; i++) 37 | { 38 | j = (j + s[i] + ((uint8_t *)key)[i % key_len]) & 255; 39 | SWAP(s[i], s[j]); 40 | } 41 | evp->i = 0; 42 | evp->j = 0; 43 | } 44 | 45 | static void rc4_encrypt(void *stream, size_t len, rc4_evp_t *evp) 46 | { 47 | #if defined(__GNUC__) 48 | # if defined(__amd64__) || defined(__x86_64__) 49 | # define RC4_ASM 1 50 | __asm__ ( 51 | /* 开头未对齐的部分,每次处理 1 字节 */ 52 | "cmpq %[stream], %[end]\n\t" 53 | "jbe 6f\n\t" 54 | "testq $7, %[stream]\n\t" 55 | "je 2f\n\t" 56 | "1:\n\t" 57 | // i = (i + 1) & 255 58 | "incl %[i]\n\t" 59 | "movzbl %b[i], %[i]\n\t" 60 | // j = (j + s[i] ) & 255 61 | "movzbl (%[s], %q[i]), %%ecx\n\t" 62 | "addb %%cl, %b[j]\n\t" 63 | // SWAP(s[i], s[j]) 64 | // *((uint8_t *)stream) ^= s[(s[i] + s[j]) & 255]; */ 65 | "movzbl (%[s], %q[j]), %%edx\n\t" 66 | "movb %%dl, (%[s], %q[i])\n\t" 67 | "addl %%ecx, %%edx\n\t" 68 | "movb %%cl, (%[s], %q[j])\n\t" 69 | "movzbl %%dl, %%edx\n\t" 70 | "movb (%[s], %%rdx), %%cl\n\t" 71 | "xorb %%cl, (%[stream])\n\t" 72 | // stream++ 73 | "incq %[stream]\n\t" 74 | "cmpq %[stream], %[end]\n\t" 75 | "jbe 6f\n\t" 76 | "testq $7, %[stream]\n\t" 77 | "jne 1b\n\t" 78 | "2:\n\t" 79 | 80 | // 中间对齐的部分,每次处理 8 字节 81 | "lea 8(%[stream]), %%r8\n\t" 82 | "cmpq %%r8, %[end]\n\t" 83 | "jbe 4f\n\t" 84 | "3:\n\t" 85 | // i = (i + 1) & 255 86 | "incl %[i]\n\t" 87 | "movzbl %b[i], %[i]\n\t" 88 | // j = (j + s[i] ) & 255 89 | "movzbl (%[s], %q[i]), %%ecx\n\t" 90 | "addb %%cl, %b[j]\n\t" 91 | // SWAP(s[i], s[j]) 92 | // r8 ^= s[(s[i] + s[j]) & 255] 93 | "movzbl (%[s], %q[j]), %%edx\n\t" 94 | "movb %%dl, (%[s], %q[i])\n\t" 95 | "addl %%ecx, %%edx\n\t" 96 | "movb %%cl, (%[s], %q[j])\n\t" 97 | "movzbl %%dl, %%edx\n\t" 98 | "shl $8, %%r8\n\t" 99 | "movb (%[s], %%rdx), %%r8b\n\t" 100 | // stream++ 101 | "incq %[stream]\n\t" 102 | "testq $7, %[stream]\n\t" 103 | "jne 3b\n\t" 104 | "bswap %%r8\n\t" 105 | "xorq %%r8, -8(%[stream])\n\t" 106 | "lea 8(%[stream]), %%r8\n\t" 107 | "cmpq %%r8, %[end]\n\t" 108 | "jg 3b\n\t" 109 | "4:\n\t" 110 | 111 | // 末尾未对齐的部分,每次处理 1 字节 112 | "cmpq %[stream], %[end]\n\t" 113 | "jbe 6f\n\t" 114 | "5:\n\t" 115 | // i = (i + 1) & 255 116 | "incl %[i]\n\t" 117 | "movzbl %b[i], %[i]\n\t" 118 | // j = (j + s[i] ) & 255 119 | "movzbl (%[s], %q[i]), %%ecx\n\t" 120 | "addb %%cl, %b[j]\n\t" 121 | // SWAP(s[i], s[j]) 122 | // *((uint8_t *)stream) ^= s[(s[i] + s[j]) & 255] 123 | "movzbl (%[s], %q[j]), %%edx\n\t" 124 | "movb %%dl, (%[s], %q[i])\n\t" 125 | "addl %%ecx, %%edx\n\t" 126 | "movb %%cl, (%[s], %q[j])\n\t" 127 | "movzbl %%dl, %%edx\n\t" 128 | "movb (%[s], %%rdx), %%cl\n\t" 129 | "xorb %%cl, (%[stream])\n\t" 130 | // stream++ 131 | "incq %[stream]\n\t" 132 | "cmpq %[stream], %[end]\n\t" 133 | "jg 5b\n\t" 134 | "6:\n\t" 135 | : [i] "=a"(evp->i), 136 | [j] "=b"(evp->j) 137 | : [stream] "r"(stream), 138 | [end] "r"(stream + len), 139 | [s] "r"(evp->s), 140 | "[i]"(evp->i), 141 | "[j]"(evp->j) 142 | : "memory", "rcx", "rdx", "r8" 143 | ); 144 | # elif defined(__i386__) 145 | # define RC4_ASM 1 146 | __asm__ __volatile__ ( 147 | "cmpl %[stream], %[end]\n\t" 148 | "je 2f\n\t" 149 | "1:\n\t" 150 | /* i = (i + 1) & 255; */ 151 | "incl %[i]\n\t" 152 | "movzbl %b[i], %[i]\n\t" 153 | /* j = (j + s[i] ) & 255 */ 154 | "movzbl (%[s], %[i]), %%ecx\n\t" 155 | "addb %%cl, %b[j]\n\t" 156 | /* SWAP(s[i], s[j]); */ 157 | /* *((uint8_t *)stream) ^= s[(s[i] + s[j]) & 255]; */ 158 | "movzbl (%[s], %[j]), %%edx\n\t" 159 | "movb %%dl, (%[s], %[i])\n\t" 160 | "addl %%ecx, %%edx\n\t" 161 | "movb %%cl, (%[s], %[j])\n\t" 162 | "movzbl %%dl, %%edx\n\t" 163 | "movb (%[s], %%edx), %%cl\n\t" 164 | "xorb %%cl, (%[stream])\n\t" 165 | /* stream++ */ 166 | "incl %[stream]\n\t" 167 | "cmpl %[stream], %[end]\n\t" 168 | "jne 1b\n\t" 169 | "2:\n\t" 170 | : [i] "=a"(evp->i), 171 | [j] "=b"(evp->j) 172 | : [stream] "r"(stream), 173 | [end] "g"(stream + len), 174 | [s] "r"(evp->s), 175 | "[i]"(evp->i), 176 | "[j]"(evp->j) 177 | : "memory", "ecx", "edx" 178 | ); 179 | # elif defined(__arm__) 180 | # define RC4_ASM 1 181 | __asm__ __volatile__ ( 182 | "cmp %[stream], %[end]\n\t" 183 | "bcs 2f\n\t" 184 | "1:\n\t" 185 | /* i = (i + 1) & 255; */ 186 | "add %[i], %[i], #1\n\t" 187 | "and %[i], %[i], #255\n\t" 188 | /* j = (j + s[i] ) & 255 */ 189 | "ldrb r4, [%[s], %[i]]\n\t" 190 | "add %[j], %[j], r4\n\t" 191 | "and %[j], %[j], #255\n\t" 192 | /* SWAP(s[i], s[j]); */ 193 | /* *((uint8_t *)stream) ^= s[(s[i] + s[j]) & 255]; */ 194 | "ldrb r5, [%[s], %[j]]\n\t" 195 | "strb r5, [%[s], %[i]]\n\t" 196 | "ldrb r6, [%[stream]]\n\t" 197 | "add r5, r5, r4\n\t" 198 | "strb r4, [%[s], %[j]]\n\t" 199 | "and r5, r5, #255\n\t" 200 | "ldrb r7, [%[s], r5]\n\t" 201 | "eor r6, r6, r7\n\t" 202 | "strb r6, [%[stream]], #1\n\t" 203 | "cmp %[stream], %[end]\n\t" 204 | "bne 1b\n\t" 205 | "2:\n\t" 206 | : [i] "=r"(evp->i), 207 | [j] "=r"(evp->j) 208 | : [stream] "r"(stream), 209 | [end] "r"(stream + len), 210 | [s] "r"(evp->s), 211 | "[i]"(evp->i), 212 | "[j]"(evp->j) 213 | : "memory", "r4", "r5", "r6", "r7" 214 | ); 215 | # endif 216 | #endif 217 | 218 | #ifndef RC4_ASM 219 | register int i = evp->i; 220 | register int j = evp->j; 221 | register uint8_t *s = evp->s; 222 | register uint8_t *end = (uint8_t *)stream + len; 223 | for (; (uint8_t *)stream < end; stream++) 224 | { 225 | i = (i + 1) & 255; 226 | j = (j + s[i]) & 255; 227 | SWAP(s[i], s[j]); 228 | *((uint8_t *)stream) ^= s[(s[i] + s[j]) & 255]; 229 | } 230 | evp->i = i; 231 | evp->j = j; 232 | #endif 233 | } 234 | 235 | #define rc4_decrypt rc4_encrypt 236 | 237 | void crypto_init(crypto_evp_t *evp, const void *key,const void *iv) 238 | { 239 | uint8_t buf[32]; 240 | memcpy(buf, iv, 16); 241 | memcpy(buf + 16, key, 16); 242 | md5(buf, buf, 32); 243 | rc4_init(&(evp->enc), buf, 16); 244 | evp->dec = evp->enc; 245 | } 246 | 247 | void crypto_encrypt(void *buf, size_t len, crypto_evp_t *evp) 248 | { 249 | rc4_encrypt(buf, len, &(evp->enc)); 250 | } 251 | 252 | void crypto_decrypt(void *buf, size_t len, crypto_evp_t *evp) 253 | { 254 | rc4_decrypt(buf, len, &(evp->dec)); 255 | } 256 | -------------------------------------------------------------------------------- /src/crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * encrypt.h - encryption and decryption 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef CRYPTO_H 21 | #define CRYPTO_H 22 | 23 | #include 24 | 25 | 26 | typedef struct 27 | { 28 | int i; 29 | int j; 30 | uint8_t s[256]; 31 | } rc4_evp_t; 32 | 33 | typedef struct 34 | { 35 | rc4_evp_t enc, dec; 36 | } crypto_evp_t; 37 | 38 | extern void crypto_init(crypto_evp_t *evp, const void *key, const void *iv); 39 | extern void crypto_encrypt(void *buf, size_t len, crypto_evp_t *evp); 40 | extern void crypto_decrypt(void *buf, size_t len, crypto_evp_t *evp); 41 | 42 | 43 | #endif // CRYPTO_H 44 | -------------------------------------------------------------------------------- /src/ev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libev native API header 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef EV_H_ 41 | #define EV_H_ 42 | 43 | #ifdef __cplusplus 44 | # define EV_CPP(x) x 45 | #else 46 | # define EV_CPP(x) 47 | #endif 48 | 49 | #define EV_THROW EV_CPP(throw()) 50 | 51 | EV_CPP(extern "C" {) 52 | 53 | /*****************************************************************************/ 54 | 55 | /* pre-4.0 compatibility */ 56 | #ifndef EV_COMPAT3 57 | # define EV_COMPAT3 1 58 | #endif 59 | 60 | #ifndef EV_FEATURES 61 | # if defined __OPTIMIZE_SIZE__ 62 | # define EV_FEATURES 0x7c 63 | # else 64 | # define EV_FEATURES 0x7f 65 | # endif 66 | #endif 67 | 68 | #define EV_FEATURE_CODE ((EV_FEATURES) & 1) 69 | #define EV_FEATURE_DATA ((EV_FEATURES) & 2) 70 | #define EV_FEATURE_CONFIG ((EV_FEATURES) & 4) 71 | #define EV_FEATURE_API ((EV_FEATURES) & 8) 72 | #define EV_FEATURE_WATCHERS ((EV_FEATURES) & 16) 73 | #define EV_FEATURE_BACKENDS ((EV_FEATURES) & 32) 74 | #define EV_FEATURE_OS ((EV_FEATURES) & 64) 75 | 76 | /* these priorities are inclusive, higher priorities will be invoked earlier */ 77 | #ifndef EV_MINPRI 78 | # define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0) 79 | #endif 80 | #ifndef EV_MAXPRI 81 | # define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0) 82 | #endif 83 | 84 | #ifndef EV_MULTIPLICITY 85 | # define EV_MULTIPLICITY EV_FEATURE_CONFIG 86 | #endif 87 | 88 | #ifndef EV_PERIODIC_ENABLE 89 | # define EV_PERIODIC_ENABLE EV_FEATURE_WATCHERS 90 | #endif 91 | 92 | #ifndef EV_STAT_ENABLE 93 | # define EV_STAT_ENABLE EV_FEATURE_WATCHERS 94 | #endif 95 | 96 | #ifndef EV_PREPARE_ENABLE 97 | # define EV_PREPARE_ENABLE EV_FEATURE_WATCHERS 98 | #endif 99 | 100 | #ifndef EV_CHECK_ENABLE 101 | # define EV_CHECK_ENABLE EV_FEATURE_WATCHERS 102 | #endif 103 | 104 | #ifndef EV_IDLE_ENABLE 105 | # define EV_IDLE_ENABLE EV_FEATURE_WATCHERS 106 | #endif 107 | 108 | #ifndef EV_FORK_ENABLE 109 | # define EV_FORK_ENABLE EV_FEATURE_WATCHERS 110 | #endif 111 | 112 | #ifndef EV_CLEANUP_ENABLE 113 | # define EV_CLEANUP_ENABLE EV_FEATURE_WATCHERS 114 | #endif 115 | 116 | #ifndef EV_SIGNAL_ENABLE 117 | # define EV_SIGNAL_ENABLE EV_FEATURE_WATCHERS 118 | #endif 119 | 120 | #ifndef EV_CHILD_ENABLE 121 | # ifdef _WIN32 122 | # define EV_CHILD_ENABLE 0 123 | # else 124 | # define EV_CHILD_ENABLE EV_FEATURE_WATCHERS 125 | #endif 126 | #endif 127 | 128 | #ifndef EV_ASYNC_ENABLE 129 | # define EV_ASYNC_ENABLE EV_FEATURE_WATCHERS 130 | #endif 131 | 132 | #ifndef EV_EMBED_ENABLE 133 | # define EV_EMBED_ENABLE EV_FEATURE_WATCHERS 134 | #endif 135 | 136 | #ifndef EV_WALK_ENABLE 137 | # define EV_WALK_ENABLE 0 /* not yet */ 138 | #endif 139 | 140 | /*****************************************************************************/ 141 | 142 | #if EV_CHILD_ENABLE && !EV_SIGNAL_ENABLE 143 | # undef EV_SIGNAL_ENABLE 144 | # define EV_SIGNAL_ENABLE 1 145 | #endif 146 | 147 | /*****************************************************************************/ 148 | 149 | typedef double ev_tstamp; 150 | 151 | #ifndef EV_ATOMIC_T 152 | # include 153 | # define EV_ATOMIC_T sig_atomic_t volatile 154 | #endif 155 | 156 | #if EV_STAT_ENABLE 157 | # ifdef _WIN32 158 | # include 159 | # include 160 | # endif 161 | # include 162 | #endif 163 | 164 | /* support multiple event loops? */ 165 | #if EV_MULTIPLICITY 166 | struct ev_loop; 167 | # define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */ 168 | # define EV_P_ EV_P, /* a loop as first of multiple parameters */ 169 | # define EV_A loop /* a loop as sole argument to a function call */ 170 | # define EV_A_ EV_A, /* a loop as first of multiple arguments */ 171 | # define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */ 172 | # define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */ 173 | # define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */ 174 | # define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */ 175 | #else 176 | # define EV_P void 177 | # define EV_P_ 178 | # define EV_A 179 | # define EV_A_ 180 | # define EV_DEFAULT 181 | # define EV_DEFAULT_ 182 | # define EV_DEFAULT_UC 183 | # define EV_DEFAULT_UC_ 184 | # undef EV_EMBED_ENABLE 185 | #endif 186 | 187 | /* EV_INLINE is used for functions in header files */ 188 | #if __STDC_VERSION__ >= 199901L || __GNUC__ >= 3 189 | # define EV_INLINE static inline 190 | #else 191 | # define EV_INLINE static 192 | #endif 193 | 194 | #ifdef EV_API_STATIC 195 | # define EV_API_DECL static 196 | #else 197 | # define EV_API_DECL extern 198 | #endif 199 | 200 | /* EV_PROTOTYPES can be used to switch of prototype declarations */ 201 | #ifndef EV_PROTOTYPES 202 | # define EV_PROTOTYPES 1 203 | #endif 204 | 205 | /*****************************************************************************/ 206 | 207 | #define EV_VERSION_MAJOR 4 208 | #define EV_VERSION_MINOR 19 209 | 210 | /* eventmask, revents, events... */ 211 | enum { 212 | EV_UNDEF = (int)0xFFFFFFFF, /* guaranteed to be invalid */ 213 | EV_NONE = 0x00, /* no events */ 214 | EV_READ = 0x01, /* ev_io detected read will not block */ 215 | EV_WRITE = 0x02, /* ev_io detected write will not block */ 216 | EV__IOFDSET = 0x80, /* internal use only */ 217 | EV_IO = EV_READ, /* alias for type-detection */ 218 | EV_TIMER = 0x00000100, /* timer timed out */ 219 | #if EV_COMPAT3 220 | EV_TIMEOUT = EV_TIMER, /* pre 4.0 API compatibility */ 221 | #endif 222 | EV_PERIODIC = 0x00000200, /* periodic timer timed out */ 223 | EV_SIGNAL = 0x00000400, /* signal was received */ 224 | EV_CHILD = 0x00000800, /* child/pid had status change */ 225 | EV_STAT = 0x00001000, /* stat data changed */ 226 | EV_IDLE = 0x00002000, /* event loop is idling */ 227 | EV_PREPARE = 0x00004000, /* event loop about to poll */ 228 | EV_CHECK = 0x00008000, /* event loop finished poll */ 229 | EV_EMBED = 0x00010000, /* embedded event loop needs sweep */ 230 | EV_FORK = 0x00020000, /* event loop resumed in child */ 231 | EV_CLEANUP = 0x00040000, /* event loop resumed in child */ 232 | EV_ASYNC = 0x00080000, /* async intra-loop signal */ 233 | EV_CUSTOM = 0x01000000, /* for use by user code */ 234 | EV_ERROR = (int)0x80000000 /* sent when an error occurs */ 235 | }; 236 | 237 | /* can be used to add custom fields to all watchers, while losing binary compatibility */ 238 | #ifndef EV_COMMON 239 | # define EV_COMMON void *data; 240 | #endif 241 | 242 | #ifndef EV_CB_DECLARE 243 | # define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents); 244 | #endif 245 | #ifndef EV_CB_INVOKE 246 | # define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents)) 247 | #endif 248 | 249 | /* not official, do not use */ 250 | #define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents) 251 | 252 | /* 253 | * struct member types: 254 | * private: you may look at them, but not change them, 255 | * and they might not mean anything to you. 256 | * ro: can be read anytime, but only changed when the watcher isn't active. 257 | * rw: can be read and modified anytime, even when the watcher is active. 258 | * 259 | * some internal details that might be helpful for debugging: 260 | * 261 | * active is either 0, which means the watcher is not active, 262 | * or the array index of the watcher (periodics, timers) 263 | * or the array index + 1 (most other watchers) 264 | * or simply 1 for watchers that aren't in some array. 265 | * pending is either 0, in which case the watcher isn't, 266 | * or the array index + 1 in the pendings array. 267 | */ 268 | 269 | #if EV_MINPRI == EV_MAXPRI 270 | # define EV_DECL_PRIORITY 271 | #elif !defined (EV_DECL_PRIORITY) 272 | # define EV_DECL_PRIORITY int priority; 273 | #endif 274 | 275 | /* shared by all watchers */ 276 | #define EV_WATCHER(type) \ 277 | int active; /* private */ \ 278 | int pending; /* private */ \ 279 | EV_DECL_PRIORITY /* private */ \ 280 | EV_COMMON /* rw */ \ 281 | EV_CB_DECLARE (type) /* private */ 282 | 283 | #define EV_WATCHER_LIST(type) \ 284 | EV_WATCHER (type) \ 285 | struct ev_watcher_list *next; /* private */ 286 | 287 | #define EV_WATCHER_TIME(type) \ 288 | EV_WATCHER (type) \ 289 | ev_tstamp at; /* private */ 290 | 291 | /* base class, nothing to see here unless you subclass */ 292 | typedef struct ev_watcher 293 | { 294 | EV_WATCHER (ev_watcher) 295 | } ev_watcher; 296 | 297 | /* base class, nothing to see here unless you subclass */ 298 | typedef struct ev_watcher_list 299 | { 300 | EV_WATCHER_LIST (ev_watcher_list) 301 | } ev_watcher_list; 302 | 303 | /* base class, nothing to see here unless you subclass */ 304 | typedef struct ev_watcher_time 305 | { 306 | EV_WATCHER_TIME (ev_watcher_time) 307 | } ev_watcher_time; 308 | 309 | /* invoked when fd is either EV_READable or EV_WRITEable */ 310 | /* revent EV_READ, EV_WRITE */ 311 | typedef struct ev_io 312 | { 313 | EV_WATCHER_LIST (ev_io) 314 | 315 | int fd; /* ro */ 316 | int events; /* ro */ 317 | } ev_io; 318 | 319 | /* invoked after a specific time, repeatable (based on monotonic clock) */ 320 | /* revent EV_TIMEOUT */ 321 | typedef struct ev_timer 322 | { 323 | EV_WATCHER_TIME (ev_timer) 324 | 325 | ev_tstamp repeat; /* rw */ 326 | } ev_timer; 327 | 328 | /* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */ 329 | /* revent EV_PERIODIC */ 330 | typedef struct ev_periodic 331 | { 332 | EV_WATCHER_TIME (ev_periodic) 333 | 334 | ev_tstamp offset; /* rw */ 335 | ev_tstamp interval; /* rw */ 336 | ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now) EV_THROW; /* rw */ 337 | } ev_periodic; 338 | 339 | /* invoked when the given signal has been received */ 340 | /* revent EV_SIGNAL */ 341 | typedef struct ev_signal 342 | { 343 | EV_WATCHER_LIST (ev_signal) 344 | 345 | int signum; /* ro */ 346 | } ev_signal; 347 | 348 | /* invoked when sigchld is received and waitpid indicates the given pid */ 349 | /* revent EV_CHILD */ 350 | /* does not support priorities */ 351 | typedef struct ev_child 352 | { 353 | EV_WATCHER_LIST (ev_child) 354 | 355 | int flags; /* private */ 356 | int pid; /* ro */ 357 | int rpid; /* rw, holds the received pid */ 358 | int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */ 359 | } ev_child; 360 | 361 | #if EV_STAT_ENABLE 362 | /* st_nlink = 0 means missing file or other error */ 363 | # ifdef _WIN32 364 | typedef struct _stati64 ev_statdata; 365 | # else 366 | typedef struct stat ev_statdata; 367 | # endif 368 | 369 | /* invoked each time the stat data changes for a given path */ 370 | /* revent EV_STAT */ 371 | typedef struct ev_stat 372 | { 373 | EV_WATCHER_LIST (ev_stat) 374 | 375 | ev_timer timer; /* private */ 376 | ev_tstamp interval; /* ro */ 377 | const char *path; /* ro */ 378 | ev_statdata prev; /* ro */ 379 | ev_statdata attr; /* ro */ 380 | 381 | int wd; /* wd for inotify, fd for kqueue */ 382 | } ev_stat; 383 | #endif 384 | 385 | #if EV_IDLE_ENABLE 386 | /* invoked when the nothing else needs to be done, keeps the process from blocking */ 387 | /* revent EV_IDLE */ 388 | typedef struct ev_idle 389 | { 390 | EV_WATCHER (ev_idle) 391 | } ev_idle; 392 | #endif 393 | 394 | /* invoked for each run of the mainloop, just before the blocking call */ 395 | /* you can still change events in any way you like */ 396 | /* revent EV_PREPARE */ 397 | typedef struct ev_prepare 398 | { 399 | EV_WATCHER (ev_prepare) 400 | } ev_prepare; 401 | 402 | /* invoked for each run of the mainloop, just after the blocking call */ 403 | /* revent EV_CHECK */ 404 | typedef struct ev_check 405 | { 406 | EV_WATCHER (ev_check) 407 | } ev_check; 408 | 409 | #if EV_FORK_ENABLE 410 | /* the callback gets invoked before check in the child process when a fork was detected */ 411 | /* revent EV_FORK */ 412 | typedef struct ev_fork 413 | { 414 | EV_WATCHER (ev_fork) 415 | } ev_fork; 416 | #endif 417 | 418 | #if EV_CLEANUP_ENABLE 419 | /* is invoked just before the loop gets destroyed */ 420 | /* revent EV_CLEANUP */ 421 | typedef struct ev_cleanup 422 | { 423 | EV_WATCHER (ev_cleanup) 424 | } ev_cleanup; 425 | #endif 426 | 427 | #if EV_EMBED_ENABLE 428 | /* used to embed an event loop inside another */ 429 | /* the callback gets invoked when the event loop has handled events, and can be 0 */ 430 | typedef struct ev_embed 431 | { 432 | EV_WATCHER (ev_embed) 433 | 434 | struct ev_loop *other; /* ro */ 435 | ev_io io; /* private */ 436 | ev_prepare prepare; /* private */ 437 | ev_check check; /* unused */ 438 | ev_timer timer; /* unused */ 439 | ev_periodic periodic; /* unused */ 440 | ev_idle idle; /* unused */ 441 | ev_fork fork; /* private */ 442 | #if EV_CLEANUP_ENABLE 443 | ev_cleanup cleanup; /* unused */ 444 | #endif 445 | } ev_embed; 446 | #endif 447 | 448 | #if EV_ASYNC_ENABLE 449 | /* invoked when somebody calls ev_async_send on the watcher */ 450 | /* revent EV_ASYNC */ 451 | typedef struct ev_async 452 | { 453 | EV_WATCHER (ev_async) 454 | 455 | EV_ATOMIC_T sent; /* private */ 456 | } ev_async; 457 | 458 | # define ev_async_pending(w) (+(w)->sent) 459 | #endif 460 | 461 | /* the presence of this union forces similar struct layout */ 462 | union ev_any_watcher 463 | { 464 | struct ev_watcher w; 465 | struct ev_watcher_list wl; 466 | 467 | struct ev_io io; 468 | struct ev_timer timer; 469 | struct ev_periodic periodic; 470 | struct ev_signal signal; 471 | struct ev_child child; 472 | #if EV_STAT_ENABLE 473 | struct ev_stat stat; 474 | #endif 475 | #if EV_IDLE_ENABLE 476 | struct ev_idle idle; 477 | #endif 478 | struct ev_prepare prepare; 479 | struct ev_check check; 480 | #if EV_FORK_ENABLE 481 | struct ev_fork fork; 482 | #endif 483 | #if EV_CLEANUP_ENABLE 484 | struct ev_cleanup cleanup; 485 | #endif 486 | #if EV_EMBED_ENABLE 487 | struct ev_embed embed; 488 | #endif 489 | #if EV_ASYNC_ENABLE 490 | struct ev_async async; 491 | #endif 492 | }; 493 | 494 | /* flag bits for ev_default_loop and ev_loop_new */ 495 | enum { 496 | /* the default */ 497 | EVFLAG_AUTO = 0x00000000U, /* not quite a mask */ 498 | /* flag bits */ 499 | EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */ 500 | EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */ 501 | /* debugging/feature disable */ 502 | EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */ 503 | #if EV_COMPAT3 504 | EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */ 505 | #endif 506 | EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */ 507 | EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */ 508 | }; 509 | 510 | /* method bits to be ored together */ 511 | enum { 512 | EVBACKEND_SELECT = 0x00000001U, /* about anywhere */ 513 | EVBACKEND_POLL = 0x00000002U, /* !win */ 514 | EVBACKEND_EPOLL = 0x00000004U, /* linux */ 515 | EVBACKEND_KQUEUE = 0x00000008U, /* bsd */ 516 | EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */ 517 | EVBACKEND_PORT = 0x00000020U, /* solaris 10 */ 518 | EVBACKEND_ALL = 0x0000003FU, /* all known backends */ 519 | EVBACKEND_MASK = 0x0000FFFFU /* all future backends */ 520 | }; 521 | 522 | #if EV_PROTOTYPES 523 | EV_API_DECL int ev_version_major (void) EV_THROW; 524 | EV_API_DECL int ev_version_minor (void) EV_THROW; 525 | 526 | EV_API_DECL unsigned int ev_supported_backends (void) EV_THROW; 527 | EV_API_DECL unsigned int ev_recommended_backends (void) EV_THROW; 528 | EV_API_DECL unsigned int ev_embeddable_backends (void) EV_THROW; 529 | 530 | EV_API_DECL ev_tstamp ev_time (void) EV_THROW; 531 | EV_API_DECL void ev_sleep (ev_tstamp delay) EV_THROW; /* sleep for a while */ 532 | 533 | /* Sets the allocation function to use, works like realloc. 534 | * It is used to allocate and free memory. 535 | * If it returns zero when memory needs to be allocated, the library might abort 536 | * or take some potentially destructive action. 537 | * The default is your system realloc function. 538 | */ 539 | EV_API_DECL void ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW; 540 | 541 | /* set the callback function to call on a 542 | * retryable syscall error 543 | * (such as failed select, poll, epoll_wait) 544 | */ 545 | EV_API_DECL void ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW; 546 | 547 | #if EV_MULTIPLICITY 548 | 549 | /* the default loop is the only one that handles signals and child watchers */ 550 | /* you can call this as often as you like */ 551 | EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; 552 | 553 | #ifdef EV_API_STATIC 554 | EV_API_DECL struct ev_loop *ev_default_loop_ptr; 555 | #endif 556 | 557 | EV_INLINE struct ev_loop * 558 | ev_default_loop_uc_ (void) EV_THROW 559 | { 560 | extern struct ev_loop *ev_default_loop_ptr; 561 | 562 | return ev_default_loop_ptr; 563 | } 564 | 565 | EV_INLINE int 566 | ev_is_default_loop (EV_P) EV_THROW 567 | { 568 | return EV_A == EV_DEFAULT_UC; 569 | } 570 | 571 | /* create and destroy alternative loops that don't handle signals */ 572 | EV_API_DECL struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0)) EV_THROW; 573 | 574 | EV_API_DECL ev_tstamp ev_now (EV_P) EV_THROW; /* time w.r.t. timers and the eventloop, updated after each poll */ 575 | 576 | #else 577 | 578 | EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */ 579 | 580 | EV_API_DECL ev_tstamp ev_rt_now; 581 | 582 | EV_INLINE ev_tstamp 583 | ev_now (void) EV_THROW 584 | { 585 | return ev_rt_now; 586 | } 587 | 588 | /* looks weird, but ev_is_default_loop (EV_A) still works if this exists */ 589 | EV_INLINE int 590 | ev_is_default_loop (void) EV_THROW 591 | { 592 | return 1; 593 | } 594 | 595 | #endif /* multiplicity */ 596 | 597 | /* destroy event loops, also works for the default loop */ 598 | EV_API_DECL void ev_loop_destroy (EV_P); 599 | 600 | /* this needs to be called after fork, to duplicate the loop */ 601 | /* when you want to re-use it in the child */ 602 | /* you can call it in either the parent or the child */ 603 | /* you can actually call it at any time, anywhere :) */ 604 | EV_API_DECL void ev_loop_fork (EV_P) EV_THROW; 605 | 606 | EV_API_DECL unsigned int ev_backend (EV_P) EV_THROW; /* backend in use by loop */ 607 | 608 | EV_API_DECL void ev_now_update (EV_P) EV_THROW; /* update event loop time */ 609 | 610 | #if EV_WALK_ENABLE 611 | /* walk (almost) all watchers in the loop of a given type, invoking the */ 612 | /* callback on every such watcher. The callback might stop the watcher, */ 613 | /* but do nothing else with the loop */ 614 | EV_API_DECL void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW; 615 | #endif 616 | 617 | #endif /* prototypes */ 618 | 619 | /* ev_run flags values */ 620 | enum { 621 | EVRUN_NOWAIT = 1, /* do not block/wait */ 622 | EVRUN_ONCE = 2 /* block *once* only */ 623 | }; 624 | 625 | /* ev_break how values */ 626 | enum { 627 | EVBREAK_CANCEL = 0, /* undo unloop */ 628 | EVBREAK_ONE = 1, /* unloop once */ 629 | EVBREAK_ALL = 2 /* unloop all loops */ 630 | }; 631 | 632 | #if EV_PROTOTYPES 633 | EV_API_DECL int ev_run (EV_P_ int flags EV_CPP (= 0)); 634 | EV_API_DECL void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)) EV_THROW; /* break out of the loop */ 635 | 636 | /* 637 | * ref/unref can be used to add or remove a refcount on the mainloop. every watcher 638 | * keeps one reference. if you have a long-running watcher you never unregister that 639 | * should not keep ev_loop from running, unref() after starting, and ref() before stopping. 640 | */ 641 | EV_API_DECL void ev_ref (EV_P) EV_THROW; 642 | EV_API_DECL void ev_unref (EV_P) EV_THROW; 643 | 644 | /* 645 | * convenience function, wait for a single event, without registering an event watcher 646 | * if timeout is < 0, do wait indefinitely 647 | */ 648 | EV_API_DECL void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW; 649 | 650 | # if EV_FEATURE_API 651 | EV_API_DECL unsigned int ev_iteration (EV_P) EV_THROW; /* number of loop iterations */ 652 | EV_API_DECL unsigned int ev_depth (EV_P) EV_THROW; /* #ev_loop enters - #ev_loop leaves */ 653 | EV_API_DECL void ev_verify (EV_P) EV_THROW; /* abort if loop data corrupted */ 654 | 655 | EV_API_DECL void ev_set_io_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ 656 | EV_API_DECL void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ 657 | 658 | /* advanced stuff for threading etc. support, see docs */ 659 | EV_API_DECL void ev_set_userdata (EV_P_ void *data) EV_THROW; 660 | EV_API_DECL void *ev_userdata (EV_P) EV_THROW; 661 | typedef void (*ev_loop_callback)(EV_P); 662 | EV_API_DECL void ev_set_invoke_pending_cb (EV_P_ ev_loop_callback invoke_pending_cb) EV_THROW; 663 | /* C++ doesn't allow the use of the ev_loop_callback typedef here, so we need to spell it out*/ 664 | EV_API_DECL void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV_P) EV_THROW) EV_THROW; 665 | 666 | EV_API_DECL unsigned int ev_pending_count (EV_P) EV_THROW; /* number of pending events, if any */ 667 | EV_API_DECL void ev_invoke_pending (EV_P); /* invoke all pending watchers */ 668 | 669 | /* 670 | * stop/start the timer handling. 671 | */ 672 | EV_API_DECL void ev_suspend (EV_P) EV_THROW; 673 | EV_API_DECL void ev_resume (EV_P) EV_THROW; 674 | #endif 675 | 676 | #endif 677 | 678 | /* these may evaluate ev multiple times, and the other arguments at most once */ 679 | /* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */ 680 | #define ev_init(ev,cb_) do { \ 681 | ((ev_watcher *)(void *)(ev))->active = \ 682 | ((ev_watcher *)(void *)(ev))->pending = 0; \ 683 | ev_set_priority ((ev), 0); \ 684 | ev_set_cb ((ev), cb_); \ 685 | } while (0) 686 | 687 | #define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0) 688 | #define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0) 689 | #define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0) 690 | #define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0) 691 | #define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0) 692 | #define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0) 693 | #define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */ 694 | #define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */ 695 | #define ev_check_set(ev) /* nop, yes, this is a serious in-joke */ 696 | #define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0) 697 | #define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */ 698 | #define ev_cleanup_set(ev) /* nop, yes, this is a serious in-joke */ 699 | #define ev_async_set(ev) /* nop, yes, this is a serious in-joke */ 700 | 701 | #define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0) 702 | #define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0) 703 | #define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0) 704 | #define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0) 705 | #define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0) 706 | #define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0) 707 | #define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0) 708 | #define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0) 709 | #define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0) 710 | #define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0) 711 | #define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0) 712 | #define ev_cleanup_init(ev,cb) do { ev_init ((ev), (cb)); ev_cleanup_set ((ev)); } while (0) 713 | #define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0) 714 | 715 | #define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */ 716 | #define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */ 717 | 718 | #define ev_cb(ev) (ev)->cb /* rw */ 719 | 720 | #if EV_MINPRI == EV_MAXPRI 721 | # define ev_priority(ev) ((ev), EV_MINPRI) 722 | # define ev_set_priority(ev,pri) ((ev), (pri)) 723 | #else 724 | # define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority)) 725 | # define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri) 726 | #endif 727 | 728 | #define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at) 729 | 730 | #ifndef ev_set_cb 731 | # define ev_set_cb(ev,cb_) ev_cb (ev) = (cb_) 732 | #endif 733 | 734 | /* stopping (enabling, adding) a watcher does nothing if it is already running */ 735 | /* stopping (disabling, deleting) a watcher does nothing unless it's already running */ 736 | #if EV_PROTOTYPES 737 | 738 | /* feeds an event into a watcher as if the event actually occurred */ 739 | /* accepts any ev_watcher type */ 740 | EV_API_DECL void ev_feed_event (EV_P_ void *w, int revents) EV_THROW; 741 | EV_API_DECL void ev_feed_fd_event (EV_P_ int fd, int revents) EV_THROW; 742 | #if EV_SIGNAL_ENABLE 743 | EV_API_DECL void ev_feed_signal (int signum) EV_THROW; 744 | EV_API_DECL void ev_feed_signal_event (EV_P_ int signum) EV_THROW; 745 | #endif 746 | EV_API_DECL void ev_invoke (EV_P_ void *w, int revents); 747 | EV_API_DECL int ev_clear_pending (EV_P_ void *w) EV_THROW; 748 | 749 | EV_API_DECL void ev_io_start (EV_P_ ev_io *w) EV_THROW; 750 | EV_API_DECL void ev_io_stop (EV_P_ ev_io *w) EV_THROW; 751 | 752 | EV_API_DECL void ev_timer_start (EV_P_ ev_timer *w) EV_THROW; 753 | EV_API_DECL void ev_timer_stop (EV_P_ ev_timer *w) EV_THROW; 754 | /* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */ 755 | EV_API_DECL void ev_timer_again (EV_P_ ev_timer *w) EV_THROW; 756 | /* return remaining time */ 757 | EV_API_DECL ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW; 758 | 759 | #if EV_PERIODIC_ENABLE 760 | EV_API_DECL void ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW; 761 | EV_API_DECL void ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW; 762 | EV_API_DECL void ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW; 763 | #endif 764 | 765 | /* only supported in the default loop */ 766 | #if EV_SIGNAL_ENABLE 767 | EV_API_DECL void ev_signal_start (EV_P_ ev_signal *w) EV_THROW; 768 | EV_API_DECL void ev_signal_stop (EV_P_ ev_signal *w) EV_THROW; 769 | #endif 770 | 771 | /* only supported in the default loop */ 772 | # if EV_CHILD_ENABLE 773 | EV_API_DECL void ev_child_start (EV_P_ ev_child *w) EV_THROW; 774 | EV_API_DECL void ev_child_stop (EV_P_ ev_child *w) EV_THROW; 775 | # endif 776 | 777 | # if EV_STAT_ENABLE 778 | EV_API_DECL void ev_stat_start (EV_P_ ev_stat *w) EV_THROW; 779 | EV_API_DECL void ev_stat_stop (EV_P_ ev_stat *w) EV_THROW; 780 | EV_API_DECL void ev_stat_stat (EV_P_ ev_stat *w) EV_THROW; 781 | # endif 782 | 783 | # if EV_IDLE_ENABLE 784 | EV_API_DECL void ev_idle_start (EV_P_ ev_idle *w) EV_THROW; 785 | EV_API_DECL void ev_idle_stop (EV_P_ ev_idle *w) EV_THROW; 786 | # endif 787 | 788 | #if EV_PREPARE_ENABLE 789 | EV_API_DECL void ev_prepare_start (EV_P_ ev_prepare *w) EV_THROW; 790 | EV_API_DECL void ev_prepare_stop (EV_P_ ev_prepare *w) EV_THROW; 791 | #endif 792 | 793 | #if EV_CHECK_ENABLE 794 | EV_API_DECL void ev_check_start (EV_P_ ev_check *w) EV_THROW; 795 | EV_API_DECL void ev_check_stop (EV_P_ ev_check *w) EV_THROW; 796 | #endif 797 | 798 | # if EV_FORK_ENABLE 799 | EV_API_DECL void ev_fork_start (EV_P_ ev_fork *w) EV_THROW; 800 | EV_API_DECL void ev_fork_stop (EV_P_ ev_fork *w) EV_THROW; 801 | # endif 802 | 803 | # if EV_CLEANUP_ENABLE 804 | EV_API_DECL void ev_cleanup_start (EV_P_ ev_cleanup *w) EV_THROW; 805 | EV_API_DECL void ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_THROW; 806 | # endif 807 | 808 | # if EV_EMBED_ENABLE 809 | /* only supported when loop to be embedded is in fact embeddable */ 810 | EV_API_DECL void ev_embed_start (EV_P_ ev_embed *w) EV_THROW; 811 | EV_API_DECL void ev_embed_stop (EV_P_ ev_embed *w) EV_THROW; 812 | EV_API_DECL void ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW; 813 | # endif 814 | 815 | # if EV_ASYNC_ENABLE 816 | EV_API_DECL void ev_async_start (EV_P_ ev_async *w) EV_THROW; 817 | EV_API_DECL void ev_async_stop (EV_P_ ev_async *w) EV_THROW; 818 | EV_API_DECL void ev_async_send (EV_P_ ev_async *w) EV_THROW; 819 | # endif 820 | 821 | #if EV_COMPAT3 822 | #define EVLOOP_NONBLOCK EVRUN_NOWAIT 823 | #define EVLOOP_ONESHOT EVRUN_ONCE 824 | #define EVUNLOOP_CANCEL EVBREAK_CANCEL 825 | #define EVUNLOOP_ONE EVBREAK_ONE 826 | #define EVUNLOOP_ALL EVBREAK_ALL 827 | #if EV_PROTOTYPES 828 | EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } 829 | EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } 830 | EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } 831 | EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } 832 | #if EV_FEATURE_API 833 | EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); } 834 | EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); } 835 | EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); } 836 | #endif 837 | #endif 838 | #else 839 | typedef struct ev_loop ev_loop; 840 | #endif 841 | 842 | #endif 843 | 844 | EV_CPP(}) 845 | 846 | #endif 847 | 848 | -------------------------------------------------------------------------------- /src/ev_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev epoll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* 41 | * general notes about epoll: 42 | * 43 | * a) epoll silently removes fds from the fd set. as nothing tells us 44 | * that an fd has been removed otherwise, we have to continually 45 | * "rearm" fds that we suspect *might* have changed (same 46 | * problem with kqueue, but much less costly there). 47 | * b) the fact that ADD != MOD creates a lot of extra syscalls due to a) 48 | * and seems not to have any advantage. 49 | * c) the inability to handle fork or file descriptors (think dup) 50 | * limits the applicability over poll, so this is not a generic 51 | * poll replacement. 52 | * d) epoll doesn't work the same as select with many file descriptors 53 | * (such as files). while not critical, no other advanced interface 54 | * seems to share this (rather non-unixy) limitation. 55 | * e) epoll claims to be embeddable, but in practise you never get 56 | * a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32). 57 | * f) epoll_ctl returning EPERM means the fd is always ready. 58 | * 59 | * lots of "weird code" and complication handling in this file is due 60 | * to these design problems with epoll, as we try very hard to avoid 61 | * epoll_ctl syscalls for common usage patterns and handle the breakage 62 | * ensuing from receiving events for closed and otherwise long gone 63 | * file descriptors. 64 | */ 65 | 66 | #include 67 | 68 | #define EV_EMASK_EPERM 0x80 69 | 70 | static void 71 | epoll_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | struct epoll_event ev; 74 | unsigned char oldmask; 75 | 76 | /* 77 | * we handle EPOLL_CTL_DEL by ignoring it here 78 | * on the assumption that the fd is gone anyways 79 | * if that is wrong, we have to handle the spurious 80 | * event in epoll_poll. 81 | * if the fd is added again, we try to ADD it, and, if that 82 | * fails, we assume it still has the same eventmask. 83 | */ 84 | if (!nev) 85 | return; 86 | 87 | oldmask = anfds [fd].emask; 88 | anfds [fd].emask = nev; 89 | 90 | /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */ 91 | ev.data.u64 = (uint64_t)(uint32_t)fd 92 | | ((uint64_t)(uint32_t)++anfds [fd].egen << 32); 93 | ev.events = (nev & EV_READ ? EPOLLIN : 0) 94 | | (nev & EV_WRITE ? EPOLLOUT : 0); 95 | 96 | if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev))) 97 | return; 98 | 99 | if (expect_true (errno == ENOENT)) 100 | { 101 | /* if ENOENT then the fd went away, so try to do the right thing */ 102 | if (!nev) 103 | goto dec_egen; 104 | 105 | if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev)) 106 | return; 107 | } 108 | else if (expect_true (errno == EEXIST)) 109 | { 110 | /* EEXIST means we ignored a previous DEL, but the fd is still active */ 111 | /* if the kernel mask is the same as the new mask, we assume it hasn't changed */ 112 | if (oldmask == nev) 113 | goto dec_egen; 114 | 115 | if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev)) 116 | return; 117 | } 118 | else if (expect_true (errno == EPERM)) 119 | { 120 | /* EPERM means the fd is always ready, but epoll is too snobbish */ 121 | /* to handle it, unlike select or poll. */ 122 | anfds [fd].emask = EV_EMASK_EPERM; 123 | 124 | /* add fd to epoll_eperms, if not already inside */ 125 | if (!(oldmask & EV_EMASK_EPERM)) 126 | { 127 | array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2); 128 | epoll_eperms [epoll_epermcnt++] = fd; 129 | } 130 | 131 | return; 132 | } 133 | 134 | fd_kill (EV_A_ fd); 135 | 136 | dec_egen: 137 | /* we didn't successfully call epoll_ctl, so decrement the generation counter again */ 138 | --anfds [fd].egen; 139 | } 140 | 141 | static void 142 | epoll_poll (EV_P_ ev_tstamp timeout) 143 | { 144 | int i; 145 | int eventcnt; 146 | 147 | if (expect_false (epoll_epermcnt)) 148 | timeout = 0.; 149 | 150 | /* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */ 151 | /* the default libev max wait time, however. */ 152 | EV_RELEASE_CB; 153 | eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3); 154 | EV_ACQUIRE_CB; 155 | 156 | if (expect_false (eventcnt < 0)) 157 | { 158 | if (errno != EINTR) 159 | ev_syserr ("(libev) epoll_wait"); 160 | 161 | return; 162 | } 163 | 164 | for (i = 0; i < eventcnt; ++i) 165 | { 166 | struct epoll_event *ev = epoll_events + i; 167 | 168 | int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */ 169 | int want = anfds [fd].events; 170 | int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0) 171 | | (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0); 172 | 173 | /* 174 | * check for spurious notification. 175 | * this only finds spurious notifications on egen updates 176 | * other spurious notifications will be found by epoll_ctl, below 177 | * we assume that fd is always in range, as we never shrink the anfds array 178 | */ 179 | if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32))) 180 | { 181 | /* recreate kernel state */ 182 | postfork = 1; 183 | continue; 184 | } 185 | 186 | if (expect_false (got & ~want)) 187 | { 188 | anfds [fd].emask = want; 189 | 190 | /* 191 | * we received an event but are not interested in it, try mod or del 192 | * this often happens because we optimistically do not unregister fds 193 | * when we are no longer interested in them, but also when we get spurious 194 | * notifications for fds from another process. this is partially handled 195 | * above with the gencounter check (== our fd is not the event fd), and 196 | * partially here, when epoll_ctl returns an error (== a child has the fd 197 | * but we closed it). 198 | */ 199 | ev->events = (want & EV_READ ? EPOLLIN : 0) 200 | | (want & EV_WRITE ? EPOLLOUT : 0); 201 | 202 | /* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */ 203 | /* which is fortunately easy to do for us. */ 204 | if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev)) 205 | { 206 | postfork = 1; /* an error occurred, recreate kernel state */ 207 | continue; 208 | } 209 | } 210 | 211 | fd_event (EV_A_ fd, got); 212 | } 213 | 214 | /* if the receive array was full, increase its size */ 215 | if (expect_false (eventcnt == epoll_eventmax)) 216 | { 217 | ev_free (epoll_events); 218 | epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1); 219 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 220 | } 221 | 222 | /* now synthesize events for all fds where epoll fails, while select works... */ 223 | for (i = epoll_epermcnt; i--; ) 224 | { 225 | int fd = epoll_eperms [i]; 226 | unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE); 227 | 228 | if (anfds [fd].emask & EV_EMASK_EPERM && events) 229 | fd_event (EV_A_ fd, events); 230 | else 231 | { 232 | epoll_eperms [i] = epoll_eperms [--epoll_epermcnt]; 233 | anfds [fd].emask = 0; 234 | } 235 | } 236 | } 237 | 238 | int inline_size 239 | epoll_init (EV_P_ int flags) 240 | { 241 | #ifdef EPOLL_CLOEXEC 242 | backend_fd = epoll_create1 (EPOLL_CLOEXEC); 243 | 244 | if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS)) 245 | #endif 246 | backend_fd = epoll_create (256); 247 | 248 | if (backend_fd < 0) 249 | return 0; 250 | 251 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 252 | 253 | backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */ 254 | backend_modify = epoll_modify; 255 | backend_poll = epoll_poll; 256 | 257 | epoll_eventmax = 64; /* initial number of events receivable per poll */ 258 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 259 | 260 | return EVBACKEND_EPOLL; 261 | } 262 | 263 | void inline_size 264 | epoll_destroy (EV_P) 265 | { 266 | ev_free (epoll_events); 267 | array_free (epoll_eperm, EMPTY); 268 | } 269 | 270 | void inline_size 271 | epoll_fork (EV_P) 272 | { 273 | close (backend_fd); 274 | 275 | while ((backend_fd = epoll_create (256)) < 0) 276 | ev_syserr ("(libev) epoll_create"); 277 | 278 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 279 | 280 | fd_rearm_all (EV_A); 281 | } 282 | 283 | -------------------------------------------------------------------------------- /src/ev_kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev kqueue backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | void inline_speed 47 | kqueue_change (EV_P_ int fd, int filter, int flags, int fflags) 48 | { 49 | ++kqueue_changecnt; 50 | array_needsize (struct kevent, kqueue_changes, kqueue_changemax, kqueue_changecnt, EMPTY2); 51 | 52 | EV_SET (&kqueue_changes [kqueue_changecnt - 1], fd, filter, flags, fflags, 0, 0); 53 | } 54 | 55 | /* OS X at least needs this */ 56 | #ifndef EV_ENABLE 57 | # define EV_ENABLE 0 58 | #endif 59 | #ifndef NOTE_EOF 60 | # define NOTE_EOF 0 61 | #endif 62 | 63 | static void 64 | kqueue_modify (EV_P_ int fd, int oev, int nev) 65 | { 66 | if (oev != nev) 67 | { 68 | if (oev & EV_READ) 69 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_DELETE, 0); 70 | 71 | if (oev & EV_WRITE) 72 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_DELETE, 0); 73 | } 74 | 75 | /* to detect close/reopen reliably, we have to re-add */ 76 | /* event requests even when oev == nev */ 77 | 78 | if (nev & EV_READ) 79 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_ADD | EV_ENABLE, NOTE_EOF); 80 | 81 | if (nev & EV_WRITE) 82 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF); 83 | } 84 | 85 | static void 86 | kqueue_poll (EV_P_ ev_tstamp timeout) 87 | { 88 | int res, i; 89 | struct timespec ts; 90 | 91 | /* need to resize so there is enough space for errors */ 92 | if (kqueue_changecnt > kqueue_eventmax) 93 | { 94 | ev_free (kqueue_events); 95 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_changecnt); 96 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 97 | } 98 | 99 | EV_RELEASE_CB; 100 | EV_TS_SET (ts, timeout); 101 | res = kevent (backend_fd, kqueue_changes, kqueue_changecnt, kqueue_events, kqueue_eventmax, &ts); 102 | EV_ACQUIRE_CB; 103 | kqueue_changecnt = 0; 104 | 105 | if (expect_false (res < 0)) 106 | { 107 | if (errno != EINTR) 108 | ev_syserr ("(libev) kevent"); 109 | 110 | return; 111 | } 112 | 113 | for (i = 0; i < res; ++i) 114 | { 115 | int fd = kqueue_events [i].ident; 116 | 117 | if (expect_false (kqueue_events [i].flags & EV_ERROR)) 118 | { 119 | int err = kqueue_events [i].data; 120 | 121 | /* we are only interested in errors for fds that we are interested in :) */ 122 | if (anfds [fd].events) 123 | { 124 | if (err == ENOENT) /* resubmit changes on ENOENT */ 125 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 126 | else if (err == EBADF) /* on EBADF, we re-check the fd */ 127 | { 128 | if (fd_valid (fd)) 129 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 130 | else 131 | fd_kill (EV_A_ fd); 132 | } 133 | else /* on all other errors, we error out on the fd */ 134 | fd_kill (EV_A_ fd); 135 | } 136 | } 137 | else 138 | fd_event ( 139 | EV_A_ 140 | fd, 141 | kqueue_events [i].filter == EVFILT_READ ? EV_READ 142 | : kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE 143 | : 0 144 | ); 145 | } 146 | 147 | if (expect_false (res == kqueue_eventmax)) 148 | { 149 | ev_free (kqueue_events); 150 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_eventmax + 1); 151 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 152 | } 153 | } 154 | 155 | int inline_size 156 | kqueue_init (EV_P_ int flags) 157 | { 158 | /* initialize the kernel queue */ 159 | kqueue_fd_pid = getpid (); 160 | if ((backend_fd = kqueue ()) < 0) 161 | return 0; 162 | 163 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 164 | 165 | backend_mintime = 1e-9; /* apparently, they did the right thing in freebsd */ 166 | backend_modify = kqueue_modify; 167 | backend_poll = kqueue_poll; 168 | 169 | kqueue_eventmax = 64; /* initial number of events receivable per poll */ 170 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 171 | 172 | kqueue_changes = 0; 173 | kqueue_changemax = 0; 174 | kqueue_changecnt = 0; 175 | 176 | return EVBACKEND_KQUEUE; 177 | } 178 | 179 | void inline_size 180 | kqueue_destroy (EV_P) 181 | { 182 | ev_free (kqueue_events); 183 | ev_free (kqueue_changes); 184 | } 185 | 186 | void inline_size 187 | kqueue_fork (EV_P) 188 | { 189 | /* some BSD kernels don't just destroy the kqueue itself, 190 | * but also close the fd, which isn't documented, and 191 | * impossible to support properly. 192 | * we remember the pid of the kqueue call and only close 193 | * the fd if the pid is still the same. 194 | * this leaks fds on sane kernels, but BSD interfaces are 195 | * notoriously buggy and rarely get fixed. 196 | */ 197 | pid_t newpid = getpid (); 198 | 199 | if (newpid == kqueue_fd_pid) 200 | close (backend_fd); 201 | 202 | kqueue_fd_pid = newpid; 203 | while ((backend_fd = kqueue ()) < 0) 204 | ev_syserr ("(libev) kqueue"); 205 | 206 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 207 | 208 | /* re-register interest in fds */ 209 | fd_rearm_all (EV_A); 210 | } 211 | 212 | /* sys/event.h defines EV_ERROR */ 213 | #undef EV_ERROR 214 | 215 | -------------------------------------------------------------------------------- /src/ev_poll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev poll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | 42 | void inline_size 43 | pollidx_init (int *base, int count) 44 | { 45 | /* consider using memset (.., -1, ...), which is practically guaranteed 46 | * to work on all systems implementing poll */ 47 | while (count--) 48 | *base++ = -1; 49 | } 50 | 51 | static void 52 | poll_modify (EV_P_ int fd, int oev, int nev) 53 | { 54 | int idx; 55 | 56 | if (oev == nev) 57 | return; 58 | 59 | array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init); 60 | 61 | idx = pollidxs [fd]; 62 | 63 | if (idx < 0) /* need to allocate a new pollfd */ 64 | { 65 | pollidxs [fd] = idx = pollcnt++; 66 | array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2); 67 | polls [idx].fd = fd; 68 | } 69 | 70 | assert (polls [idx].fd == fd); 71 | 72 | if (nev) 73 | polls [idx].events = 74 | (nev & EV_READ ? POLLIN : 0) 75 | | (nev & EV_WRITE ? POLLOUT : 0); 76 | else /* remove pollfd */ 77 | { 78 | pollidxs [fd] = -1; 79 | 80 | if (expect_true (idx < --pollcnt)) 81 | { 82 | polls [idx] = polls [pollcnt]; 83 | pollidxs [polls [idx].fd] = idx; 84 | } 85 | } 86 | } 87 | 88 | static void 89 | poll_poll (EV_P_ ev_tstamp timeout) 90 | { 91 | struct pollfd *p; 92 | int res; 93 | 94 | EV_RELEASE_CB; 95 | res = poll (polls, pollcnt, timeout * 1e3); 96 | EV_ACQUIRE_CB; 97 | 98 | if (expect_false (res < 0)) 99 | { 100 | if (errno == EBADF) 101 | fd_ebadf (EV_A); 102 | else if (errno == ENOMEM && !syserr_cb) 103 | fd_enomem (EV_A); 104 | else if (errno != EINTR) 105 | ev_syserr ("(libev) poll"); 106 | } 107 | else 108 | for (p = polls; res; ++p) 109 | { 110 | assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt)); 111 | 112 | if (expect_false (p->revents)) /* this expect is debatable */ 113 | { 114 | --res; 115 | 116 | if (expect_false (p->revents & POLLNVAL)) 117 | fd_kill (EV_A_ p->fd); 118 | else 119 | fd_event ( 120 | EV_A_ 121 | p->fd, 122 | (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 123 | | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 124 | ); 125 | } 126 | } 127 | } 128 | 129 | int inline_size 130 | poll_init (EV_P_ int flags) 131 | { 132 | backend_mintime = 1e-3; 133 | backend_modify = poll_modify; 134 | backend_poll = poll_poll; 135 | 136 | pollidxs = 0; pollidxmax = 0; 137 | polls = 0; pollmax = 0; pollcnt = 0; 138 | 139 | return EVBACKEND_POLL; 140 | } 141 | 142 | void inline_size 143 | poll_destroy (EV_P) 144 | { 145 | ev_free (pollidxs); 146 | ev_free (polls); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /src/ev_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev solaris event port backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* useful reading: 41 | * 42 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6268715 (random results) 43 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6455223 (just totally broken) 44 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6873782 (manpage ETIME) 45 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6874410 (implementation ETIME) 46 | * http://www.mail-archive.com/networking-discuss@opensolaris.org/msg11898.html ETIME vs. nget 47 | * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/event_port.c (libc) 48 | * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325 (kernel) 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | void inline_speed 59 | port_associate_and_check (EV_P_ int fd, int ev) 60 | { 61 | if (0 > 62 | port_associate ( 63 | backend_fd, PORT_SOURCE_FD, fd, 64 | (ev & EV_READ ? POLLIN : 0) 65 | | (ev & EV_WRITE ? POLLOUT : 0), 66 | 0 67 | ) 68 | ) 69 | { 70 | if (errno == EBADFD) 71 | fd_kill (EV_A_ fd); 72 | else 73 | ev_syserr ("(libev) port_associate"); 74 | } 75 | } 76 | 77 | static void 78 | port_modify (EV_P_ int fd, int oev, int nev) 79 | { 80 | /* we need to reassociate no matter what, as closes are 81 | * once more silently being discarded. 82 | */ 83 | if (!nev) 84 | { 85 | if (oev) 86 | port_dissociate (backend_fd, PORT_SOURCE_FD, fd); 87 | } 88 | else 89 | port_associate_and_check (EV_A_ fd, nev); 90 | } 91 | 92 | static void 93 | port_poll (EV_P_ ev_tstamp timeout) 94 | { 95 | int res, i; 96 | struct timespec ts; 97 | uint_t nget = 1; 98 | 99 | /* we initialise this to something we will skip in the loop, as */ 100 | /* port_getn can return with nget unchanged, but no indication */ 101 | /* whether it was the original value or has been updated :/ */ 102 | port_events [0].portev_source = 0; 103 | 104 | EV_RELEASE_CB; 105 | EV_TS_SET (ts, timeout); 106 | res = port_getn (backend_fd, port_events, port_eventmax, &nget, &ts); 107 | EV_ACQUIRE_CB; 108 | 109 | /* port_getn may or may not set nget on error */ 110 | /* so we rely on port_events [0].portev_source not being updated */ 111 | if (res == -1 && errno != ETIME && errno != EINTR) 112 | ev_syserr ("(libev) port_getn (see http://bugs.opensolaris.org/view_bug.do?bug_id=6268715, try LIBEV_FLAGS=3 env variable)"); 113 | 114 | for (i = 0; i < nget; ++i) 115 | { 116 | if (port_events [i].portev_source == PORT_SOURCE_FD) 117 | { 118 | int fd = port_events [i].portev_object; 119 | 120 | fd_event ( 121 | EV_A_ 122 | fd, 123 | (port_events [i].portev_events & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 124 | | (port_events [i].portev_events & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 125 | ); 126 | 127 | fd_change (EV_A_ fd, EV__IOFDSET); 128 | } 129 | } 130 | 131 | if (expect_false (nget == port_eventmax)) 132 | { 133 | ev_free (port_events); 134 | port_eventmax = array_nextsize (sizeof (port_event_t), port_eventmax, port_eventmax + 1); 135 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 136 | } 137 | } 138 | 139 | int inline_size 140 | port_init (EV_P_ int flags) 141 | { 142 | /* Initialize the kernel queue */ 143 | if ((backend_fd = port_create ()) < 0) 144 | return 0; 145 | 146 | assert (("libev: PORT_SOURCE_FD must not be zero", PORT_SOURCE_FD)); 147 | 148 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 149 | 150 | /* if my reading of the opensolaris kernel sources are correct, then 151 | * opensolaris does something very stupid: it checks if the time has already 152 | * elapsed and doesn't round up if that is the case,m otherwise it DOES round 153 | * up. Since we can't know what the case is, we need to guess by using a 154 | * "large enough" timeout. Normally, 1e-9 would be correct. 155 | */ 156 | backend_mintime = 1e-3; /* needed to compensate for port_getn returning early */ 157 | backend_modify = port_modify; 158 | backend_poll = port_poll; 159 | 160 | port_eventmax = 64; /* initial number of events receivable per poll */ 161 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 162 | 163 | return EVBACKEND_PORT; 164 | } 165 | 166 | void inline_size 167 | port_destroy (EV_P) 168 | { 169 | ev_free (port_events); 170 | } 171 | 172 | void inline_size 173 | port_fork (EV_P) 174 | { 175 | close (backend_fd); 176 | 177 | while ((backend_fd = port_create ()) < 0) 178 | ev_syserr ("(libev) port"); 179 | 180 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 181 | 182 | /* re-register interest in fds */ 183 | fd_rearm_all (EV_A); 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/ev_select.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev select fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef _WIN32 41 | /* for unix systems */ 42 | # include 43 | # ifndef __hpux 44 | /* for REAL unix systems */ 45 | # include 46 | # endif 47 | #endif 48 | 49 | #ifndef EV_SELECT_USE_FD_SET 50 | # ifdef NFDBITS 51 | # define EV_SELECT_USE_FD_SET 0 52 | # else 53 | # define EV_SELECT_USE_FD_SET 1 54 | # endif 55 | #endif 56 | 57 | #if EV_SELECT_IS_WINSOCKET 58 | # undef EV_SELECT_USE_FD_SET 59 | # define EV_SELECT_USE_FD_SET 1 60 | # undef NFDBITS 61 | # define NFDBITS 0 62 | #endif 63 | 64 | #if !EV_SELECT_USE_FD_SET 65 | # define NFDBYTES (NFDBITS / 8) 66 | #endif 67 | 68 | #include 69 | 70 | static void 71 | select_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | if (oev == nev) 74 | return; 75 | 76 | { 77 | #if EV_SELECT_USE_FD_SET 78 | 79 | #if EV_SELECT_IS_WINSOCKET 80 | SOCKET handle = anfds [fd].handle; 81 | #else 82 | int handle = fd; 83 | #endif 84 | 85 | assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE)); 86 | 87 | /* FD_SET is broken on windows (it adds the fd to a set twice or more, 88 | * which eventually leads to overflows). Need to call it only on changes. 89 | */ 90 | #if EV_SELECT_IS_WINSOCKET 91 | if ((oev ^ nev) & EV_READ) 92 | #endif 93 | if (nev & EV_READ) 94 | FD_SET (handle, (fd_set *)vec_ri); 95 | else 96 | FD_CLR (handle, (fd_set *)vec_ri); 97 | 98 | #if EV_SELECT_IS_WINSOCKET 99 | if ((oev ^ nev) & EV_WRITE) 100 | #endif 101 | if (nev & EV_WRITE) 102 | FD_SET (handle, (fd_set *)vec_wi); 103 | else 104 | FD_CLR (handle, (fd_set *)vec_wi); 105 | 106 | #else 107 | 108 | int word = fd / NFDBITS; 109 | fd_mask mask = 1UL << (fd % NFDBITS); 110 | 111 | if (expect_false (vec_max <= word)) 112 | { 113 | int new_max = word + 1; 114 | 115 | vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES); 116 | vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */ 117 | vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES); 118 | vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */ 119 | #ifdef _WIN32 120 | vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */ 121 | #endif 122 | 123 | for (; vec_max < new_max; ++vec_max) 124 | ((fd_mask *)vec_ri) [vec_max] = 125 | ((fd_mask *)vec_wi) [vec_max] = 0; 126 | } 127 | 128 | ((fd_mask *)vec_ri) [word] |= mask; 129 | if (!(nev & EV_READ)) 130 | ((fd_mask *)vec_ri) [word] &= ~mask; 131 | 132 | ((fd_mask *)vec_wi) [word] |= mask; 133 | if (!(nev & EV_WRITE)) 134 | ((fd_mask *)vec_wi) [word] &= ~mask; 135 | #endif 136 | } 137 | } 138 | 139 | static void 140 | select_poll (EV_P_ ev_tstamp timeout) 141 | { 142 | struct timeval tv; 143 | int res; 144 | int fd_setsize; 145 | 146 | EV_RELEASE_CB; 147 | EV_TV_SET (tv, timeout); 148 | 149 | #if EV_SELECT_USE_FD_SET 150 | fd_setsize = sizeof (fd_set); 151 | #else 152 | fd_setsize = vec_max * NFDBYTES; 153 | #endif 154 | 155 | memcpy (vec_ro, vec_ri, fd_setsize); 156 | memcpy (vec_wo, vec_wi, fd_setsize); 157 | 158 | #ifdef _WIN32 159 | /* pass in the write set as except set. 160 | * the idea behind this is to work around a windows bug that causes 161 | * errors to be reported as an exception and not by setting 162 | * the writable bit. this is so uncontrollably lame. 163 | */ 164 | memcpy (vec_eo, vec_wi, fd_setsize); 165 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv); 166 | #elif EV_SELECT_USE_FD_SET 167 | fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE; 168 | res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 169 | #else 170 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 171 | #endif 172 | EV_ACQUIRE_CB; 173 | 174 | if (expect_false (res < 0)) 175 | { 176 | #if EV_SELECT_IS_WINSOCKET 177 | errno = WSAGetLastError (); 178 | #endif 179 | #ifdef WSABASEERR 180 | /* on windows, select returns incompatible error codes, fix this */ 181 | if (errno >= WSABASEERR && errno < WSABASEERR + 1000) 182 | if (errno == WSAENOTSOCK) 183 | errno = EBADF; 184 | else 185 | errno -= WSABASEERR; 186 | #endif 187 | 188 | #ifdef _WIN32 189 | /* select on windows erroneously returns EINVAL when no fd sets have been 190 | * provided (this is documented). what microsoft doesn't tell you that this bug 191 | * exists even when the fd sets _are_ provided, so we have to check for this bug 192 | * here and emulate by sleeping manually. 193 | * we also get EINVAL when the timeout is invalid, but we ignore this case here 194 | * and assume that EINVAL always means: you have to wait manually. 195 | */ 196 | if (errno == EINVAL) 197 | { 198 | if (timeout) 199 | { 200 | unsigned long ms = timeout * 1e3; 201 | Sleep (ms ? ms : 1); 202 | } 203 | 204 | return; 205 | } 206 | #endif 207 | 208 | if (errno == EBADF) 209 | fd_ebadf (EV_A); 210 | else if (errno == ENOMEM && !syserr_cb) 211 | fd_enomem (EV_A); 212 | else if (errno != EINTR) 213 | ev_syserr ("(libev) select"); 214 | 215 | return; 216 | } 217 | 218 | #if EV_SELECT_USE_FD_SET 219 | 220 | { 221 | int fd; 222 | 223 | for (fd = 0; fd < anfdmax; ++fd) 224 | if (anfds [fd].events) 225 | { 226 | int events = 0; 227 | #if EV_SELECT_IS_WINSOCKET 228 | SOCKET handle = anfds [fd].handle; 229 | #else 230 | int handle = fd; 231 | #endif 232 | 233 | if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ; 234 | if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE; 235 | #ifdef _WIN32 236 | if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE; 237 | #endif 238 | 239 | if (expect_true (events)) 240 | fd_event (EV_A_ fd, events); 241 | } 242 | } 243 | 244 | #else 245 | 246 | { 247 | int word, bit; 248 | for (word = vec_max; word--; ) 249 | { 250 | fd_mask word_r = ((fd_mask *)vec_ro) [word]; 251 | fd_mask word_w = ((fd_mask *)vec_wo) [word]; 252 | #ifdef _WIN32 253 | word_w |= ((fd_mask *)vec_eo) [word]; 254 | #endif 255 | 256 | if (word_r || word_w) 257 | for (bit = NFDBITS; bit--; ) 258 | { 259 | fd_mask mask = 1UL << bit; 260 | int events = 0; 261 | 262 | events |= word_r & mask ? EV_READ : 0; 263 | events |= word_w & mask ? EV_WRITE : 0; 264 | 265 | if (expect_true (events)) 266 | fd_event (EV_A_ word * NFDBITS + bit, events); 267 | } 268 | } 269 | } 270 | 271 | #endif 272 | } 273 | 274 | int inline_size 275 | select_init (EV_P_ int flags) 276 | { 277 | backend_mintime = 1e-6; 278 | backend_modify = select_modify; 279 | backend_poll = select_poll; 280 | 281 | #if EV_SELECT_USE_FD_SET 282 | vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); 283 | vec_ro = ev_malloc (sizeof (fd_set)); 284 | vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); 285 | vec_wo = ev_malloc (sizeof (fd_set)); 286 | #ifdef _WIN32 287 | vec_eo = ev_malloc (sizeof (fd_set)); 288 | #endif 289 | #else 290 | vec_max = 0; 291 | vec_ri = 0; 292 | vec_ro = 0; 293 | vec_wi = 0; 294 | vec_wo = 0; 295 | #ifdef _WIN32 296 | vec_eo = 0; 297 | #endif 298 | #endif 299 | 300 | return EVBACKEND_SELECT; 301 | } 302 | 303 | void inline_size 304 | select_destroy (EV_P) 305 | { 306 | ev_free (vec_ri); 307 | ev_free (vec_ro); 308 | ev_free (vec_wi); 309 | ev_free (vec_wo); 310 | #ifdef _WIN32 311 | ev_free (vec_eo); 312 | #endif 313 | } 314 | 315 | -------------------------------------------------------------------------------- /src/ev_vars.h: -------------------------------------------------------------------------------- 1 | /* 2 | * loop member variable declarations 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #define VARx(type,name) VAR(name, type name) 41 | 42 | VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */ 43 | VARx(ev_tstamp, mn_now) /* monotonic clock "now" */ 44 | VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */ 45 | 46 | /* for reverse feeding of events */ 47 | VARx(W *, rfeeds) 48 | VARx(int, rfeedmax) 49 | VARx(int, rfeedcnt) 50 | 51 | VAR (pendings, ANPENDING *pendings [NUMPRI]) 52 | VAR (pendingmax, int pendingmax [NUMPRI]) 53 | VAR (pendingcnt, int pendingcnt [NUMPRI]) 54 | VARx(int, pendingpri) /* highest priority currently pending */ 55 | VARx(ev_prepare, pending_w) /* dummy pending watcher */ 56 | 57 | VARx(ev_tstamp, io_blocktime) 58 | VARx(ev_tstamp, timeout_blocktime) 59 | 60 | VARx(int, backend) 61 | VARx(int, activecnt) /* total number of active events ("refcount") */ 62 | VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */ 63 | 64 | VARx(int, backend_fd) 65 | VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */ 66 | VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev)) 67 | VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout)) 68 | 69 | VARx(ANFD *, anfds) 70 | VARx(int, anfdmax) 71 | 72 | VAR (evpipe, int evpipe [2]) 73 | VARx(ev_io, pipe_w) 74 | VARx(EV_ATOMIC_T, pipe_write_wanted) 75 | VARx(EV_ATOMIC_T, pipe_write_skipped) 76 | 77 | #if !defined(_WIN32) || EV_GENWRAP 78 | VARx(pid_t, curpid) 79 | #endif 80 | 81 | VARx(char, postfork) /* true if we need to recreate kernel state after fork */ 82 | 83 | #if EV_USE_SELECT || EV_GENWRAP 84 | VARx(void *, vec_ri) 85 | VARx(void *, vec_ro) 86 | VARx(void *, vec_wi) 87 | VARx(void *, vec_wo) 88 | #if defined(_WIN32) || EV_GENWRAP 89 | VARx(void *, vec_eo) 90 | #endif 91 | VARx(int, vec_max) 92 | #endif 93 | 94 | #if EV_USE_POLL || EV_GENWRAP 95 | VARx(struct pollfd *, polls) 96 | VARx(int, pollmax) 97 | VARx(int, pollcnt) 98 | VARx(int *, pollidxs) /* maps fds into structure indices */ 99 | VARx(int, pollidxmax) 100 | #endif 101 | 102 | #if EV_USE_EPOLL || EV_GENWRAP 103 | VARx(struct epoll_event *, epoll_events) 104 | VARx(int, epoll_eventmax) 105 | VARx(int *, epoll_eperms) 106 | VARx(int, epoll_epermcnt) 107 | VARx(int, epoll_epermmax) 108 | #endif 109 | 110 | #if EV_USE_KQUEUE || EV_GENWRAP 111 | VARx(pid_t, kqueue_fd_pid) 112 | VARx(struct kevent *, kqueue_changes) 113 | VARx(int, kqueue_changemax) 114 | VARx(int, kqueue_changecnt) 115 | VARx(struct kevent *, kqueue_events) 116 | VARx(int, kqueue_eventmax) 117 | #endif 118 | 119 | #if EV_USE_PORT || EV_GENWRAP 120 | VARx(struct port_event *, port_events) 121 | VARx(int, port_eventmax) 122 | #endif 123 | 124 | #if EV_USE_IOCP || EV_GENWRAP 125 | VARx(HANDLE, iocp) 126 | #endif 127 | 128 | VARx(int *, fdchanges) 129 | VARx(int, fdchangemax) 130 | VARx(int, fdchangecnt) 131 | 132 | VARx(ANHE *, timers) 133 | VARx(int, timermax) 134 | VARx(int, timercnt) 135 | 136 | #if EV_PERIODIC_ENABLE || EV_GENWRAP 137 | VARx(ANHE *, periodics) 138 | VARx(int, periodicmax) 139 | VARx(int, periodiccnt) 140 | #endif 141 | 142 | #if EV_IDLE_ENABLE || EV_GENWRAP 143 | VAR (idles, ev_idle **idles [NUMPRI]) 144 | VAR (idlemax, int idlemax [NUMPRI]) 145 | VAR (idlecnt, int idlecnt [NUMPRI]) 146 | #endif 147 | VARx(int, idleall) /* total number */ 148 | 149 | VARx(struct ev_prepare **, prepares) 150 | VARx(int, preparemax) 151 | VARx(int, preparecnt) 152 | 153 | VARx(struct ev_check **, checks) 154 | VARx(int, checkmax) 155 | VARx(int, checkcnt) 156 | 157 | #if EV_FORK_ENABLE || EV_GENWRAP 158 | VARx(struct ev_fork **, forks) 159 | VARx(int, forkmax) 160 | VARx(int, forkcnt) 161 | #endif 162 | 163 | #if EV_CLEANUP_ENABLE || EV_GENWRAP 164 | VARx(struct ev_cleanup **, cleanups) 165 | VARx(int, cleanupmax) 166 | VARx(int, cleanupcnt) 167 | #endif 168 | 169 | #if EV_ASYNC_ENABLE || EV_GENWRAP 170 | VARx(EV_ATOMIC_T, async_pending) 171 | VARx(struct ev_async **, asyncs) 172 | VARx(int, asyncmax) 173 | VARx(int, asynccnt) 174 | #endif 175 | 176 | #if EV_USE_INOTIFY || EV_GENWRAP 177 | VARx(int, fs_fd) 178 | VARx(ev_io, fs_w) 179 | VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */ 180 | VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE]) 181 | #endif 182 | 183 | VARx(EV_ATOMIC_T, sig_pending) 184 | #if EV_USE_SIGNALFD || EV_GENWRAP 185 | VARx(int, sigfd) 186 | VARx(ev_io, sigfd_w) 187 | VARx(sigset_t, sigfd_set) 188 | #endif 189 | 190 | VARx(unsigned int, origflags) /* original loop flags */ 191 | 192 | #if EV_FEATURE_API || EV_GENWRAP 193 | VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */ 194 | VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */ 195 | 196 | VARx(void *, userdata) 197 | /* C++ doesn't support the ev_loop_callback typedef here. stinks. */ 198 | VAR (release_cb, void (*release_cb)(EV_P) EV_THROW) 199 | VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW) 200 | VAR (invoke_cb , ev_loop_callback invoke_cb) 201 | #endif 202 | 203 | #undef VARx 204 | 205 | -------------------------------------------------------------------------------- /src/ev_win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev win32 compatibility cruft (_not_ a backend) 3 | * 4 | * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifdef _WIN32 41 | 42 | /* timeb.h is actually xsi legacy functionality */ 43 | #include 44 | 45 | /* note: the comment below could not be substantiated, but what would I care */ 46 | /* MSDN says this is required to handle SIGFPE */ 47 | /* my wild guess would be that using something floating-pointy is required */ 48 | /* for the crt to do something about it */ 49 | volatile double SIGFPE_REQ = 0.0f; 50 | 51 | static SOCKET 52 | ev_tcp_socket (void) 53 | { 54 | #if EV_USE_WSASOCKET 55 | return WSASocket (AF_INET, SOCK_STREAM, 0, 0, 0, 0); 56 | #else 57 | return socket (AF_INET, SOCK_STREAM, 0); 58 | #endif 59 | } 60 | 61 | /* oh, the humanity! */ 62 | static int 63 | ev_pipe (int filedes [2]) 64 | { 65 | struct sockaddr_in addr = { 0 }; 66 | int addr_size = sizeof (addr); 67 | struct sockaddr_in adr2; 68 | int adr2_size = sizeof (adr2); 69 | SOCKET listener; 70 | SOCKET sock [2] = { -1, -1 }; 71 | 72 | if ((listener = ev_tcp_socket ()) == INVALID_SOCKET) 73 | return -1; 74 | 75 | addr.sin_family = AF_INET; 76 | addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 77 | addr.sin_port = 0; 78 | 79 | if (bind (listener, (struct sockaddr *)&addr, addr_size)) 80 | goto fail; 81 | 82 | if (getsockname (listener, (struct sockaddr *)&addr, &addr_size)) 83 | goto fail; 84 | 85 | if (listen (listener, 1)) 86 | goto fail; 87 | 88 | if ((sock [0] = ev_tcp_socket ()) == INVALID_SOCKET) 89 | goto fail; 90 | 91 | if (connect (sock [0], (struct sockaddr *)&addr, addr_size)) 92 | goto fail; 93 | 94 | if ((sock [1] = accept (listener, 0, 0)) < 0) 95 | goto fail; 96 | 97 | /* windows vista returns fantasy port numbers for sockets: 98 | * example for two interconnected tcp sockets: 99 | * 100 | * (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364 101 | * (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363 102 | * (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363 103 | * (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365 104 | * 105 | * wow! tridirectional sockets! 106 | * 107 | * this way of checking ports seems to work: 108 | */ 109 | if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size)) 110 | goto fail; 111 | 112 | if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size)) 113 | goto fail; 114 | 115 | errno = WSAEINVAL; 116 | if (addr_size != adr2_size 117 | || addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */ 118 | || addr.sin_port != adr2.sin_port) 119 | goto fail; 120 | 121 | closesocket (listener); 122 | 123 | #if EV_SELECT_IS_WINSOCKET 124 | filedes [0] = EV_WIN32_HANDLE_TO_FD (sock [0]); 125 | filedes [1] = EV_WIN32_HANDLE_TO_FD (sock [1]); 126 | #else 127 | /* when select isn't winsocket, we also expect socket, connect, accept etc. 128 | * to work on fds */ 129 | filedes [0] = sock [0]; 130 | filedes [1] = sock [1]; 131 | #endif 132 | 133 | return 0; 134 | 135 | fail: 136 | closesocket (listener); 137 | 138 | if (sock [0] != INVALID_SOCKET) closesocket (sock [0]); 139 | if (sock [1] != INVALID_SOCKET) closesocket (sock [1]); 140 | 141 | return -1; 142 | } 143 | 144 | #undef pipe 145 | #define pipe(filedes) ev_pipe (filedes) 146 | 147 | #define EV_HAVE_EV_TIME 1 148 | ev_tstamp 149 | ev_time (void) 150 | { 151 | FILETIME ft; 152 | ULARGE_INTEGER ui; 153 | 154 | GetSystemTimeAsFileTime (&ft); 155 | ui.u.LowPart = ft.dwLowDateTime; 156 | ui.u.HighPart = ft.dwHighDateTime; 157 | 158 | /* msvc cannot convert ulonglong to double... yes, it is that sucky */ 159 | return (LONGLONG)(ui.QuadPart - 116444736000000000) * 1e-7; 160 | } 161 | 162 | #endif 163 | 164 | -------------------------------------------------------------------------------- /src/ev_wrap.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT, automatically generated by update_ev_wrap */ 2 | #ifndef EV_WRAP_H 3 | #define EV_WRAP_H 4 | #define acquire_cb ((loop)->acquire_cb) 5 | #define activecnt ((loop)->activecnt) 6 | #define anfdmax ((loop)->anfdmax) 7 | #define anfds ((loop)->anfds) 8 | #define async_pending ((loop)->async_pending) 9 | #define asynccnt ((loop)->asynccnt) 10 | #define asyncmax ((loop)->asyncmax) 11 | #define asyncs ((loop)->asyncs) 12 | #define backend ((loop)->backend) 13 | #define backend_fd ((loop)->backend_fd) 14 | #define backend_mintime ((loop)->backend_mintime) 15 | #define backend_modify ((loop)->backend_modify) 16 | #define backend_poll ((loop)->backend_poll) 17 | #define checkcnt ((loop)->checkcnt) 18 | #define checkmax ((loop)->checkmax) 19 | #define checks ((loop)->checks) 20 | #define cleanupcnt ((loop)->cleanupcnt) 21 | #define cleanupmax ((loop)->cleanupmax) 22 | #define cleanups ((loop)->cleanups) 23 | #define curpid ((loop)->curpid) 24 | #define epoll_epermcnt ((loop)->epoll_epermcnt) 25 | #define epoll_epermmax ((loop)->epoll_epermmax) 26 | #define epoll_eperms ((loop)->epoll_eperms) 27 | #define epoll_eventmax ((loop)->epoll_eventmax) 28 | #define epoll_events ((loop)->epoll_events) 29 | #define evpipe ((loop)->evpipe) 30 | #define fdchangecnt ((loop)->fdchangecnt) 31 | #define fdchangemax ((loop)->fdchangemax) 32 | #define fdchanges ((loop)->fdchanges) 33 | #define forkcnt ((loop)->forkcnt) 34 | #define forkmax ((loop)->forkmax) 35 | #define forks ((loop)->forks) 36 | #define fs_2625 ((loop)->fs_2625) 37 | #define fs_fd ((loop)->fs_fd) 38 | #define fs_hash ((loop)->fs_hash) 39 | #define fs_w ((loop)->fs_w) 40 | #define idleall ((loop)->idleall) 41 | #define idlecnt ((loop)->idlecnt) 42 | #define idlemax ((loop)->idlemax) 43 | #define idles ((loop)->idles) 44 | #define invoke_cb ((loop)->invoke_cb) 45 | #define io_blocktime ((loop)->io_blocktime) 46 | #define iocp ((loop)->iocp) 47 | #define kqueue_changecnt ((loop)->kqueue_changecnt) 48 | #define kqueue_changemax ((loop)->kqueue_changemax) 49 | #define kqueue_changes ((loop)->kqueue_changes) 50 | #define kqueue_eventmax ((loop)->kqueue_eventmax) 51 | #define kqueue_events ((loop)->kqueue_events) 52 | #define kqueue_fd_pid ((loop)->kqueue_fd_pid) 53 | #define loop_count ((loop)->loop_count) 54 | #define loop_depth ((loop)->loop_depth) 55 | #define loop_done ((loop)->loop_done) 56 | #define mn_now ((loop)->mn_now) 57 | #define now_floor ((loop)->now_floor) 58 | #define origflags ((loop)->origflags) 59 | #define pending_w ((loop)->pending_w) 60 | #define pendingcnt ((loop)->pendingcnt) 61 | #define pendingmax ((loop)->pendingmax) 62 | #define pendingpri ((loop)->pendingpri) 63 | #define pendings ((loop)->pendings) 64 | #define periodiccnt ((loop)->periodiccnt) 65 | #define periodicmax ((loop)->periodicmax) 66 | #define periodics ((loop)->periodics) 67 | #define pipe_w ((loop)->pipe_w) 68 | #define pipe_write_skipped ((loop)->pipe_write_skipped) 69 | #define pipe_write_wanted ((loop)->pipe_write_wanted) 70 | #define pollcnt ((loop)->pollcnt) 71 | #define pollidxmax ((loop)->pollidxmax) 72 | #define pollidxs ((loop)->pollidxs) 73 | #define pollmax ((loop)->pollmax) 74 | #define polls ((loop)->polls) 75 | #define port_eventmax ((loop)->port_eventmax) 76 | #define port_events ((loop)->port_events) 77 | #define postfork ((loop)->postfork) 78 | #define preparecnt ((loop)->preparecnt) 79 | #define preparemax ((loop)->preparemax) 80 | #define prepares ((loop)->prepares) 81 | #define release_cb ((loop)->release_cb) 82 | #define rfeedcnt ((loop)->rfeedcnt) 83 | #define rfeedmax ((loop)->rfeedmax) 84 | #define rfeeds ((loop)->rfeeds) 85 | #define rtmn_diff ((loop)->rtmn_diff) 86 | #define sig_pending ((loop)->sig_pending) 87 | #define sigfd ((loop)->sigfd) 88 | #define sigfd_set ((loop)->sigfd_set) 89 | #define sigfd_w ((loop)->sigfd_w) 90 | #define timeout_blocktime ((loop)->timeout_blocktime) 91 | #define timercnt ((loop)->timercnt) 92 | #define timermax ((loop)->timermax) 93 | #define timers ((loop)->timers) 94 | #define userdata ((loop)->userdata) 95 | #define vec_eo ((loop)->vec_eo) 96 | #define vec_max ((loop)->vec_max) 97 | #define vec_ri ((loop)->vec_ri) 98 | #define vec_ro ((loop)->vec_ro) 99 | #define vec_wi ((loop)->vec_wi) 100 | #define vec_wo ((loop)->vec_wo) 101 | #else 102 | #undef EV_WRAP_H 103 | #undef acquire_cb 104 | #undef activecnt 105 | #undef anfdmax 106 | #undef anfds 107 | #undef async_pending 108 | #undef asynccnt 109 | #undef asyncmax 110 | #undef asyncs 111 | #undef backend 112 | #undef backend_fd 113 | #undef backend_mintime 114 | #undef backend_modify 115 | #undef backend_poll 116 | #undef checkcnt 117 | #undef checkmax 118 | #undef checks 119 | #undef cleanupcnt 120 | #undef cleanupmax 121 | #undef cleanups 122 | #undef curpid 123 | #undef epoll_epermcnt 124 | #undef epoll_epermmax 125 | #undef epoll_eperms 126 | #undef epoll_eventmax 127 | #undef epoll_events 128 | #undef evpipe 129 | #undef fdchangecnt 130 | #undef fdchangemax 131 | #undef fdchanges 132 | #undef forkcnt 133 | #undef forkmax 134 | #undef forks 135 | #undef fs_2625 136 | #undef fs_fd 137 | #undef fs_hash 138 | #undef fs_w 139 | #undef idleall 140 | #undef idlecnt 141 | #undef idlemax 142 | #undef idles 143 | #undef invoke_cb 144 | #undef io_blocktime 145 | #undef iocp 146 | #undef kqueue_changecnt 147 | #undef kqueue_changemax 148 | #undef kqueue_changes 149 | #undef kqueue_eventmax 150 | #undef kqueue_events 151 | #undef kqueue_fd_pid 152 | #undef loop_count 153 | #undef loop_depth 154 | #undef loop_done 155 | #undef mn_now 156 | #undef now_floor 157 | #undef origflags 158 | #undef pending_w 159 | #undef pendingcnt 160 | #undef pendingmax 161 | #undef pendingpri 162 | #undef pendings 163 | #undef periodiccnt 164 | #undef periodicmax 165 | #undef periodics 166 | #undef pipe_w 167 | #undef pipe_write_skipped 168 | #undef pipe_write_wanted 169 | #undef pollcnt 170 | #undef pollidxmax 171 | #undef pollidxs 172 | #undef pollmax 173 | #undef polls 174 | #undef port_eventmax 175 | #undef port_events 176 | #undef postfork 177 | #undef preparecnt 178 | #undef preparemax 179 | #undef prepares 180 | #undef release_cb 181 | #undef rfeedcnt 182 | #undef rfeedmax 183 | #undef rfeeds 184 | #undef rtmn_diff 185 | #undef sig_pending 186 | #undef sigfd 187 | #undef sigfd_set 188 | #undef sigfd_w 189 | #undef timeout_blocktime 190 | #undef timercnt 191 | #undef timermax 192 | #undef timers 193 | #undef userdata 194 | #undef vec_eo 195 | #undef vec_max 196 | #undef vec_ri 197 | #undef vec_ro 198 | #undef vec_wi 199 | #undef vec_wo 200 | #endif 201 | -------------------------------------------------------------------------------- /src/ioclient.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ioclient.c - iosocks client 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "async_connect.h" 35 | #include "conf.h" 36 | #include "crypto.h" 37 | #include "log.h" 38 | #include "md5.h" 39 | #include "relay.h" 40 | #include "socks5.h" 41 | #include "utils.h" 42 | 43 | #define UNUSED(x) do {(void)(x);} while (0) 44 | 45 | // 最大连接尝试次数 46 | #define MAX_TRY 4 47 | 48 | #ifndef EAGAIN 49 | # define EAGAIN EWOULDBLOCK 50 | #endif 51 | #ifndef EWOULDBLOCK 52 | # define EWOULDBLOCK EAGAIN 53 | #endif 54 | 55 | typedef struct 56 | { 57 | int sock_local; 58 | int sock_remote; 59 | int server_id; 60 | int server_tried; 61 | char host[257]; 62 | char port[15]; 63 | crypto_evp_t evp; 64 | ev_io w_write; 65 | ssize_t len; 66 | uint8_t buf[16 + 257 + 15]; 67 | } ctx_t; 68 | 69 | static void timer_cb(EV_P_ ev_timer *w, int revents); 70 | static void signal_cb(EV_P_ ev_signal *w, int revents); 71 | static void accept_cb(EV_P_ ev_io *w, int revents); 72 | static void socks5_cb(int sock, char *host, char *port); 73 | static void connect_cb(int sock, void *data); 74 | static void iosocks_send_cb(EV_P_ ev_io *w, int revents); 75 | static int select_server(void); 76 | static void connect_server(ctx_t *ctx); 77 | 78 | // ev loop 79 | struct ev_loop *loop; 80 | 81 | // 配置信息 82 | static conf_t conf; 83 | 84 | // 服务器的信息 85 | static struct 86 | { 87 | struct sockaddr_storage addr; 88 | socklen_t addrlen; 89 | char *key; 90 | time_t health; // 0 可用,非 0 不可用 91 | } servers[MAX_SERVER]; 92 | 93 | int main(int argc, char **argv) 94 | { 95 | if (parse_args(argc, argv, &conf) != 0) 96 | { 97 | return EXIT_FAILURE; 98 | } 99 | 100 | // daemonize 101 | if (conf.daemon) 102 | { 103 | if (daemonize(conf.pidfile, conf.logfile) != 0) 104 | { 105 | return EXIT_FAILURE; 106 | } 107 | } 108 | 109 | // 服务器信息 110 | struct addrinfo hints; 111 | struct addrinfo *res; 112 | for (int i = 0; i < conf.server_num; i++) 113 | { 114 | servers[i].health = 0; 115 | servers[i].key = conf.server[i].key; 116 | bzero(&hints, sizeof(struct addrinfo)); 117 | hints.ai_family = AF_UNSPEC; 118 | hints.ai_socktype = SOCK_STREAM; 119 | if (getaddrinfo(conf.server[i].address, conf.server[i].port, &hints, &res) != 0) 120 | { 121 | LOG("failed to resolv %s:%s", conf.server[i].address, conf.server[i].port); 122 | return 2; 123 | } 124 | memcpy(&servers[i].addr, res->ai_addr, res->ai_addrlen); 125 | servers[i].addrlen = res->ai_addrlen; 126 | freeaddrinfo(res); 127 | } 128 | 129 | // 初始化本地监听 socket 130 | bzero(&hints, sizeof(struct addrinfo)); 131 | hints.ai_family = AF_UNSPEC; 132 | hints.ai_socktype = SOCK_STREAM; 133 | if (getaddrinfo(conf.local.address, conf.local.port, &hints, &res) != 0) 134 | { 135 | LOG("failed to resolv %s:%s", conf.local.address, conf.local.port); 136 | return EXIT_FAILURE; 137 | } 138 | int sock_listen = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP); 139 | if (sock_listen < 0) 140 | { 141 | ERROR("socket"); 142 | return EXIT_FAILURE; 143 | } 144 | setnonblock(sock_listen); 145 | setreuseaddr(sock_listen); 146 | if (bind(sock_listen, (struct sockaddr *)res->ai_addr, res->ai_addrlen) != 0) 147 | { 148 | ERROR("bind"); 149 | return EXIT_FAILURE; 150 | } 151 | freeaddrinfo(res); 152 | if (listen(sock_listen, 1024) != 0) 153 | { 154 | ERROR("listen"); 155 | return EXIT_FAILURE; 156 | } 157 | 158 | // 初始化 ev 159 | loop = EV_DEFAULT; 160 | ev_signal w_sigint; 161 | ev_signal w_sigterm; 162 | ev_signal_init(&w_sigint, signal_cb, SIGINT); 163 | ev_signal_init(&w_sigterm, signal_cb, SIGTERM); 164 | ev_signal_start(EV_A_ &w_sigint); 165 | ev_signal_start(EV_A_ &w_sigterm); 166 | ev_io w_listen; 167 | ev_io_init(&w_listen, accept_cb, sock_listen, EV_READ); 168 | ev_io_start(EV_A_ &w_listen); 169 | ev_timer w_timer; 170 | ev_timer_init(&w_timer, timer_cb, 5.0, 5.0); 171 | ev_timer_start(EV_A_ &w_timer); 172 | 173 | // drop root privilege 174 | if (runas(conf.user) != 0) 175 | { 176 | ERROR("runas"); 177 | } 178 | 179 | LOG("starting ioclient at %s:%s", conf.local.address, conf.local.port); 180 | 181 | // 执行事件循环 182 | ev_run(EV_A_ 0); 183 | 184 | // 退出 185 | close(sock_listen); 186 | LOG("Exit"); 187 | 188 | return EXIT_SUCCESS; 189 | } 190 | 191 | static void timer_cb(EV_P_ ev_timer *w, int revents) 192 | { 193 | UNUSED(loop); 194 | UNUSED(w); 195 | UNUSED(revents); 196 | 197 | time_t t = time(NULL); 198 | for (int i = 0; i < conf.server_num; i++) 199 | { 200 | if (servers[i].health + 20 < t) 201 | { 202 | servers[i].health = 0; 203 | } 204 | } 205 | } 206 | 207 | static void signal_cb(EV_P_ ev_signal *w, int revents) 208 | { 209 | UNUSED(revents); 210 | assert((w->signum == SIGINT) || (w->signum == SIGTERM)); 211 | ev_break(EV_A_ EVBREAK_ALL); 212 | } 213 | 214 | static void accept_cb(EV_P_ ev_io *w, int revents) 215 | { 216 | UNUSED(loop); 217 | UNUSED(revents); 218 | 219 | int sock = accept(w->fd, NULL, NULL); 220 | 221 | if (sock < 0) 222 | { 223 | ERROR("accept"); 224 | } 225 | else 226 | { 227 | setnonblock(sock); 228 | settimeout(sock); 229 | setkeepalive(sock); 230 | socks5_accept(sock, socks5_cb); 231 | } 232 | } 233 | 234 | void socks5_cb(int sock, char *host, char *port) 235 | { 236 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 237 | if (ctx == NULL) 238 | { 239 | LOG("out of memory"); 240 | close(sock); 241 | return; 242 | } 243 | ctx->sock_local = sock; 244 | strcpy(ctx->host, host); 245 | strcpy(ctx->port, port); 246 | ctx->server_tried = 0; 247 | connect_server(ctx); 248 | } 249 | 250 | static void connect_server(ctx_t *ctx) 251 | { 252 | // 随机选择一个 server 253 | ctx->server_id = select_server(); 254 | if (ctx->server_id < 0) 255 | { 256 | LOG("no available server, abort"); 257 | close(ctx->sock_local); 258 | free(ctx); 259 | return; 260 | } 261 | ctx->server_tried++; 262 | LOG("connect %s:%s via %s:%s", 263 | ctx->host, ctx->port, 264 | conf.server[ctx->server_id].address, 265 | conf.server[ctx->server_id].port); 266 | 267 | // 建立远程连接 268 | async_connect((struct sockaddr *)&servers[ctx->server_id].addr, 269 | servers[ctx->server_id].addrlen, connect_cb, ctx); 270 | } 271 | 272 | static void connect_cb(int sock, void *data) 273 | { 274 | ctx_t *ctx = (ctx_t *)(data); 275 | 276 | assert(ctx != NULL); 277 | 278 | if (sock > 0) 279 | { 280 | // 连接成功 281 | ctx->sock_remote = sock; 282 | 283 | // IoSocks Request 284 | // +------+------+------+ 285 | // | IV | HOST | PORT | 286 | // +------+------+------+ 287 | // | 16 | 257 | 15 | 288 | // +------+------+------+ 289 | bzero(ctx->buf, 16 + 257 + 15); 290 | strcpy((char *)ctx->buf + 16, ctx->host); 291 | strcpy((char *)ctx->buf + 16 + 257, ctx->port); 292 | md5(ctx->buf, ctx->buf + 16, 257 + 15); 293 | crypto_init(&(ctx->evp), servers[ctx->server_id].key, ctx->buf); 294 | crypto_encrypt(ctx->buf + 16, 257 + 15, &(ctx->evp)); 295 | ctx->len = 16 + 257 + 15; 296 | ssize_t n = send(ctx->sock_remote, ctx->buf, ctx->len, MSG_NOSIGNAL); 297 | if (n < 0) 298 | { 299 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 300 | { 301 | ev_io_init(&(ctx->w_write), iosocks_send_cb, ctx->sock_remote, EV_WRITE); 302 | ctx->w_write.data = (void *)ctx; 303 | ev_io_start(EV_A_ &ctx->w_write); 304 | } 305 | else 306 | { 307 | close(ctx->sock_local); 308 | close(ctx->sock_remote); 309 | free(ctx); 310 | } 311 | } 312 | else 313 | { 314 | relay(ctx->sock_local, ctx->sock_remote, &(ctx->evp)); 315 | free(ctx); 316 | } 317 | } 318 | else 319 | { 320 | // 连接失败 321 | servers[ctx->server_id].health = time(NULL); 322 | if (ctx->server_tried < MAX_TRY) 323 | { 324 | LOG("connect to ioserver failed, try again"); 325 | close(ctx->sock_remote); 326 | connect_server(ctx); 327 | } 328 | else 329 | { 330 | LOG("connect to ioserver failed, abort"); 331 | close(ctx->sock_local); 332 | close(ctx->sock_remote); 333 | free(ctx); 334 | } 335 | } 336 | } 337 | 338 | static void iosocks_send_cb(EV_P_ ev_io *w, int revents) 339 | { 340 | ctx_t *ctx = (ctx_t *)(w->data); 341 | 342 | UNUSED(revents); 343 | assert(ctx != NULL); 344 | 345 | ev_io_stop(EV_A_ w); 346 | 347 | ssize_t n = send(ctx->sock_remote, ctx->buf, ctx->len, MSG_NOSIGNAL); 348 | if (n < 0) 349 | { 350 | close(ctx->sock_local); 351 | close(ctx->sock_remote); 352 | free(ctx); 353 | return; 354 | } 355 | else 356 | { 357 | relay(ctx->sock_local, ctx->sock_remote, &(ctx->evp)); 358 | free(ctx); 359 | } 360 | } 361 | 362 | static int select_server(void) 363 | { 364 | unsigned char rand_num; 365 | int id; 366 | int tries = 0; 367 | while (tries++ < 100) 368 | { 369 | rand_bytes(&rand_num, sizeof(unsigned char)); 370 | id = (int)rand_num % conf.server_num; 371 | if (servers[id].health == 0) 372 | { 373 | return id; 374 | } 375 | } 376 | return -1; 377 | } 378 | -------------------------------------------------------------------------------- /src/ioredir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ioredir.c - A transparent TCP proxy 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "conf.h" 36 | #include "crypto.h" 37 | #include "log.h" 38 | #include "md5.h" 39 | #include "relay.h" 40 | #include "utils.h" 41 | 42 | #define UNUSED(x) do {(void)(x);} while (0) 43 | 44 | // 最大连接尝试次数 45 | #define MAX_TRY 4 46 | 47 | typedef struct 48 | { 49 | int sock_local; 50 | int sock_remote; 51 | int server_id; 52 | int server_tried; 53 | char host[257]; 54 | char port[15]; 55 | crypto_evp_t evp; 56 | ev_io w_write; 57 | ssize_t len; 58 | uint8_t buf[16 + 257 + 15]; 59 | } ctx_t; 60 | 61 | static void timer_cb(EV_P_ ev_timer *w, int revents); 62 | static void signal_cb(EV_P_ ev_signal *w, int revents); 63 | static void accept_cb(EV_P_ ev_io *w, int revents); 64 | static void iosocks_send_cb(EV_P_ ev_io *w, int revents); 65 | static int select_server(void); 66 | static void connect_server(ctx_t *ctx); 67 | 68 | // ev loop 69 | struct ev_loop *loop; 70 | 71 | // 配置信息 72 | static conf_t conf; 73 | 74 | // 服务器的信息 75 | static struct 76 | { 77 | struct sockaddr_storage addr; 78 | socklen_t addrlen; 79 | char *key; 80 | time_t health; // 0 可用,非 0 不可用 81 | } servers[MAX_SERVER]; 82 | 83 | int main(int argc, char **argv) 84 | { 85 | if (parse_args(argc, argv, &conf) != 0) 86 | { 87 | return EXIT_FAILURE; 88 | } 89 | 90 | // daemonize 91 | if (conf.daemon) 92 | { 93 | if (daemonize(conf.pidfile, conf.logfile) != 0) 94 | { 95 | return -1; 96 | } 97 | } 98 | 99 | // 服务器信息 100 | struct addrinfo hints; 101 | struct addrinfo *res; 102 | for (int i = 0; i < conf.server_num; i++) 103 | { 104 | servers[i].health = 0; 105 | servers[i].key = conf.server[i].key; 106 | bzero(&hints, sizeof(struct addrinfo)); 107 | hints.ai_family = AF_UNSPEC; 108 | hints.ai_socktype = SOCK_STREAM; 109 | if (getaddrinfo(conf.server[i].address, conf.server[i].port, &hints, &res) != 0) 110 | { 111 | LOG("failed to resolv %s:%s", conf.server[i].address, conf.server[i].port); 112 | return 2; 113 | } 114 | memcpy(&servers[i].addr, res->ai_addr, res->ai_addrlen); 115 | servers[i].addrlen = res->ai_addrlen; 116 | freeaddrinfo(res); 117 | } 118 | 119 | // 初始化本地监听 socket 120 | bzero(&hints, sizeof(struct addrinfo)); 121 | hints.ai_family = AF_INET; 122 | hints.ai_socktype = SOCK_STREAM; 123 | if (getaddrinfo(conf.redir.address, conf.redir.port, &hints, &res) != 0) 124 | { 125 | LOG("wrong local_host/local_port"); 126 | return EXIT_FAILURE; 127 | } 128 | int sock_listen = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP); 129 | if (sock_listen < 0) 130 | { 131 | ERROR("socket"); 132 | return EXIT_FAILURE; 133 | } 134 | setnonblock(sock_listen); 135 | setreuseaddr(sock_listen); 136 | if (bind(sock_listen, (struct sockaddr *)res->ai_addr, res->ai_addrlen) != 0) 137 | { 138 | ERROR("bind"); 139 | return EXIT_FAILURE; 140 | } 141 | freeaddrinfo(res); 142 | if (listen(sock_listen, 1024) != 0) 143 | { 144 | ERROR("listen"); 145 | return EXIT_FAILURE; 146 | } 147 | 148 | // 初始化 ev 149 | loop = EV_DEFAULT; 150 | ev_signal w_sigint; 151 | ev_signal w_sigterm; 152 | ev_signal_init(&w_sigint, signal_cb, SIGINT); 153 | ev_signal_init(&w_sigterm, signal_cb, SIGTERM); 154 | ev_signal_start(EV_A_ &w_sigint); 155 | ev_signal_start(EV_A_ &w_sigterm); 156 | ev_io w_listen; 157 | ev_io_init(&w_listen, accept_cb, sock_listen, EV_READ); 158 | ev_io_start(EV_A_ &w_listen); 159 | ev_timer w_timer; 160 | ev_timer_init(&w_timer, timer_cb, 5.0, 5.0); 161 | ev_timer_start(EV_A_ &w_timer); 162 | 163 | // drop root privilege 164 | if (runas(conf.user) != 0) 165 | { 166 | ERROR("runas"); 167 | } 168 | 169 | LOG("starting ioredir at %s:%s", conf.redir.address, conf.redir.port); 170 | 171 | // 执行事件循环 172 | ev_run(EV_A_ 0); 173 | 174 | // 退出 175 | close(sock_listen); 176 | LOG("Exit"); 177 | 178 | return EXIT_SUCCESS; 179 | } 180 | 181 | static void timer_cb(EV_P_ ev_timer *w, int revents) 182 | { 183 | UNUSED(loop); 184 | UNUSED(w); 185 | UNUSED(revents); 186 | 187 | time_t t = time(NULL); 188 | for (int i = 0; i < conf.server_num; i++) 189 | { 190 | if (servers[i].health + 20 < t) 191 | { 192 | servers[i].health = 0; 193 | } 194 | } 195 | } 196 | 197 | static void signal_cb(EV_P_ ev_signal *w, int revents) 198 | { 199 | UNUSED(revents); 200 | assert((w->signum == SIGINT) || (w->signum == SIGTERM)); 201 | ev_break(EV_A_ EVBREAK_ALL); 202 | } 203 | 204 | static void accept_cb(EV_P_ ev_io *w, int revents) 205 | { 206 | UNUSED(loop); 207 | UNUSED(revents); 208 | 209 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 210 | if (ctx == NULL) 211 | { 212 | LOG("out of memory"); 213 | return; 214 | } 215 | ctx->sock_local = accept(w->fd, NULL, NULL); 216 | if (ctx->sock_local < 0) 217 | { 218 | ERROR("accept"); 219 | free(ctx); 220 | return; 221 | } 222 | setnonblock(ctx->sock_local); 223 | settimeout(ctx->sock_local); 224 | setkeepalive(ctx->sock_local); 225 | 226 | // 获取原始地址 227 | struct sockaddr_storage addr; 228 | socklen_t addrlen = sizeof(struct sockaddr_storage); 229 | if (getdestaddr(ctx->sock_local, (struct sockaddr *)&addr, &addrlen) != 0) 230 | { 231 | ERROR("getdestaddr"); 232 | close(ctx->sock_local); 233 | free(ctx); 234 | return; 235 | } 236 | if (addr.ss_family == AF_INET) 237 | { 238 | inet_ntop(AF_INET, &(((struct sockaddr_in *)&addr)->sin_addr), 239 | ctx->host, INET_ADDRSTRLEN); 240 | sprintf(ctx->port, "%u", ntohs(((struct sockaddr_in *)&addr)->sin_port)); 241 | } 242 | else 243 | { 244 | inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)&addr)->sin6_addr), 245 | ctx->host, INET6_ADDRSTRLEN); 246 | sprintf(ctx->port, "%u", ntohs(((struct sockaddr_in6 *)&addr)->sin6_port)); 247 | } 248 | 249 | // 连接 iosocks server 250 | ctx->server_tried = 0; 251 | connect_server(ctx); 252 | } 253 | 254 | static void connect_cb(int sock, void *data) 255 | { 256 | ctx_t *ctx = (ctx_t *)(data); 257 | 258 | assert(ctx != NULL); 259 | 260 | if (sock > 0) 261 | { 262 | // 连接成功 263 | ctx->sock_remote = sock; 264 | 265 | // IoSocks Request 266 | // +------+------+------+ 267 | // | IV | HOST | PORT | 268 | // +------+------+------+ 269 | // | 16 | 257 | 15 | 270 | // +------+------+------+ 271 | bzero(ctx->buf, 16 + 257 + 15); 272 | strcpy((char *)ctx->buf + 16, ctx->host); 273 | strcpy((char *)ctx->buf + 16 + 257, ctx->port); 274 | md5(ctx->buf, ctx->buf + 16, 257 + 15); 275 | crypto_init(&(ctx->evp), servers[ctx->server_id].key, ctx->buf); 276 | crypto_encrypt(ctx->buf + 16, 257 + 15, &(ctx->evp)); 277 | ctx->len = 16 + 257 + 15; 278 | ssize_t n = send(ctx->sock_remote, ctx->buf, ctx->len, MSG_NOSIGNAL); 279 | if (n < 0) 280 | { 281 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 282 | { 283 | ev_io_init(&(ctx->w_write), iosocks_send_cb, ctx->sock_remote, EV_WRITE); 284 | ctx->w_write.data = (void *)ctx; 285 | ev_io_start(EV_A_ &ctx->w_write); 286 | } 287 | else 288 | { 289 | close(ctx->sock_local); 290 | close(ctx->sock_remote); 291 | free(ctx); 292 | } 293 | } 294 | else 295 | { 296 | relay(ctx->sock_local, ctx->sock_remote, &(ctx->evp)); 297 | free(ctx); 298 | } 299 | } 300 | else 301 | { 302 | // 连接失败 303 | servers[ctx->server_id].health = time(NULL); 304 | if (ctx->server_tried < MAX_TRY) 305 | { 306 | LOG("connect to ioserver failed, try again"); 307 | close(ctx->sock_remote); 308 | connect_server(ctx); 309 | } 310 | else 311 | { 312 | LOG("connect to ioserver failed, abort"); 313 | close(ctx->sock_local); 314 | close(ctx->sock_remote); 315 | free(ctx); 316 | } 317 | } 318 | } 319 | 320 | static void iosocks_send_cb(EV_P_ ev_io *w, int revents) 321 | { 322 | ctx_t *ctx = (ctx_t *)(w->data); 323 | 324 | UNUSED(revents); 325 | assert(ctx != NULL); 326 | 327 | ev_io_stop(EV_A_ w); 328 | 329 | ssize_t n = send(ctx->sock_remote, ctx->buf, ctx->len, MSG_NOSIGNAL); 330 | if (n < 0) 331 | { 332 | close(ctx->sock_local); 333 | close(ctx->sock_remote); 334 | free(ctx); 335 | return; 336 | } 337 | else 338 | { 339 | relay(ctx->sock_local, ctx->sock_remote, &(ctx->evp)); 340 | free(ctx); 341 | } 342 | } 343 | 344 | static void connect_server(ctx_t *ctx) 345 | { 346 | // 随机选择一个 server 347 | ctx->server_id = select_server(); 348 | if (ctx->server_id < 0) 349 | { 350 | LOG("no available server, abort"); 351 | close(ctx->sock_local); 352 | free(ctx); 353 | return; 354 | } 355 | ctx->server_tried++; 356 | LOG("connect %s:%s via %s:%s", 357 | ctx->host, ctx->port, 358 | conf.server[ctx->server_id].address, 359 | conf.server[ctx->server_id].port); 360 | 361 | // 建立远程连接 362 | async_connect((struct sockaddr *)&servers[ctx->server_id].addr, 363 | servers[ctx->server_id].addrlen, connect_cb, ctx); 364 | } 365 | 366 | static int select_server(void) 367 | { 368 | unsigned char rand_num; 369 | int id; 370 | int tries = 0; 371 | while (tries++ < 100) 372 | { 373 | rand_bytes(&rand_num, sizeof(unsigned char)); 374 | id = (int)rand_num % conf.server_num; 375 | if (servers[id].health == 0) 376 | { 377 | return id; 378 | } 379 | } 380 | return -1; 381 | } 382 | -------------------------------------------------------------------------------- /src/ioserver.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ioserver.c - iosocks server 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "async_connect.h" 36 | #include "async_resolv.h" 37 | #include "conf.h" 38 | #include "crypto.h" 39 | #include "log.h" 40 | #include "md5.h" 41 | #include "relay.h" 42 | #include "utils.h" 43 | 44 | #define UNUSED(x) do {(void)(x);} while (0) 45 | 46 | // 连接控制块 47 | typedef struct 48 | { 49 | int sock; 50 | int server_id; 51 | struct addrinfo *_res; 52 | struct addrinfo *res; 53 | ev_io w_read; 54 | crypto_evp_t evp; 55 | } ctx_t; 56 | 57 | static void signal_cb(EV_P_ ev_signal *w, int revents); 58 | static void accept_cb(EV_P_ ev_io *w, int revents); 59 | static void iosocks_recv_cb(EV_P_ ev_io *w, int revents); 60 | static void resolv_cb(struct addrinfo *res, void *data); 61 | static void connect_cb(int sock, void *data); 62 | 63 | // 服务器的信息 64 | static struct 65 | { 66 | char *key; 67 | } servers[MAX_SERVER]; 68 | 69 | struct ev_loop *loop; 70 | 71 | int main(int argc, char **argv) 72 | { 73 | conf_t conf; 74 | 75 | if (parse_args(argc, argv, &conf) != 0) 76 | { 77 | return EXIT_FAILURE; 78 | } 79 | 80 | // daemonize 81 | if (conf.daemon) 82 | { 83 | if (daemonize(conf.pidfile, conf.logfile) != 0) 84 | { 85 | return EXIT_FAILURE; 86 | } 87 | } 88 | 89 | // 服务器信息 90 | for (int i = 0; i < conf.server_num; i++) 91 | { 92 | servers[i].key = conf.server[i].key; 93 | } 94 | 95 | // 初始化 ev_signal 96 | loop = EV_DEFAULT; 97 | ev_signal w_sigint; 98 | ev_signal w_sigterm; 99 | ev_signal_init(&w_sigint, signal_cb, SIGINT); 100 | ev_signal_init(&w_sigterm, signal_cb, SIGTERM); 101 | ev_signal_start(EV_A_ &w_sigint); 102 | ev_signal_start(EV_A_ &w_sigterm); 103 | 104 | // 初始化本地监听 socket 105 | int sock_listen[conf.server_num]; 106 | ev_io w_listen[conf.server_num]; 107 | struct addrinfo hints; 108 | struct addrinfo *res; 109 | for (int i = 0; i < conf.server_num; i++) 110 | { 111 | bzero(&hints, sizeof(struct addrinfo)); 112 | hints.ai_family = AF_UNSPEC; 113 | hints.ai_socktype = SOCK_STREAM; 114 | if (getaddrinfo(conf.server[i].address, conf.server[i].port, &hints, &res) != 0) 115 | { 116 | LOG("failed to resolv %s:%s", conf.server[i].address, conf.server[i].port); 117 | return EXIT_FAILURE; 118 | } 119 | sock_listen[i] = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP); 120 | if (sock_listen[i] < 0) 121 | { 122 | ERROR("socket"); 123 | return EXIT_FAILURE; 124 | } 125 | setnonblock(sock_listen[i]); 126 | setreuseaddr(sock_listen[i]); 127 | if (bind(sock_listen[i], (struct sockaddr *)res->ai_addr, res->ai_addrlen) != 0) 128 | { 129 | ERROR("bind"); 130 | return EXIT_FAILURE; 131 | } 132 | freeaddrinfo(res); 133 | if (listen(sock_listen[i], 1024) != 0) 134 | { 135 | ERROR("listen"); 136 | return EXIT_FAILURE; 137 | } 138 | ev_io_init(&(w_listen[i]), accept_cb, sock_listen[i], EV_READ); 139 | w_listen[i].data = (void *)(uintptr_t)i; 140 | ev_io_start(EV_A_ &(w_listen[i])); 141 | LOG("starting ioserver at %s:%s", conf.server[i].address, conf.server[i].port); 142 | } 143 | 144 | // 初始化 async_resolv 145 | if (resolv_init() != 0) 146 | { 147 | return EXIT_FAILURE; 148 | } 149 | 150 | // drop root privilege 151 | if (runas(conf.user) != 0) 152 | { 153 | ERROR("runas"); 154 | } 155 | 156 | // 执行事件循环 157 | ev_run(EV_A_ 0); 158 | 159 | // 退出 160 | for (int i = 0; i < conf.server_num; i++) 161 | { 162 | close(sock_listen[i]); 163 | } 164 | LOG("Exit"); 165 | 166 | return EXIT_SUCCESS; 167 | } 168 | 169 | static void signal_cb(EV_P_ ev_signal *w, int revents) 170 | { 171 | UNUSED(revents); 172 | assert((w->signum == SIGINT) || (w->signum == SIGTERM)); 173 | ev_break(EV_A_ EVBREAK_ALL); 174 | } 175 | 176 | static void accept_cb(EV_P_ ev_io *w, int revents) 177 | { 178 | UNUSED(revents); 179 | 180 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 181 | if (ctx == NULL) 182 | { 183 | LOG("out of memory"); 184 | return; 185 | } 186 | ctx->sock = accept(w->fd, NULL, NULL); 187 | if (ctx->sock < 0) 188 | { 189 | ERROR("accept"); 190 | free(ctx); 191 | return; 192 | } 193 | setnonblock(ctx->sock); 194 | settimeout(ctx->sock); 195 | setkeepalive(ctx->sock); 196 | ctx->server_id = (int)(uintptr_t)(w->data); 197 | ev_io_init(&ctx->w_read, iosocks_recv_cb, ctx->sock, EV_READ); 198 | ctx->w_read.data = (void *)ctx; 199 | ev_io_start(EV_A_ &ctx->w_read); 200 | } 201 | 202 | static void iosocks_recv_cb(EV_P_ ev_io *w, int revents) 203 | { 204 | assert(w->data != NULL); 205 | UNUSED(revents); 206 | 207 | ctx_t *ctx = (ctx_t *)(w->data); 208 | 209 | ev_io_stop(EV_A_ w); 210 | 211 | uint8_t buf[288]; 212 | ssize_t n = recv(ctx->sock, buf, sizeof(buf), 0); 213 | if (n != 288) 214 | { 215 | if (n < 0) 216 | { 217 | ERROR("recv"); 218 | } 219 | else 220 | { 221 | LOG("bad client"); 222 | } 223 | close(ctx->sock); 224 | free(ctx); 225 | return; 226 | } 227 | 228 | // IoSocks Request 229 | // +------+------+------+ 230 | // | IV | HOST | PORT | 231 | // +------+------+------+ 232 | // | 16 | 257 | 15 | 233 | // +------+------+------+ 234 | crypto_init(&(ctx->evp), servers[ctx->server_id].key, buf); 235 | crypto_decrypt(buf + 16, 257 + 15, &(ctx->evp)); 236 | uint8_t tmp[16]; 237 | md5(tmp, buf + 16, 257 + 15); 238 | if (memcmp(buf, tmp, 16) != 0) 239 | { 240 | LOG("illegal client"); 241 | close(ctx->sock); 242 | free(ctx); 243 | return; 244 | } 245 | char *host = (char *)(buf + 16); 246 | char *port = (char *)(buf + 16 + 257); 247 | host[256] = '\0'; 248 | port[14] = '\0'; 249 | LOG("connect %s:%s", host, port); 250 | async_resolv(host, port, resolv_cb, ctx); 251 | } 252 | 253 | static void resolv_cb(struct addrinfo *res, void *data) 254 | { 255 | assert(data != NULL); 256 | 257 | ctx_t *ctx = (ctx_t *)data; 258 | 259 | if (res != NULL) 260 | { 261 | // 域名解析成功,建立远程连接 262 | ctx->_res = res; 263 | ctx->res = res; 264 | async_connect(ctx->res->ai_addr, ctx->res->ai_addrlen, connect_cb, data); 265 | } 266 | else 267 | { 268 | // 域名解析失败 269 | close(ctx->sock); 270 | free(ctx); 271 | } 272 | } 273 | 274 | static void connect_cb(int sock, void *data) 275 | { 276 | ctx_t *ctx = (ctx_t *)(data); 277 | 278 | assert(ctx != NULL); 279 | 280 | if (sock > 0) 281 | { 282 | // 连接成功 283 | freeaddrinfo(ctx->_res); 284 | relay(sock, ctx->sock, &(ctx->evp)); 285 | free(ctx); 286 | } 287 | else 288 | { 289 | // 连接失败 290 | close(ctx->sock); 291 | ctx->res = ctx->res->ai_next; 292 | if (ctx->res != NULL) 293 | { 294 | // 尝试连接下一个地址 295 | async_connect(ctx->res->ai_addr, ctx->res->ai_addrlen, connect_cb, data); 296 | } 297 | else 298 | { 299 | // 所有地址均连接失败 300 | LOG("connect failed"); 301 | close(ctx->sock); 302 | freeaddrinfo(ctx->_res); 303 | free(ctx); 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * log.c - log functions 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "log.h" 26 | 27 | void __log(FILE *stream, const char *format, ...) 28 | { 29 | time_t now = time(NULL); 30 | char timestr[20]; 31 | strftime(timestr, 20, "%y-%m-%d %H:%M:%S", localtime(&now)); 32 | fprintf(stream, "[%s] ", timestr); 33 | 34 | va_list args; 35 | va_start(args, format); 36 | vfprintf(stream, format, args); 37 | va_end(args); 38 | putchar('\n'); 39 | fflush(stream); 40 | } 41 | 42 | void __err(const char *msg) 43 | { 44 | __log(stderr, "%s: %s", msg, strerror(errno)); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * log.h - log functions 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef LOG_H 21 | #define LOG_H 22 | 23 | 24 | #include 25 | extern void __log(FILE *stream, const char *format, ...); 26 | extern void __err(const char *msg); 27 | #define LOG(format, ...) do{__log(stdout, format, ##__VA_ARGS__);}while(0) 28 | #define ERROR(msg) do{__err(msg);}while(0) 29 | 30 | 31 | #endif // LOG_H 32 | -------------------------------------------------------------------------------- /src/md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * md5.c - the MD5 Message-Digest Algorithm (RFC 1321) 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include "md5.h" 23 | 24 | static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z) 25 | { 26 | return (x & y) | (~x & z); 27 | } 28 | 29 | static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z) 30 | { 31 | return (x & z) | (y & ~z); 32 | } 33 | 34 | static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z) 35 | { 36 | return x ^ y ^ z; 37 | } 38 | 39 | static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z) 40 | { 41 | return y ^ (x | ~z); 42 | } 43 | 44 | static inline uint32_t LEFT_ROTATE(uint32_t x, uint32_t n) 45 | { 46 | return (x << n) | (x >> (32 - n)); 47 | } 48 | 49 | #define FF(a, b, c, d, x, s, ac) \ 50 | do {(a) = (b) + LEFT_ROTATE((a) + F((b), (c), (d)) + (x) + (ac), s);} while (0) 51 | 52 | #define GG(a, b, c, d, x, s, ac) \ 53 | do {(a) = (b) + LEFT_ROTATE((a) + G((b), (c), (d)) + (x) + (ac), s);} while (0) 54 | 55 | #define HH(a, b, c, d, x, s, ac) \ 56 | do {(a) = (b) + LEFT_ROTATE((a) + H((b), (c), (d)) + (x) + (ac), s);} while (0) 57 | 58 | #define II(a, b, c, d, x, s, ac) \ 59 | do {(a) = (b) + LEFT_ROTATE((a) + I((b), (c), (d)) + (x) + (ac), s);} while (0) 60 | 61 | void md5(void *digest, const void *in, size_t len) 62 | { 63 | size_t n = len * 8 / 512 + 1; 64 | uint32_t M[(n + 1) * 16]; 65 | 66 | bzero(M, sizeof(M)); 67 | for (size_t i = 0; i < len; i++) 68 | { 69 | M[i / 4] |= ((unsigned char *)in)[i] << 8 * ((i % 4)); 70 | } 71 | M[len / 4] |= 0x80 << 8 * ((len % 4)); 72 | M[n * 16 - 2] = len * 8; 73 | 74 | uint32_t state[4] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476}; 75 | 76 | for (size_t i = 0; i < n; i++) 77 | { 78 | uint32_t X[16]; 79 | uint32_t a, b, c, d; 80 | 81 | for (int j = 0; j < 16; j++) 82 | { 83 | X[j] = M[i * 16 + j]; 84 | } 85 | 86 | a = state[0]; 87 | b = state[1]; 88 | c = state[2]; 89 | d = state[3]; 90 | 91 | // 第一轮 92 | FF(a, b, c, d, X[ 0], 7, 0xd76aa478); // 1 93 | FF(d, a, b, c, X[ 1], 12, 0xe8c7b756); // 2 94 | FF(c, d, a, b, X[ 2], 17, 0x242070db); // 3 95 | FF(b, c, d, a, X[ 3], 22, 0xc1bdceee); // 4 96 | FF(a, b, c, d, X[ 4], 7, 0xf57c0faf); // 5 97 | FF(d, a, b, c, X[ 5], 12, 0x4787c62a); // 6 98 | FF(c, d, a, b, X[ 6], 17, 0xa8304613); // 7 99 | FF(b, c, d, a, X[ 7], 22, 0xfd469501); // 8 100 | FF(a, b, c, d, X[ 8], 7, 0x698098d8); // 9 101 | FF(d, a, b, c, X[ 9], 12, 0x8b44f7af); // 10 102 | FF(c, d, a, b, X[10], 17, 0xffff5bb1); // 11 103 | FF(b, c, d, a, X[11], 22, 0x895cd7be); // 12 104 | FF(a, b, c, d, X[12], 7, 0x6b901122); // 13 105 | FF(d, a, b, c, X[13], 12, 0xfd987193); // 14 106 | FF(c, d, a, b, X[14], 17, 0xa679438e); // 15 107 | FF(b, c, d, a, X[15], 22, 0x49b40821); // 16 108 | 109 | // 第二轮 110 | GG(a, b, c, d, X[ 1], 5, 0xf61e2562); // 17 111 | GG(d, a, b, c, X[ 6], 9, 0xc040b340); // 18 112 | GG(c, d, a, b, X[11], 14, 0x265e5a51); // 19 113 | GG(b, c, d, a, X[ 0], 20, 0xe9b6c7aa); // 20 114 | GG(a, b, c, d, X[ 5], 5, 0xd62f105d); // 21 115 | GG(d, a, b, c, X[10], 9, 0x02441453); // 22 116 | GG(c, d, a, b, X[15], 14, 0xd8a1e681); // 23 117 | GG(b, c, d, a, X[ 4], 20, 0xe7d3fbc8); // 24 118 | GG(a, b, c, d, X[ 9], 5, 0x21e1cde6); // 25 119 | GG(d, a, b, c, X[14], 9, 0xc33707d6); // 26 120 | GG(c, d, a, b, X[ 3], 14, 0xf4d50d87); // 27 121 | GG(b, c, d, a, X[ 8], 20, 0x455a14ed); // 28 122 | GG(a, b, c, d, X[13], 5, 0xa9e3e905); // 29 123 | GG(d, a, b, c, X[ 2], 9, 0xfcefa3f8); // 30 124 | GG(c, d, a, b, X[ 7], 14, 0x676f02d9); // 31 125 | GG(b, c, d, a, X[12], 20, 0x8d2a4c8a); // 32 126 | 127 | // 第三轮 128 | HH(a, b, c, d, X[ 5], 4, 0xfffa3942); // 33 129 | HH(d, a, b, c, X[ 8], 11, 0x8771f681); // 34 130 | HH(c, d, a, b, X[11], 16, 0x6d9d6122); // 35 131 | HH(b, c, d, a, X[14], 23, 0xfde5380c); // 36 132 | HH(a, b, c, d, X[ 1], 4, 0xa4beea44); // 37 133 | HH(d, a, b, c, X[ 4], 11, 0x4bdecfa9); // 38 134 | HH(c, d, a, b, X[ 7], 16, 0xf6bb4b60); // 39 135 | HH(b, c, d, a, X[10], 23, 0xbebfbc70); // 40 136 | HH(a, b, c, d, X[13], 4, 0x289b7ec6); // 41 137 | HH(d, a, b, c, X[ 0], 11, 0xeaa127fa); // 42 138 | HH(c, d, a, b, X[ 3], 16, 0xd4ef3085); // 43 139 | HH(b, c, d, a, X[ 6], 23, 0x04881d05); // 44 140 | HH(a, b, c, d, X[ 9], 4, 0xd9d4d039); // 45 141 | HH(d, a, b, c, X[12], 11, 0xe6db99e5); // 46 142 | HH(c, d, a, b, X[15], 16, 0x1fa27cf8); // 47 143 | HH(b, c, d, a, X[ 2], 23, 0xc4ac5665); // 48 144 | 145 | // 第四轮 146 | II(a, b, c, d, X[ 0], 6, 0xf4292244); // 49 147 | II(d, a, b, c, X[ 7], 10, 0x432aff97); // 50 148 | II(c, d, a, b, X[14], 15, 0xab9423a7); // 51 149 | II(b, c, d, a, X[ 5], 21, 0xfc93a039); // 52 150 | II(a, b, c, d, X[12], 6, 0x655b59c3); // 53 151 | II(d, a, b, c, X[ 3], 10, 0x8f0ccc92); // 54 152 | II(c, d, a, b, X[10], 15, 0xffeff47d); // 55 153 | II(b, c, d, a, X[ 1], 21, 0x85845dd1); // 56 154 | II(a, b, c, d, X[ 8], 6, 0x6fa87e4f); // 57 155 | II(d, a, b, c, X[15], 10, 0xfe2ce6e0); // 58 156 | II(c, d, a, b, X[ 6], 15, 0xa3014314); // 59 157 | II(b, c, d, a, X[13], 21, 0x4e0811a1); // 60 158 | II(a, b, c, d, X[ 4], 6, 0xf7537e82); // 61 159 | II(d, a, b, c, X[11], 10, 0xbd3af235); // 62 160 | II(c, d, a, b, X[ 2], 15, 0x2ad7d2bb); // 63 161 | II(b, c, d, a, X[ 9], 21, 0xeb86d391); // 64 162 | 163 | state[0] += a; 164 | state[1] += b; 165 | state[2] += c; 166 | state[3] += d; 167 | } 168 | 169 | if (digest != NULL) 170 | { 171 | memcpy(digest, state, sizeof(uint32_t) * 4); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * md5.h - the MD5 Message-Digest Algorithm (RFC 1321) 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef MD5_H 21 | #define MD5_H 22 | 23 | #include 24 | extern void md5(void *digest, const void *in, size_t len); 25 | 26 | #endif // MD5_H 27 | -------------------------------------------------------------------------------- /src/relay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * relay.c - TCP relay 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "crypto.h" 28 | #include "log.h" 29 | #include "relay.h" 30 | 31 | #define UNUSED(x) do {(void)(x);} while (0) 32 | 33 | // 缓冲区大小 34 | #define BUF_SIZE 8192 35 | 36 | #ifndef EAGAIN 37 | # define EAGAIN EWOULDBLOCK 38 | #endif 39 | #ifndef EWOULDBLOCK 40 | # define EWOULDBLOCK EAGAIN 41 | #endif 42 | 43 | typedef struct 44 | { 45 | int sock_local; 46 | int sock_remote; 47 | ssize_t rx_bytes; 48 | ssize_t rx_offset; 49 | ssize_t tx_bytes; 50 | ssize_t tx_offset; 51 | crypto_evp_t evp; 52 | ev_io w_local_read; 53 | ev_io w_local_write; 54 | ev_io w_remote_read; 55 | ev_io w_remote_write; 56 | uint8_t rx_buf[BUF_SIZE]; 57 | uint8_t tx_buf[BUF_SIZE]; 58 | } ctx_t; 59 | 60 | static void local_read_cb(EV_P_ ev_io *w, int revents); 61 | static void local_write_cb(EV_P_ ev_io *w, int revents); 62 | static void remote_read_cb(EV_P_ ev_io *w, int revents); 63 | static void remote_write_cb(EV_P_ ev_io *w, int revents); 64 | static void cleanup(EV_P_ ctx_t *ctx); 65 | 66 | extern struct ev_loop *loop; 67 | 68 | void relay(int local, int remote, crypto_evp_t *evp) 69 | { 70 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 71 | if (ctx == NULL) 72 | { 73 | LOG("out of memory"); 74 | close(local); 75 | close(remote); 76 | return; 77 | } 78 | ctx->sock_local = local; 79 | ctx->sock_remote = remote; 80 | ctx->evp = *evp; 81 | 82 | ev_io_init(&(ctx->w_local_read), local_read_cb, ctx->sock_local, EV_READ); 83 | ev_io_init(&(ctx->w_local_write), local_write_cb, ctx->sock_local, EV_WRITE); 84 | ev_io_init(&(ctx->w_remote_read), remote_read_cb, ctx->sock_remote, EV_READ); 85 | ev_io_init(&(ctx->w_remote_write), remote_write_cb, ctx->sock_remote, EV_WRITE); 86 | ctx->w_local_read.data = (void *)ctx; 87 | ctx->w_local_write.data = (void *)ctx; 88 | ctx->w_remote_read.data = (void *)ctx; 89 | ctx->w_remote_write.data = (void *)ctx; 90 | ev_io_start(EV_A_ &(ctx->w_local_read)); 91 | ev_io_start(EV_A_ &(ctx->w_remote_read)); 92 | } 93 | 94 | static void local_read_cb(EV_P_ ev_io *w, int revents) 95 | { 96 | ctx_t *ctx = (ctx_t *)(w->data); 97 | 98 | UNUSED(revents); 99 | assert(ctx != NULL); 100 | 101 | ctx->tx_bytes = recv(ctx->sock_local, ctx->tx_buf, BUF_SIZE, 0); 102 | if (ctx->tx_bytes <= 0) 103 | { 104 | if (ctx->tx_bytes < 0) 105 | { 106 | LOG("client reset"); 107 | } 108 | cleanup(EV_A_ ctx); 109 | return; 110 | } 111 | crypto_encrypt(ctx->tx_buf, ctx->tx_bytes, &(ctx->evp)); 112 | ssize_t n = send(ctx->sock_remote, ctx->tx_buf, 113 | ctx->tx_bytes, MSG_NOSIGNAL); 114 | if (n < 0) 115 | { 116 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 117 | { 118 | ctx->tx_offset = 0; 119 | } 120 | else 121 | { 122 | ERROR("send"); 123 | cleanup(EV_A_ ctx); 124 | return; 125 | } 126 | } 127 | else if (n < ctx->tx_bytes) 128 | { 129 | ctx->tx_offset = n; 130 | ctx->tx_bytes -= n; 131 | } 132 | else 133 | { 134 | return; 135 | } 136 | ev_io_start(EV_A_ &(ctx->w_remote_write)); 137 | ev_io_stop(EV_A_ w); 138 | } 139 | 140 | static void local_write_cb(EV_P_ ev_io *w, int revents) 141 | { 142 | ctx_t *ctx = (ctx_t *)(w->data); 143 | 144 | UNUSED(revents); 145 | assert(ctx != NULL); 146 | assert(ctx->rx_bytes > 0); 147 | 148 | ssize_t n = send(ctx->sock_local, ctx->rx_buf + ctx->rx_offset, 149 | ctx->rx_bytes, MSG_NOSIGNAL); 150 | if (n < 0) 151 | { 152 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 153 | { 154 | return; 155 | } 156 | else 157 | { 158 | ERROR("send"); 159 | cleanup(EV_A_ ctx); 160 | return; 161 | } 162 | } 163 | else if (n < ctx->rx_bytes) 164 | { 165 | ctx->rx_offset += n; 166 | ctx->rx_bytes -= n; 167 | } 168 | else 169 | { 170 | ev_io_start(EV_A_ &(ctx->w_remote_read)); 171 | ev_io_stop(EV_A_ w); 172 | } 173 | } 174 | 175 | static void remote_read_cb(EV_P_ ev_io *w, int revents) 176 | { 177 | ctx_t *ctx = (ctx_t *)(w->data); 178 | 179 | UNUSED(revents); 180 | assert(ctx != NULL); 181 | 182 | ctx->rx_bytes = recv(ctx->sock_remote, ctx->rx_buf, BUF_SIZE, 0); 183 | if (ctx->rx_bytes <= 0) 184 | { 185 | if (ctx->rx_bytes < 0) 186 | { 187 | LOG("server reset"); 188 | } 189 | cleanup(EV_A_ ctx); 190 | return; 191 | } 192 | crypto_decrypt(ctx->rx_buf, ctx->rx_bytes, &(ctx->evp)); 193 | ssize_t n = send(ctx->sock_local, ctx->rx_buf, 194 | ctx->rx_bytes, MSG_NOSIGNAL); 195 | if (n < 0) 196 | { 197 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 198 | { 199 | ctx->rx_offset = 0; 200 | } 201 | else 202 | { 203 | ERROR("send"); 204 | cleanup(EV_A_ ctx); 205 | return; 206 | } 207 | } 208 | else if (n < ctx->rx_bytes) 209 | { 210 | ctx->rx_offset = n; 211 | ctx->rx_bytes -= n; 212 | } 213 | else 214 | { 215 | return; 216 | } 217 | ev_io_start(EV_A_ &(ctx->w_local_write)); 218 | ev_io_stop(EV_A_ w); 219 | } 220 | 221 | static void remote_write_cb(EV_P_ ev_io *w, int revents) 222 | { 223 | ctx_t *ctx = (ctx_t *)(w->data); 224 | 225 | UNUSED(revents); 226 | assert(ctx != NULL); 227 | assert(ctx->tx_bytes > 0); 228 | 229 | ev_io_stop(EV_A_ w); 230 | 231 | ssize_t n = send(ctx->sock_remote, ctx->tx_buf + ctx->tx_offset, 232 | ctx->tx_bytes, MSG_NOSIGNAL); 233 | if (n < 0) 234 | { 235 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 236 | { 237 | return; 238 | } 239 | else 240 | { 241 | ERROR("send"); 242 | cleanup(EV_A_ ctx); 243 | return; 244 | } 245 | } 246 | else if (n < ctx->tx_bytes) 247 | { 248 | ctx->tx_offset += n; 249 | ctx->tx_bytes -= n; 250 | } 251 | else 252 | { 253 | ev_io_start(EV_A_ &(ctx->w_local_read)); 254 | ev_io_stop(EV_A_ w); 255 | } 256 | } 257 | 258 | static void cleanup(EV_P_ ctx_t *ctx) 259 | { 260 | ev_io_stop(EV_A_ &ctx->w_local_read); 261 | ev_io_stop(EV_A_ &ctx->w_local_write); 262 | ev_io_stop(EV_A_ &ctx->w_remote_read); 263 | ev_io_stop(EV_A_ &ctx->w_remote_write); 264 | close(ctx->sock_local); 265 | close(ctx->sock_remote); 266 | free(ctx); 267 | } 268 | -------------------------------------------------------------------------------- /src/relay.h: -------------------------------------------------------------------------------- 1 | /* 2 | * relay.h - TCP relay 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | #ifndef RELAY_H 22 | #define RELAY_H 23 | 24 | #include "crypto.h" 25 | extern void relay(int local, int remote, crypto_evp_t *evp); 26 | 27 | #endif // RELAY_H 28 | -------------------------------------------------------------------------------- /src/socks5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * socks5.c - SOCKS5 Protocol 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "log.h" 32 | #include "socks5.h" 33 | #include "utils.h" 34 | 35 | #define UNUSED(x) do {(void)(x);} while (0) 36 | #define BUF_SIZE 264 37 | 38 | typedef enum 39 | { 40 | CLOSED = 0, 41 | HELLO_RCVD, 42 | HELLO_ERR, 43 | HELLO_SENT, 44 | REQ_RCVD, 45 | REQ_ERR 46 | } state_t; 47 | 48 | typedef struct 49 | { 50 | int sock; 51 | state_t state; 52 | int len; 53 | void (*cb)(int, char *, char *); 54 | ev_io w_read; 55 | ev_io w_write; 56 | char host[257]; 57 | char port[15]; 58 | uint8_t buf[BUF_SIZE]; 59 | } ctx_t; 60 | 61 | static void socks5_send_cb(EV_P_ ev_io *w, int revents); 62 | static void socks5_recv_cb(EV_P_ ev_io *w, int revents); 63 | 64 | extern struct ev_loop *loop; 65 | 66 | void socks5_accept(int sock, void (*cb)(int, char *, char *)) 67 | { 68 | ctx_t *ctx = (ctx_t *)malloc(sizeof(ctx_t)); 69 | if (ctx == NULL) 70 | { 71 | LOG("out of memory"); 72 | close(sock); 73 | return; 74 | } 75 | ctx->sock = sock; 76 | ctx->cb = cb; 77 | ctx->state = CLOSED; 78 | 79 | ev_io_init(&(ctx->w_read), socks5_recv_cb, ctx->sock, EV_READ); 80 | ev_io_init(&(ctx->w_write), socks5_send_cb, ctx->sock, EV_WRITE); 81 | ctx->w_read.data = (void *)ctx; 82 | ctx->w_write.data = (void *)ctx; 83 | 84 | ev_io_start(EV_A_ &(ctx->w_read)); 85 | } 86 | 87 | static void socks5_recv_cb(EV_P_ ev_io *w, int revents) 88 | { 89 | ctx_t *ctx = (ctx_t *)(w->data); 90 | 91 | UNUSED(revents); 92 | assert(ctx != NULL); 93 | 94 | ev_io_stop(EV_A_ w); 95 | 96 | bzero(ctx->buf, BUF_SIZE); 97 | ssize_t n = recv(ctx->sock, ctx->buf, BUF_SIZE, 0); 98 | if (n <= 0) 99 | { 100 | if (n < 0) 101 | { 102 | LOG("client reset"); 103 | } 104 | close(ctx->sock); 105 | free(ctx); 106 | return; 107 | } 108 | 109 | switch (ctx->state) 110 | { 111 | case CLOSED: 112 | { 113 | // SOCKS5 HELLO 114 | // +-----+----------+----------+ 115 | // | VER | NMETHODS | METHODS | 116 | // +-----+----------+----------+ 117 | // | 1 | 1 | 1 to 255 | 118 | // +-----+----------+----------+ 119 | int error = 0; 120 | if (ctx->buf[0] != 0x05) 121 | { 122 | error = 1; 123 | } 124 | uint8_t nmethods = ctx->buf[1]; 125 | uint8_t i; 126 | for (i = 0; i < nmethods; i++) 127 | { 128 | if (ctx->buf[2 + i] == 0x00) 129 | { 130 | break; 131 | } 132 | } 133 | if (i >= nmethods) 134 | { 135 | error = 2; 136 | } 137 | // SOCKS5 HELLO 138 | // +-----+--------+ 139 | // | VER | METHOD | 140 | // +-----+--------+ 141 | // | 1 | 1 | 142 | // +-----+--------+ 143 | ctx->buf[0] = 0x05; 144 | ctx->buf[1] = 0x00; 145 | ctx->len = 2; 146 | ctx->state = HELLO_RCVD; 147 | if (error != 0) 148 | { 149 | ctx->state = HELLO_ERR; 150 | ctx->buf[1] = 0xff; 151 | } 152 | ev_io_start(EV_A_ &(ctx->w_write)); 153 | break; 154 | } 155 | case HELLO_SENT: 156 | { 157 | // SOCKS5 REQUEST 158 | // +-----+-----+-------+------+----------+----------+ 159 | // | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 160 | // +-----+-----+-------+------+----------+----------+ 161 | // | 1 | 1 | X'00' | 1 | Variable | 2 | 162 | // +-----+-----+-------+------+----------+----------+ 163 | int error = 0; 164 | if (ctx->buf[0] != 0x05) 165 | { 166 | error = 1; 167 | } 168 | if (ctx->buf[1] != 0x01) 169 | { 170 | // 只支持 CONNECT 命令 171 | error = 2; 172 | } 173 | if (ctx->buf[3] == 0x01) 174 | { 175 | // IPv4 地址 176 | inet_ntop(AF_INET, (const void *)(ctx->buf + 4), ctx->host, 177 | INET_ADDRSTRLEN); 178 | sprintf(ctx->port, "%u", ntohs(*(uint16_t *)(ctx->buf + 8))); 179 | } 180 | else if (ctx->buf[3] == 0x03) 181 | { 182 | // 域名 183 | memcpy(ctx->host, ctx->buf + 5, ctx->buf[4]); 184 | ctx->host[ctx->buf[4]] = '\0'; 185 | sprintf(ctx->port, "%u", 186 | ntohs(*(uint16_t *)(ctx->buf + ctx->buf[4] + 5))); 187 | } 188 | else if (ctx->buf[3] == 0x04) 189 | { 190 | // IPv6 地址 191 | inet_ntop(AF_INET6, (const void *)(ctx->buf + 4), ctx->host, 192 | INET6_ADDRSTRLEN); 193 | sprintf(ctx->port, "%u", ntohs(*(uint16_t *)(ctx->buf + 20))); 194 | } 195 | else 196 | { 197 | // 不支持的地址类型 198 | error = 3; 199 | } 200 | 201 | // SOCKS5 REPLY 202 | // +-----+-----+-------+------+----------+----------+ 203 | // | VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 204 | // +-----+-----+-------+------+----------+----------+ 205 | // | 1 | 1 | X'00' | 1 | Variable | 2 | 206 | // +-----+-----+-------+------+----------+----------+ 207 | bzero(ctx->buf, 10); 208 | ctx->buf[0] = 0x05; 209 | if (error == 0) 210 | { 211 | ctx->buf[1] = 0x00; 212 | } 213 | else if (error == 1) 214 | { 215 | ctx->buf[1] = 0x01; 216 | } 217 | else if (error == 2) 218 | { 219 | ctx->buf[1] = 0x07; 220 | } 221 | else 222 | { 223 | ctx->buf[1] = 0x08; 224 | } 225 | ctx->buf[2] = 0x00; 226 | ctx->buf[3] = 0x01; 227 | ctx->len = 10; 228 | ctx->state = REQ_RCVD; 229 | if (error != 0) 230 | { 231 | ctx->state = REQ_ERR; 232 | } 233 | ev_io_start(EV_A_ &ctx->w_write); 234 | break; 235 | } 236 | default: 237 | { 238 | // 不应该来到这里 239 | assert(0 != 0); 240 | break; 241 | } 242 | } 243 | } 244 | 245 | static void socks5_send_cb(EV_P_ ev_io *w, int revents) 246 | { 247 | ctx_t *ctx = (ctx_t *)(w->data); 248 | 249 | UNUSED(revents); 250 | assert(ctx != NULL); 251 | 252 | ev_io_stop(EV_A_ w); 253 | 254 | ssize_t n = send(ctx->sock, ctx->buf, ctx->len, MSG_NOSIGNAL); 255 | if (n != ctx->len) 256 | { 257 | if (n < 0) 258 | { 259 | ERROR("send"); 260 | } 261 | close(ctx->sock); 262 | free(ctx); 263 | return; 264 | } 265 | 266 | switch (ctx->state) 267 | { 268 | case HELLO_RCVD: 269 | case HELLO_ERR: 270 | { 271 | if (ctx->state == HELLO_RCVD) 272 | { 273 | ctx->state = HELLO_SENT; 274 | ev_io_start(EV_A_ &(ctx->w_read)); 275 | } 276 | else 277 | { 278 | close(ctx->sock); 279 | free(ctx); 280 | } 281 | break; 282 | } 283 | case REQ_RCVD: 284 | case REQ_ERR: 285 | { 286 | if (ctx->state == REQ_RCVD) 287 | { 288 | (ctx->cb)(ctx->sock, ctx->host, ctx->port); 289 | free(ctx); 290 | } 291 | else 292 | { 293 | close(ctx->sock); 294 | free(ctx); 295 | } 296 | break; 297 | } 298 | default: 299 | { 300 | // 不应该来到这里 301 | assert(0 != 0); 302 | break; 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/socks5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * socks5.h - SOCKS5 Protocol 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef SOCKS5_H 21 | #define SOCKS5_H 22 | 23 | #include 24 | 25 | extern void socks5_accept(int sock, void (*cb)(int, char *host, char *port)); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.c - Some util functions 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "utils.h" 36 | 37 | #ifndef IP6T_SO_ORIGINAL_DST 38 | # define IP6T_SO_ORIGINAL_DST 80 39 | #endif 40 | 41 | ssize_t rand_bytes(void *stream, size_t len) 42 | { 43 | static int urand = -1; 44 | if (urand == -1) 45 | { 46 | urand = open("/dev/urandom", O_RDONLY, 0); 47 | } 48 | if (urand < 0) 49 | { 50 | return -1; 51 | } 52 | return read(urand, stream, len); 53 | } 54 | 55 | int setnonblock(int fd) 56 | { 57 | int flags; 58 | flags = fcntl(fd, F_GETFL, 0); 59 | if (flags == -1) 60 | { 61 | return -1; 62 | } 63 | if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) 64 | { 65 | return -1; 66 | } 67 | return 0; 68 | } 69 | 70 | int settimeout(int fd) 71 | { 72 | struct timeval timeout = { .tv_sec = 10, .tv_usec = 0}; 73 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) != 0) 74 | { 75 | return -1; 76 | } 77 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) != 0) 78 | { 79 | return -1; 80 | } 81 | return 0; 82 | } 83 | 84 | int setreuseaddr(int fd) 85 | { 86 | int reuseaddr = 1; 87 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) != 0) 88 | { 89 | return -1; 90 | } 91 | return 0; 92 | } 93 | 94 | int setkeepalive(int fd) 95 | { 96 | int keepalive = 1; 97 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(int)) != 0) 98 | { 99 | return -1; 100 | } 101 | return 0; 102 | } 103 | 104 | int getdestaddr(int fd, struct sockaddr *addr, socklen_t *addrlen) 105 | { 106 | if (getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, addr, addrlen) == 0) 107 | { 108 | return 0; 109 | } 110 | if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, addr, addrlen) == 0) 111 | { 112 | return 0; 113 | } 114 | return -1; 115 | } 116 | 117 | int getsockerror(int fd) 118 | { 119 | int error = 0; 120 | socklen_t len = sizeof(int); 121 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) 122 | { 123 | return -1; 124 | } 125 | return error; 126 | } 127 | 128 | int runas(const char *user) 129 | { 130 | struct passwd *pw_ent = NULL; 131 | 132 | if (user != NULL) 133 | { 134 | pw_ent = getpwnam(user); 135 | } 136 | 137 | if (pw_ent != NULL) 138 | { 139 | if (setregid(pw_ent->pw_gid, pw_ent->pw_gid) != 0) 140 | { 141 | return -1; 142 | } 143 | if (setreuid(pw_ent->pw_uid, pw_ent->pw_uid) != 0) 144 | { 145 | return -1; 146 | } 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | int daemonize(const char *pidfile, const char *logfile) 153 | { 154 | pid_t pid; 155 | 156 | pid = fork(); 157 | if (pid < 0) 158 | { 159 | fprintf(stderr, "fork: %s\n", strerror(errno)); 160 | return -1; 161 | } 162 | 163 | if (pid > 0) 164 | { 165 | FILE *fp = fopen(pidfile, "w"); 166 | if (fp == NULL) 167 | { 168 | fprintf(stderr, "Invalid pid file\n"); 169 | } 170 | else 171 | { 172 | fprintf(fp, "%d", pid); 173 | fclose(fp); 174 | } 175 | exit(EXIT_SUCCESS); 176 | } 177 | 178 | umask(0); 179 | 180 | if (setsid() < 0) 181 | { 182 | fprintf(stderr, "setsid: %s\n", strerror(errno)); 183 | return -1; 184 | } 185 | 186 | fclose(stdin); 187 | FILE *fp; 188 | fp = freopen(logfile, "w", stdout); 189 | if (fp == NULL) 190 | { 191 | fprintf(stderr, "freopen: %s\n", strerror(errno)); 192 | return -1; 193 | } 194 | fp = freopen(logfile, "w", stderr); 195 | if (fp == NULL) 196 | { 197 | fprintf(stderr, "freopen: %s\n", strerror(errno)); 198 | return -1; 199 | } 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.h - Some util functions 3 | * 4 | * Copyright (C) 2014 - 2015, Xiaoxiao 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef UTILS_H 21 | #define UTILS_H 22 | 23 | #include 24 | 25 | extern ssize_t rand_bytes(void *stream, size_t len); 26 | extern int setnonblock(int fd); 27 | extern int settimeout(int fd); 28 | extern int setreuseaddr(int fd); 29 | extern int setkeepalive(int fd); 30 | extern int getdestaddr(int fd, struct sockaddr *addr, socklen_t *addrlen); 31 | extern int getsockerror(int fd); 32 | extern int runas(const char *user); 33 | extern int daemonize(const char *pidfile, const char *logfile); 34 | 35 | #endif // UTILS_H 36 | -------------------------------------------------------------------------------- /test/test.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | user=nobody 3 | 4 | [server] 5 | address=127.0.0.1 6 | port=1205, 1206 7 | key=testkey1 8 | 9 | [server] 10 | address=127.0.0.1 11 | port=1207 12 | key=testkey2 13 | 14 | [local] 15 | address=127.0.0.1 16 | port=1080 17 | 18 | [redir] 19 | address=127.0.0.1 20 | port=1081 21 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import signal 6 | import time 7 | from subprocess import Popen 8 | 9 | ioserver = ['src/ioserver', '-c', 'test/test.conf'] 10 | ioclient = ['src/ioclient', '-c', 'test/test.conf'] 11 | ioredir = ['src/ioredir', '-c', 'test/test.conf'] 12 | 13 | p1 = Popen(ioserver, shell=False, bufsize=0, close_fds=True) 14 | p2 = Popen(ioclient, shell=False, bufsize=0, close_fds=True) 15 | p3 = Popen(ioredir, shell=False, bufsize=0, close_fds=True) 16 | time.sleep(1) 17 | 18 | cmds = ['curl -o /dev/null --socks5-hostname 127.0.0.2:1080 https://github.com/', 19 | 'curl -o /dev/null --socks5-hostname 127.0.0.2:1080 https://www.baidu.com/', 20 | 'curl -o /dev/null --socks5 127.0.0.2:1080 https://github.com/', 21 | 'curl -o /dev/null --local-port 2001 https://github.com/', 22 | 'curl -o /dev/null --local-port 2002 https://www.baidu.com/'] 23 | 24 | for cmd in cmds: 25 | p4 = Popen(cmd.split(), shell=False, bufsize=0, close_fds=True) 26 | if p4 is not None: 27 | r = p4.wait() 28 | if r == 0: 29 | print('test passed') 30 | time.sleep(1) 31 | 32 | for p in [p1, p2, p3]: 33 | try: 34 | os.kill(p.pid, signal.SIGINT) 35 | os.waitpid(p.pid, 0) 36 | except OSError: 37 | pass 38 | 39 | sys.exit(r) 40 | --------------------------------------------------------------------------------