├── bin ├── forward └── forwardarm ├── makefile ├── README.md └── forward.cpp /bin/forward: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangyu-/tiny-encrypted-udp-tunnel/HEAD/bin/forward -------------------------------------------------------------------------------- /bin/forwardarm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangyu-/tiny-encrypted-udp-tunnel/HEAD/bin/forwardarm -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | ccarm=mips-openwrt-linux-g++ 2 | all: 3 | g++ forward.cpp -o forward -static 4 | ${ccarm} forward.cpp -o forwardarm -static -lgcc_eh 5 | #g++ forward.cpp aes.c -o forward -static 6 | # ${ccarm} forward.cpp aes.c -o forwardarm -static -lgcc_eh 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tiny-encrypted-udp-tunnel 2 | tiny singlefile encrypted udp tunnel/forwarder 3 | 4 | # keywords 5 | encrypted,udp port forwarder,udp tunnel,openvpn being blocked,hide openvpn traffic 6 | 7 | # background 8 | this program is originally designed for tunneling udp mode openvpn.(though it can be used for forwarding/tunneling any udp based protocol) 9 | 10 | as we know,openvpn uses encryption for authentication and it encrypts the data it carrys. 11 | 12 | however,it doesnt encrypt the handshake and control flow.in other words,it doenst aim at prevent itself from being detected by firewalls. 13 | 14 | while openssh provided an easy-to-use tunnel feature,it doesnt support tunneling udp.ssh aslo doesnt prevent itself from being detected by firewalls. 15 | 16 | this program allow you to do encrypted tunneling so that firewalls wont be able to know the existence of a openvpn connection. 17 | 18 | has been stablely running for years on my server and router. 19 | 20 | linux x64 and mips_ar71xx binaries have already been built. 21 | 22 | # usage 23 | this program is essentially a port forwarder which allows you to use a key for encryption/decryption at either side.if you use a pair of them,one at local host,the other at remote host,they form a tunnel together. 24 | 25 | forward -l [adressA:]portA -r [adressB:]portB [-a passwdA] [-b passwdB] 26 | 27 | after being started,this program will forward all packet received from adressA:portA to adressB:portB. and all packet received back from adressB:portB will be forward back to adressA:portA. it can handle multiple udp connection. 28 | 29 | basic option: 30 | 31 | -l -r option are required. -l indicates the local adress&port, -r indicates the remote adress&port. 32 | 33 | adressA and adressB are optional,if adressA or adressB are ommited,127.0.0.1 will be used by default.only ipv4 adress has been tested. 34 | 35 | 36 | encryption option: 37 | 38 | -a and -b are optional. 39 | 40 | if -a is used ,all packet goes into adressA:portA will be decrypted by passwdA,and all packet goes out from adressA:portA will be encrypted by passwdA.if -a is omited,data goes into/out adressA:portA will not be encrypted/decrypted. 41 | 42 | if -b is used ,all packet goes into adressB:portB will be decrypted by passwdB,and all packet goes out from adressB:portB will be encrypted by passwdB.if -b is omited,data goes into/out adressB:portB will not be encrypted/decrypted. 43 | 44 | 45 | 46 | 47 | # example 48 | assume an udp mode openvpn server is running at 44.55.66.77:9000 49 | 50 | run this at sever side at 44.55.66.77: 51 | ./forward -l0.0.0.0:9001 -r127.0.0.1:9000 -a'abcd' > /dev/null & 52 | 53 | run this at client side: 54 | ./forward -l 127.0.0.1:9002 -r 44.55.66.77:9001 -b 'abcd' >/dev/null& 55 | 56 | now,configure you openvpn client to connect to 127.0.0.1:9002 57 | 58 | dataflow: 59 | 60 | 61 | client computer                                                           server computer (44.55.66.77) 62 | +---------------------------------------------+ +------------------------------------------------+ 63 | | openvpn | | openvpn server | 64 | | client forwarder | | forwarder daemon | 65 | | +-----------+ +------------+ | | +-----------+ +------------+ | 66 | | | |r | |r | | | |r | | | 67 | | | |a 9| |a | | 9| |a 9| | | 68 | | | |n 0| |n | | 0| |n 0| | | 69 | | | |d <--------> 0| |d<-----------------------------> 0| |d <--------> 0| | | 70 | | | |o(unencrypted)2| |o | (encrypted channel | 1| |o (unencrypted)0| | | 71 | | | |m | |m | by key 'abcd') | | |m | | | 72 | | +-----------+ +------------+ | | +-----------+ +------------+ | 73 | | | | | 74 | +---------------------------------------------+ +------------------------------------------------+ 75 | 76 | 77 | 78 | # method of encryption 79 | currently this program only use XOR for encrypting.mainly bc i use a mips_ar71xx router as client.router's cpu is slow,i personally need fast processing speed.and XOR is enough for fooling the firewall i have encountered. 80 | 81 | nevertheless,you can easily integrate your own encrytion algotirhm into this program if you need stronger encryption.all you need to do is to rewrite 'void encrypt(char * input,int len,char *key)' and 'void decrypt(char * input,int len,char *key)'. 82 | 83 | (a good way to implemnet AES encrytion might be using the lib in this repo https://github.com/kokke/tiny-AES128-C ) 84 | -------------------------------------------------------------------------------- /forward.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | //#include"aes.h" 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | using namespace std; 20 | 21 | map mp; 22 | 23 | char local_address[100], remote_address[100]; 24 | int local_port = -1, remote_port = -1; 25 | char keya[100], keyb[100]; 26 | char iv[100]; 27 | const int buf_len = 20480; 28 | 29 | void handler(int num) { 30 | int status; 31 | int pid; 32 | while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 33 | if (WIFEXITED(status)) { 34 | //printf("The child exit with code %d",WEXITSTATUS(status)); 35 | } 36 | } 37 | 38 | } 39 | void encrypt(char * input, int len, char *key) { 40 | int i, j; 41 | //char tmp[buf_len]; 42 | //len=len/16*16+1; 43 | //AES128_CBC_encrypt_buffer((uint8_t *)tmp, (uint8_t *)input, len, (uint8_t *)key, (uint8_t *)iv); 44 | for (i = 0, j = 0; i < len; i++, j++) { 45 | if (key[j] == 0) 46 | j = 0; 47 | input[i] ^= key[j]; 48 | } 49 | } 50 | void decrypt(char * input, int len, char *key) { 51 | int i, j; 52 | //char tmp[buf_len]; 53 | //len=len/16*16+1; 54 | //AES128_CBC_decrypt_buffer((uint8_t *)tmp, (uint8_t *)input, len, (uint8_t *)key, (uint8_t *)iv); 55 | //for(i=0;i", optopt); 134 | } 135 | } 136 | 137 | if (no_l) 138 | printf("error: -i not found\n"); 139 | if (no_r) 140 | printf("error: -o not found\n"); 141 | if (no_l || no_r) { 142 | exit(-1); 143 | } 144 | 145 | struct sockaddr_in local_me, local_other; 146 | int local_listen_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 147 | int yes = 1; 148 | setsockopt(local_listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); 149 | 150 | char buf[buf_len]; 151 | socklen_t slen = sizeof(sockaddr_in); 152 | memset(&local_me, 0, sizeof(local_me)); 153 | local_me.sin_family = AF_INET; 154 | local_me.sin_port = htons(local_port); 155 | local_me.sin_addr.s_addr = inet_addr(local_address); 156 | if (bind(local_listen_fd, (struct sockaddr*) &local_me, slen) == -1) { 157 | printf("socket bind error"); 158 | exit(1); 159 | } 160 | while (1) { 161 | socklen_t recv_len; 162 | if ((recv_len = recvfrom(local_listen_fd, buf, buf_len, 0, 163 | (struct sockaddr *) &local_other, &slen)) == -1) { 164 | printf("recv_from error"); 165 | exit(1); 166 | } 167 | printf("Received packet from %s:%d\n", inet_ntoa(local_other.sin_addr), 168 | ntohs(local_other.sin_port)); 169 | 170 | if (keya[0]) { 171 | decrypt(buf, recv_len, keya);/*recv_len-=16;*/ 172 | } 173 | buf[recv_len] = 0; 174 | printf("recv_len: %d\n", recv_len); 175 | fflush(stdout); 176 | //printf("Data: %s\n" , buf); 177 | int local_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 178 | //local_me.sin_addr.s_addr=inet_addr("127.0.0.1"); 179 | setsockopt(local_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); 180 | if (bind(local_fd, (struct sockaddr*) &local_me, slen) == -1) { 181 | printf("socket bind error in chilld"); 182 | exit(1); 183 | } 184 | int ret = connect(local_fd, (struct sockaddr *) &local_other, slen);//父进程替子进程做 185 | if (fork() == 0) //子 186 | { 187 | if (ret != 0) { 188 | printf("connect return %d @1\n", ret); 189 | exit(1); 190 | } 191 | close(local_listen_fd); 192 | 193 | struct sockaddr_in remote_me, remote_other; 194 | 195 | memset(&remote_other, 0, sizeof(remote_other)); 196 | remote_other.sin_family = AF_INET; 197 | //printf("remote_address=%s remote_port=%d\n",remote_address,remote_port); 198 | remote_other.sin_port = htons(remote_port); 199 | remote_other.sin_addr.s_addr = inet_addr(remote_address); 200 | int remote_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 201 | ret = connect(remote_fd, (struct sockaddr *) &remote_other, slen); 202 | if (ret != 0) { 203 | printf("connect return %d @2\n", ret); 204 | exit(1); 205 | } 206 | 207 | if (keyb[0]) {/*recv_len+=16;*/ 208 | encrypt(buf, recv_len, keyb); 209 | } 210 | ret = send(remote_fd, buf, recv_len, 0); 211 | printf("send return %d\n", ret); 212 | if (ret < 0) 213 | exit(-1); 214 | 215 | setnonblocking(remote_fd); 216 | setnonblocking(local_fd); 217 | int epollfd = epoll_create1(0); 218 | const int max_events = 4096; 219 | struct epoll_event ev, events[max_events]; 220 | if (epollfd < 0) { 221 | printf("epoll return %d\n", epollfd); 222 | exit(-1); 223 | } 224 | ev.events = EPOLLIN; 225 | ev.data.fd = local_fd; 226 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, local_fd, &ev); 227 | if (ret < 0) { 228 | printf("epoll_ctl return %d\n", ret); 229 | exit(-1); 230 | } 231 | ev.events = EPOLLIN; 232 | ev.data.fd = remote_fd; 233 | ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, remote_fd, &ev); 234 | if (ret < 0) { 235 | printf("epoll_ctl return %d\n", ret); 236 | exit(-1); 237 | } 238 | for (;;) { 239 | int nfds = epoll_wait(epollfd, events, max_events, 180 * 1000); 240 | if (nfds <= 0) { 241 | printf("epoll_wait return %d\n", nfds); 242 | exit(-1); 243 | } 244 | int n; 245 | for (n = 0; n < nfds; ++n) { 246 | if (events[n].data.fd == local_fd) { 247 | recv_len = recv(local_fd, buf, buf_len, 0); 248 | if (recv_len < 0) { 249 | printf("recv return %d @1", recv_len); 250 | exit(1); 251 | } 252 | if (keya[0]) { 253 | decrypt(buf, recv_len, keya);/*recv_len-=16;*/ 254 | } 255 | buf[recv_len] = 0; 256 | printf("len %d received from child@1\n", recv_len); 257 | //printf("%s received from child@1\n",buf); 258 | if (keyb[0]) {/*recv_len+=16;*/ 259 | encrypt(buf, recv_len, keyb); 260 | } 261 | //printf("before send %s\n",buf); 262 | ret = send(remote_fd, buf, recv_len, 0); 263 | if (ret < 0) { 264 | printf("send return %d at @1", ret); 265 | exit(1); 266 | } 267 | printf("send return %d @1\n", ret); 268 | } else if (events[n].data.fd == remote_fd) { 269 | recv_len = recv(remote_fd, buf, buf_len, 0); 270 | if (recv_len < 0) { 271 | printf("recv return -1 @2", recv_len); 272 | exit(1); 273 | } 274 | if (keyb[0]) { 275 | decrypt(buf, recv_len, keyb);/*recv_len-=16;*/ 276 | } 277 | buf[recv_len] = 0; 278 | printf("len %d received from child@1\n", recv_len); 279 | //printf("%s received from child@2\n",buf); 280 | if (keya[0]) {/*recv_len+=16*/ 281 | ; 282 | encrypt(buf, recv_len, keya); 283 | } 284 | ret = send(local_fd, buf, recv_len, 0); 285 | if (ret < 0) { 286 | printf("send return %d @2", ret); 287 | exit(1); 288 | } 289 | 290 | printf("send return %d @2\n", ret); 291 | } 292 | } 293 | } 294 | exit(0); 295 | } else { 296 | close(local_fd); 297 | } 298 | //exit(0); 299 | } 300 | 301 | return 0; 302 | } 303 | --------------------------------------------------------------------------------