Skip to content

Commit bedf89e

Browse files
committed
Merge branch 'pull/31'
* Merging pull request apenwarr#31.
2 parents 284ebd2 + cdc63a6 commit bedf89e

File tree

4 files changed

+72
-24
lines changed

4 files changed

+72
-24
lines changed

Documentation/sshuttle.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ entire subnet to the VPN.
3636
single IP address), 1.2.3.4/32 (equivalent to 1.2.3.4),
3737
1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0
3838
netmask), and 0/0 ('just route everything through the
39-
VPN').
39+
VPN'). In addition, it is also possible to filter
40+
additionally on the tcp port to include/exclude.
41+
This is done by adding :port at the end of your netmask.
42+
Note that `:port` is also valid. In this case, the netmask
43+
will be defaulted to 0/0. Valid examples are `:80`,
44+
`0:80`, `1.2.3.4:80` and `1.2.3.4/24:80`.
4045

4146
-l, --listen=*[ip:]port*
4247
: use this ip address and port number as the transparent

client.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,10 @@ def check(self):
160160

161161
def start(self):
162162
self.pfile.write('ROUTES\n')
163-
for (ip,width) in self.subnets_include+self.auto_nets:
164-
self.pfile.write('%d,0,%s\n' % (width, ip))
165-
for (ip,width) in self.subnets_exclude:
166-
self.pfile.write('%d,1,%s\n' % (width, ip))
163+
for (ip,width,port) in self.subnets_include+self.auto_nets:
164+
self.pfile.write('%d,%d,0,%s\n' % (width, port, ip))
165+
for (ip,width,port) in self.subnets_exclude:
166+
self.pfile.write('%d,%d,1,%s\n' % (width, port, ip))
167167
self.pfile.write('GO\n')
168168
self.pfile.flush()
169169
line = self.pfile.readline()

firewall.py

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# vim: set tabstop=4 expandtab :
2+
13
import re, errno, socket, select, signal, struct
24
import compat.ssubprocess as ssubprocess
35
import helpers, ssyslog
@@ -106,17 +108,36 @@ def do_iptables(port, dnsport, route_username, excludedports, subnets):
106108
# to least-specific, and at any given level of specificity, we want
107109
# excludes to come first. That's why the columns are in such a non-
108110
# intuitive order.
109-
for swidth,sexclude,snet in sorted(subnets, reverse=True):
111+
for swidth,sport,sexclude,snet in sorted(subnets, reverse=True):
110112
if sexclude:
111-
ipt('-A', chain, '-j', 'RETURN',
112-
'--dest', '%s/%s' % (snet,swidth),
113-
'-p', 'tcp')
113+
if sport > 0:
114+
ipt('-A', chain, '-j', 'RETURN',
115+
'--dest', '%s/%s' % (snet,swidth),
116+
'-m', 'tcp',
117+
'--dport', '%d' % sport,
118+
'-p', 'tcp')
119+
else:
120+
ipt('-A', chain, '-j', 'RETURN',
121+
'--dest', '%s/%s' % (snet,swidth),
122+
'-p', 'tcp')
114123
else:
115-
ipt_ttl('-A', chain, '-j', 'REDIRECT',
124+
if sport > 0:
125+
ipt_ttl('-A', chain, '-j', 'REDIRECT',
116126
'--dest', '%s/%s' % (snet,swidth),
127+
'-m', 'tcp',
128+
'--dport', '%d' % sport,
117129
'-p', 'tcp',
130+
<<<<<<< HEAD
118131
'--to-ports', str(port),
119132
*eportsargv)
133+
=======
134+
'--to-ports', str(port))
135+
else:
136+
ipt_ttl('-A', chain, '-j', 'REDIRECT',
137+
'--dest', '%s/%s' % (snet,swidth),
138+
'-p', 'tcp',
139+
'--to-ports', str(port))
140+
>>>>>>> pull/31
120141

121142
if dnsport:
122143
for ip in nslist:
@@ -327,16 +348,29 @@ def do_ipfw(port, dnsport, route_username, excludedports, subnets):
327348

328349
if subnets:
329350
# create new subnet entries
330-
for swidth,sexclude,snet in sorted(subnets, reverse=True):
351+
for swidth,dport,sexclude,snet in sorted(subnets, reverse=True):
331352
if sexclude:
332-
ipfw('add', sport, 'skipto', xsport,
333-
'tcp',
334-
'from', 'any', 'to', '%s/%s' % (snet,swidth))
353+
if dport > 0:
354+
ipfw('add', sport, 'skipto', xsport,
355+
'tcp',
356+
'from', 'any', 'to', '%s/%s' % (snet,swidth),
357+
'%d' % dport)
358+
else:
359+
ipfw('add', sport, 'skipto', xsport,
360+
'tcp',
361+
'from', 'any', 'to', '%s/%s' % (snet,swidth))
335362
else:
336-
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
337-
'tcp',
338-
'from', 'any', 'to', '%s/%s' % (snet,swidth),
339-
'not', 'ipttl', '42', 'keep-state', 'setup')
363+
if dport > 0:
364+
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
365+
'tcp',
366+
'from', 'any', 'to', '%s/%s' % (snet,swidth),
367+
'%d' % dport,
368+
'not', 'ipttl', '42', 'keep-state', 'setup')
369+
else:
370+
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
371+
'tcp',
372+
'from', 'any', 'to', '%s/%s' % (snet,swidth),
373+
'not', 'ipttl', '42', 'keep-state', 'setup')
340374

