1 /* This is https://fenua.org/gaetan/src/usocks-0.7.c */
2 /*
3 * Copyright (C) 2013-2018, Gaetan Bisson <bisson@archlinux.org>.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /*
19 * USocks. Minimalistic SOCKS5 proxying library.
20 *
21 * USocks implements a connect() function over the system one in order to
22 * forward connections through a prescribed SOCKS5 proxy; its design focuses
23 * are code clarity and conciseness.
24 *
25 * Compile with:
26 *
27 * cc -O2 -fPIC -ldl -shared -o usocks.so usocks.c
28 *
29 * Use by exporting:
30 *
31 * USOCKS_PORT=7772
32 * USOCKS_ADDR=127.0.0.1
33 * LD_PRELOAD=`pwd`/usocks.so
34 */
35
36
37 /* ****************************************************************************
38 *
39 * HEADERS
40 *
41 */
42
43
44 #define _GNU_SOURCE
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <dlfcn.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55
56
57 /* ****************************************************************************
58 *
59 * INITIALIZE PROXY DATA AND LOCATE SYSTEM FUNCTIONS
60 *
61 */
62
63
64 struct sockaddr_in us_proxy;
65
66 typedef int (*connect_t)(int, const struct sockaddr *, socklen_t);
67 connect_t sys_connect;
68
69 int us_init (void) {
70 char *port = getenv("USOCKS_PORT");
71 char *addr = getenv("USOCKS_ADDR");
72 if (!port) return -1;
73 if (!addr) return -1;
74
75 memset(&us_proxy, 0, sizeof(us_proxy));
76 us_proxy.sin_family = AF_INET;
77 us_proxy.sin_port = htons(atoi(port));
78 us_proxy.sin_addr.s_addr = inet_addr(addr);
79
80 sys_connect = (connect_t)(intptr_t)dlsym(RTLD_NEXT, "connect");
81 return 0;
82 }
83
84
85 /* ****************************************************************************
86 *
87 * LEAVE NO BYTES BEHIND
88 *
89 */
90
91
92 int us_sendall (int socket, const char *buffer, size_t length, int flags) {
93 int r, off=0;
94 while(off<length) {
95 r = send(socket, buffer+off, length-off, flags);
96 if (r<0) return -1;
97 off += r;
98 }
99 return off;
100 }
101
102 int us_recvall (int socket, char *buffer, size_t length, int flags) {
103 int r, off=0;
104 while (off<length) {
105 r = recv(socket, buffer+off, length-off, flags);
106 if (r<0) return -1;
107 off += r;
108 }
109 return off;
110 }
111
112
113 /* ****************************************************************************
114 *
115 * REDEFINE CONNECT()
116 *
117 */
118
119
120 const unsigned char l4[] = { 0x7f }; /* matches loopback IPv4 addresses */
121 const unsigned char l6[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; /* matches loopback IPv6 address */
122 const unsigned char l64[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f }; /* matches loopback IPv6to4 addresses */
123
124 int connect (int sock, const struct sockaddr *addr, socklen_t len) {
125 int p, t, v, f=sizeof(t);
126 char b[256];
127
128 if (!sys_connect) if (us_init()) return -1;
129
130 /* let unix domain sockets and loopback traffic through */
131 switch (addr->sa_family) {
132 case AF_UNIX:
133 return sys_connect(sock,addr,len);
134 case AF_INET6:
135 #if 0
136 if (!memcmp(&(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), l64, 13) ||
137 !memcmp(&(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), l6, 16))
138 return sys_connect(sock,addr,len);
139 #endif
140 v=16;
141 break;
142 case AF_INET:
143 #if 0
144 /*if (!memcmp(&(((struct sockaddr_in *)addr)->sin_addr.s_addr), l4, 1))
145 return sys_connect(sock,addr,len);*/
146 #endif
147 v=4;
148 break;
149 default:
150 return -1;
151 }
152
153 /* let non-TCP through */
154 getsockopt(sock, SOL_SOCKET, SO_TYPE, &t, (socklen_t *)&f);
155 if (t!=SOCK_STREAM) return sys_connect(sock,addr,len);
156
157 /* open blocking connection to proxy */
158 f = fcntl(sock, F_GETFL);
159 p = socket(AF_INET, SOCK_STREAM, 0);
160 fcntl(p, F_SETFL, f & ~O_NONBLOCK);
161 if (sys_connect(p,(struct sockaddr*)&us_proxy,sizeof(us_proxy))) return -1;
162
163 /* protocol version and authentication method */
164 memcpy(b, "\x05\x01\x00", 3);
165 if (us_sendall(p,b,3,0)!=3) goto err;
166 if (us_recvall(p,b,2,0)!=2) goto err;
167 if (memcmp(b,"\x05\x00",2)) goto err;
168
169 /* connection request */
170 memcpy(b, "\x05\x01\x00", 3);
171 if (v==4) {
172 b[3] = '\x01';
173 memcpy(b+4, &(((struct sockaddr_in *)addr)->sin_addr.s_addr), 4);
174 memcpy(b+8, &(((struct sockaddr_in *)addr)->sin_port), 2);
175 } else {
176 b[3] = '\x04';
177 memcpy(b+4, &(((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr), 16);
178 memcpy(b+20, &(((struct sockaddr_in6 *)addr)->sin6_port), 2);
179 }
180 if (us_sendall(p,b,v+6,0)!=v+6) goto err;
181 if (us_recvall(p,b,4,0)!=4) goto err;
182 if (memcmp(b,"\x05\x00\x00",3)) goto err;
183 if (us_recvall(p,b,v+2,0)!=v+2) goto err;
184
185 /* return proxy socket */
186 close(sock);
187 fcntl(p, F_SETFL, f);
188 fcntl(p, F_DUPFD, sock);
189
190 return 0;
191
192 err:
193 close(p);
194 return -1;
195 }
|