1 /*
2 
3   Copyright (c) 2018 Joseph deBlaquiere
4 
5   Permission is hereby granted, free of charge, to any person obtaining a copy
6   of this software and associated documentation files (the "Software"),
7   to deal in the Software without restriction, including without limitation
8   the rights to use, copy, modify, merge, publish, distribute, sublicense,
9   and/or sell copies of the Software, and to permit persons to whom
10   the Software is furnished to do so, subject to the following conditions:
11 
12   The above copyright notice and this permission notice shall be included
13   in all copies or substantial portions of the Software.
14 
15   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21   IN THE SOFTWARE.
22 
23 */
24 
25 #include "assert.h"
26 #include "../libdill.h"
27 #include <stdio.h>
28 #include <string.h>
29 
30 static char *auth_user = NULL;
31 static char *auth_pass = NULL;
32 
auth_fn(const char * user,const char * pass)33 int auth_fn(const char *user, const char* pass) {
34     if(auth_user) {
35         if(!user) return 0;
36         if(strcmp(user, auth_user) != 0) return 0;
37     }
38     if(auth_pass) {
39         if(!pass) return 0;
40         if(strcmp(pass, auth_pass) != 0) return 0;
41     }
42     return 1;
43 }
44 
45 // forwards input to output. signal and return on error (connection closed)
forward(int in_s,int out_s,int ch)46 coroutine void forward(int in_s, int out_s, int ch) {
47     while (1) {
48         uint8_t d[1];
49         if(brecv(in_s, d, 1, -1)) break;
50         if(bsend(out_s, d, 1, -1)) break;
51     }
52     chdone(ch);
53     return;
54 }
55 
do_proxy(int s)56 coroutine void do_proxy(int s) {
57     if((!auth_user) || (!auth_pass)){
58         if(socks5_proxy_auth(s, NULL, -1)) goto in_close;
59     } else {
60         if(socks5_proxy_auth(s, auth_fn, -1)) goto in_close;
61     }
62 
63     struct ipaddr addr;
64     int cmd = socks5_proxy_recvcommand(s, &addr, -1);
65     if(cmd != SOCKS5_CONNECT) {
66         socks5_proxy_sendreply(s, SOCKS5_COMMAND_NOT_SUPPORTED, &addr, -1);
67         goto in_close;
68     }
69 
70     int s_rem = tcp_connect(&addr, -1);
71     if(s_rem < 0) goto in_close;
72 
73     if(ipaddr_remote(&addr, "0.0.0.0", 0, IPADDR_IPV4, -1)) goto both_close;
74 
75     if(socks5_proxy_sendreply(s, SOCKS5_SUCCESS, &addr, -1)) goto both_close;
76 
77     // channels for outboud, inbound to signal done (to close connection)
78     int och[2], ich[2];
79     if(chmake(och)) goto both_close;
80     if(chmake(ich)) goto both_close;
81 
82     // start coroutines to handle inbound and outbound forwarding
83     int ob = go(forward(s, s_rem, och[1]));
84     if(ob < 0) goto both_close;
85     int ib = go(forward(s_rem, s, ich[1]));
86     if(ib < 0) goto both_close;
87 
88     // wait for message in channel - one side closed, tcp_done the other
89     struct chclause cc[] = {{CHRECV, och[0], &cmd, 1},
90                             {CHRECV, ich[0], &cmd, 1}};
91     switch(choose(cc, 2, -1)) {
92         case 0:
93             if(bundle_wait(ob, -1)) break;
94             tcp_done(s_rem, -1);
95             bundle_wait(ib, -1);
96             break;
97         case 1:
98             if(bundle_wait(ib, -1)) break;
99             tcp_done(s, -1);
100             bundle_wait(ob, -1);
101             break;
102         case -1:
103             break;
104         default:
105             // unexpected answer
106             assert(0);
107     }
108 both_close:
109     tcp_close(s_rem, -1);
110 in_close:
111     tcp_close(s, -1);
112     return;
113 }
114 
main(int argc,char ** argv)115 int main(int argc, char** argv) {
116     if (argc >=3) {
117         // set up auth
118         auth_user = argv[1];
119         auth_pass = argv[2];
120     }
121     int workers = bundle();
122     struct ipaddr addr;
123     int rc = ipaddr_local(&addr, NULL, 1080, 0);
124     assert(rc == 0);
125     int ls = tcp_listen(&addr, 10);
126     assert(ls >= 0);
127     printf("SOCKS5 proxy listening on :1080\n");
128     while(1) {
129         struct ipaddr caddr;
130         int s = tcp_accept(ls, &caddr, -1);
131         assert(s >= 0);
132         rc = bundle_go(workers, do_proxy(s));
133         assert(rc == 0);
134     }
135 }
136