341375
# This part is much crazier than it is on Linux, because MacOS (at least
342376
# 10.6, and probably other versions, and maybe FreeBSD too) doesn't
@@ -529,10 +563,10 @@ def main(port, dnsport, syslog, route_username, excludedports):
529563
elif line == 'GO\n':
530564
break
531565
try:
532-
(width,exclude,ip) = line.strip().split(',', 2)
566+
(width,dport,exclude,ip) = line.strip().split(',', 3)
533567
except:
534568
raise Fatal('firewall: expected route or GO but got %r' % line)
535-
subnets.append((int(width), bool(int(exclude)), ip))
569+
subnets.append((int(width), int(dport), bool(int(exclude)), ip))
536570

537571
try:
538572
if line:

main.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# vim: set tabstop=4 expandtab :
12
import sys, os, re
23
import helpers, options, client, server, firewall, hostwatch
34
import compat.ssubprocess as ssubprocess
@@ -9,11 +10,16 @@
910
def parse_subnets(subnets_str):
1011
subnets = []
1112
for s in subnets_str:
12-
m = re.match(r'(\d+)(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?$', s)
13+
m = re.match(r'(\d+)?(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?(:\d+)?$', s)
1314
if not m:
1415
raise Fatal('%r is not a valid IP subnet format' % s)
15-
(a,b,c,d,width) = m.groups()
16+
(a,b,c,d,width,port) = m.groups()
1617
(a,b,c,d) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0))
18+
if port == None:
19+
port = 0
20+
else:
21+
port = int(re.sub('^:','',port))
22+
1723
if width == None:
1824
width = 32
1925
else:
@@ -22,7 +28,10 @@ def parse_subnets(subnets_str):
2228
raise Fatal('%d.%d.%d.%d has numbers > 255' % (a,b,c,d))
2329
if width > 32:
2430
raise Fatal('*/%d is greater than the maximum of 32' % width)
25-
subnets.append(('%d.%d.%d.%d' % (a,b,c,d), width))
31+
if port > 65535:
32+
raise Fatal('*:%d is greater than the maximum of 65535' % port)
33+
subnets.append(('%d.%d.%d.%d' % (a,b,c,d), width, port))
34+
2635
return subnets
2736

2837

@@ -57,7 +66,7 @@ def parse_ipport(s):
5766
dns-hosts= capture DNS requests to these servers and forward (comma-separated)
5867
python= path to python interpreter on the remote server
5968
r,remote= ssh hostname (and optional username) of remote sshuttle server
60-
x,exclude= exclude this subnet (can be used more than once)
69+
x,exclude= exclude this subnet and/or port (can be used more than once)
6170
exclude-from= exclude the subnets in a file (whitespace separated)
6271
v,verbose increase debug message verbosity
6372
e,ssh-cmd= the command to use to connect to the remote [ssh]

0 commit comments

Comments
 (0)