8
8
from sshuttle .methods import get_auto_method , get_method
9
9
10
10
hostmap = {}
11
+ HOSTSFILE = '/etc/hosts'
11
12
12
13
13
14
def rewrite_etc_hosts (port ):
14
- HOSTSFILE = '/etc/hosts'
15
15
BAKFILE = '%s.sbak' % HOSTSFILE
16
16
APPEND = '# sshuttle-firewall-%d AUTOCREATED' % port
17
17
old_content = ''
@@ -51,6 +51,30 @@ def restore_etc_hosts(port):
51
51
rewrite_etc_hosts (port )
52
52
53
53
54
+ # Isolate function that needs to be replaced for tests
55
+ def setup_daemon ():
56
+ if os .getuid () != 0 :
57
+ raise Fatal ('you must be root (or enable su/sudo) to set the firewall' )
58
+
59
+ # don't disappear if our controlling terminal or stdout/stderr
60
+ # disappears; we still have to clean up.
61
+ signal .signal (signal .SIGHUP , signal .SIG_IGN )
62
+ signal .signal (signal .SIGPIPE , signal .SIG_IGN )
63
+ signal .signal (signal .SIGTERM , signal .SIG_IGN )
64
+ signal .signal (signal .SIGINT , signal .SIG_IGN )
65
+
66
+ # ctrl-c shouldn't be passed along to me. When the main sshuttle dies,
67
+ # I'll die automatically.
68
+ os .setsid ()
69
+
70
+ # because of limitations of the 'su' command, the *real* stdin/stdout
71
+ # are both attached to stdout initially. Clone stdout into stdin so we
72
+ # can read from it.
73
+ os .dup2 (1 , 0 )
74
+
75
+ return sys .stdin , sys .stdout
76
+
77
+
54
78
# This is some voodoo for setting up the kernel's transparent
55
79
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
56
80
# otherwise we delete it, then make them from scratch.
@@ -60,50 +84,33 @@ def restore_etc_hosts(port):
60
84
# supercede it in the transproxy list, at least, so the leftover rules
61
85
# are hopefully harmless.
62
86
def main (method_name , syslog ):
63
- if os .getuid () != 0 :
64
- raise Fatal ('you must be root (or enable su/sudo) to set the firewall' )
87
+ stdin , stdout = setup_daemon ()
65
88
66
89
if method_name == "auto" :
67
90
method = get_auto_method ()
68
91
else :
69
92
method = get_method (method_name )
70
93
71
- # because of limitations of the 'su' command, the *real* stdin/stdout
72
- # are both attached to stdout initially. Clone stdout into stdin so we
73
- # can read from it.
74
- os .dup2 (1 , 0 )
75
-
76
94
if syslog :
77
95
ssyslog .start_syslog ()
78
96
ssyslog .stderr_to_syslog ()
79
97
80
- debug1 ('firewall manager ready method name %s.\n ' % method .name )
81
- sys .stdout .write ('READY %s\n ' % method .name )
82
- sys .stdout .flush ()
83
-
84
- # don't disappear if our controlling terminal or stdout/stderr
85
- # disappears; we still have to clean up.
86
- signal .signal (signal .SIGHUP , signal .SIG_IGN )
87
- signal .signal (signal .SIGPIPE , signal .SIG_IGN )
88
- signal .signal (signal .SIGTERM , signal .SIG_IGN )
89
- signal .signal (signal .SIGINT , signal .SIG_IGN )
90
-
91
- # ctrl-c shouldn't be passed along to me. When the main sshuttle dies,
92
- # I'll die automatically.
93
- os .setsid ()
98
+ debug1 ('firewall manager ready method name %s.\n ' % method_name )
99
+ stdout .write ('READY %s\n ' % method_name )
100
+ stdout .flush ()
94
101
95
102
# we wait until we get some input before creating the rules. That way,
96
103
# sshuttle can launch us as early as possible (and get sudo password
97
104
# authentication as early in the startup process as possible).
98
- line = sys . stdin .readline (128 )
105
+ line = stdin .readline (128 )
99
106
if not line :
100
107
return # parent died; nothing to do
101
108
102
109
subnets = []
103
110
if line != 'ROUTES\n ' :
104
111
raise Fatal ('firewall: expected ROUTES but got %r' % line )
105
112
while 1 :
106
- line = sys . stdin .readline (128 )
113
+ line = stdin .readline (128 )
107
114
if not line :
108
115
raise Fatal ('firewall: expected route but got %r' % line )
109
116
elif line .startswith ("NSLIST\n " ):
@@ -119,7 +126,7 @@ def main(method_name, syslog):
119
126
if line != 'NSLIST\n ' :
120
127
raise Fatal ('firewall: expected NSLIST but got %r' % line )
121
128
while 1 :
122
- line = sys . stdin .readline (128 )
129
+ line = stdin .readline (128 )
123
130
if not line :
124
131
raise Fatal ('firewall: expected nslist but got %r' % line )
125
132
elif line .startswith ("PORTS " ):
@@ -155,7 +162,7 @@ def main(method_name, syslog):
155
162
debug2 ('Got ports: %d,%d,%d,%d\n '
156
163
% (port_v6 , port_v4 , dnsport_v6 , dnsport_v4 ))
157
164
158
- line = sys . stdin .readline (128 )
165
+ line = stdin .readline (128 )
159
166
if not line :
160
167
raise Fatal ('firewall: expected GO but got %r' % line )
161
168
elif not line .startswith ("GO " ):
@@ -169,26 +176,28 @@ def main(method_name, syslog):
169
176
do_wait = None
170
177
debug1 ('firewall manager: starting transproxy.\n ' )
171
178
179
+ nslist_v6 = [i for i in nslist if i [0 ] == socket .AF_INET6 ]
172
180
subnets_v6 = [i for i in subnets if i [0 ] == socket .AF_INET6 ]
173
181
if port_v6 > 0 :
174
182
do_wait = method .setup_firewall (
175
- port_v6 , dnsport_v6 , nslist ,
183
+ port_v6 , dnsport_v6 , nslist_v6 ,
176
184
socket .AF_INET6 , subnets_v6 , udp )
177
185
elif len (subnets_v6 ) > 0 :
178
186
debug1 ("IPv6 subnets defined but IPv6 disabled\n " )
179
187
188
+ nslist_v4 = [i for i in nslist if i [0 ] == socket .AF_INET ]
180
189
subnets_v4 = [i for i in subnets if i [0 ] == socket .AF_INET ]
181
190
if port_v4 > 0 :
182
191
do_wait = method .setup_firewall (
183
- port_v4 , dnsport_v4 , nslist ,
192
+ port_v4 , dnsport_v4 , nslist_v4 ,
184
193
socket .AF_INET , subnets_v4 , udp )
185
194
elif len (subnets_v4 ) > 0 :
186
195
debug1 ('IPv4 subnets defined but IPv4 disabled\n ' )
187
196
188
- sys . stdout .write ('STARTED\n ' )
197
+ stdout .write ('STARTED\n ' )
189
198
190
199
try :
191
- sys . stdout .flush ()
200
+ stdout .flush ()
192
201
except IOError :
193
202
# the parent process died for some reason; he's surely been loud
194
203
# enough, so no reason to report another error
@@ -198,9 +207,9 @@ def main(method_name, syslog):
198
207
# to stay running so that we don't need a *second* password
199
208
# authentication at shutdown time - that cleanup is important!
200
209
while 1 :
201
- if do_wait :
210
+ if do_wait is not None :
202
211
do_wait ()
203
- line = sys . stdin .readline (128 )
212
+ line = stdin .readline (128 )
204
213
if line .startswith ('HOST ' ):
205
214
(name , ip ) = line [5 :].strip ().split (',' , 1 )
206
215
hostmap [name ] = ip
0 commit comments