Exemplo n.º 1
0
def do_iptables_nat(port, dnsport, family, subnets, udp):
    # only ipv4 supported with NAT
    if family != socket.AF_INET:
        raise Exception(
            'Address family "%s" unsupported by nat method'
            % family_to_string(family))
    if udp:
        raise Exception("UDP not supported by nat method")

    table = "nat"

    def ipt(*args):
        return _ipt(family, table, *args)

    def ipt_ttl(*args):
        return _ipt_ttl(family, table, *args)

    chain = 'sshuttle-%s' % port

    # basic cleanup/setup of chains
    if ipt_chain_exists(family, table, chain):
        nonfatal(ipt, '-D', 'OUTPUT', '-j', chain)
        nonfatal(ipt, '-D', 'PREROUTING', '-j', chain)
        nonfatal(ipt, '-F', chain)
        ipt('-X', chain)

    if subnets or dnsport:
        ipt('-N', chain)
        ipt('-F', chain)
        ipt('-I', 'OUTPUT', '1', '-j', chain)
        ipt('-I', 'PREROUTING', '1', '-j', chain)

    if subnets:
        # create new subnet entries.  Note that we're sorting in a very
        # particular order: we need to go from most-specific (largest swidth)
        # to least-specific, and at any given level of specificity, we want
        # excludes to come first.  That's why the columns are in such a non-
        # intuitive order.
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipt('-A', chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-p', 'tcp')
            else:
                ipt_ttl('-A', chain, '-j', 'REDIRECT',
                        '--dest', '%s/%s' % (snet, swidth),
                        '-p', 'tcp',
                        '--to-ports', str(port))

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            ipt_ttl('-A', chain, '-j', 'REDIRECT',
                    '--dest', '%s/32' % ip,
                    '-p', 'udp',
                    '--dport', '53',
                    '--to-ports', str(dnsport))
Exemplo n.º 2
0
def do_iptables_nat(port, dnsport, family, subnets, udp):
    # only ipv4 supported with NAT
    if family != socket.AF_INET:
        raise Exception(
            'Address family "%s" unsupported by nat method'
            % family_to_string(family))
    if udp:
        raise Exception("UDP not supported by nat method")

    table = "nat"

    def ipt(*args):
        return _ipt(family, table, *args)

    def ipt_ttl(*args):
        return _ipt_ttl(family, table, *args)

    chain = 'sshuttle-%s' % port

    # basic cleanup/setup of chains
    if ipt_chain_exists(family, table, chain):
        nonfatal(ipt, '-D', 'OUTPUT', '-j', chain)
        nonfatal(ipt, '-D', 'PREROUTING', '-j', chain)
        nonfatal(ipt, '-F', chain)
        ipt('-X', chain)

    if subnets or dnsport:
        ipt('-N', chain)
        ipt('-F', chain)
        ipt('-I', 'OUTPUT', '1', '-j', chain)
        ipt('-I', 'PREROUTING', '1', '-j', chain)

    if subnets:
        # create new subnet entries.  Note that we're sorting in a very
        # particular order: we need to go from most-specific (largest swidth)
        # to least-specific, and at any given level of specificity, we want
        # excludes to come first.  That's why the columns are in such a non-
        # intuitive order.
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipt('-A', chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-p', 'tcp')
            else:
                ipt_ttl('-A', chain, '-j', 'REDIRECT',
                        '--dest', '%s/%s' % (snet, swidth),
                        '-p', 'tcp',
                        '--to-ports', str(port))

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            ipt_ttl('-A', chain, '-j', 'REDIRECT',
                    '--dest', '%s/32' % ip,
                    '-p', 'udp',
                    '--dport', '53',
                    '--to-ports', str(dnsport))
Exemplo n.º 3
0
def do_pf(port, dnsport, family, subnets, udp):
    global _pf_started_by_sshuttle
    tables = []
    translating_rules = []
    filtering_rules = []

    if subnets:
        includes = []
        # If a given subnet is both included and excluded, list the exclusion
        # first; the table will ignore the second, opposite definition
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: (s[1], s[2]), reverse=True):
            includes.append("%s%s/%s" %
                            ("!" if sexclude else "", snet, swidth))

        tables.append('table <forward_subnets> {%s}' % ','.join(includes))
        translating_rules.append(
            'rdr pass on lo0 proto tcp to <forward_subnets> -> 127.0.0.1 port %r'
            % port)
        filtering_rules.append(
            'pass out route-to lo0 inet proto tcp to <forward_subnets> keep state'
        )

        if dnsport:
            nslist = resolvconf_nameservers()
            tables.append('table <dns_servers> {%s}' %
                          ','.join([ns[1] for ns in nslist]))
            translating_rules.append(
                'rdr pass on lo0 proto udp to <dns_servers> port 53 -> 127.0.0.1 port %r'
                % dnsport)
            filtering_rules.append(
                'pass out route-to lo0 inet proto udp to <dns_servers> port 53 keep state'
            )

        rules = '\n'.join(tables + translating_rules + filtering_rules) + '\n'

        pf_status = pfctl('-s all')[0]
        if not '\nrdr-anchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_RDR, "sshuttle")
        if not '\nanchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_PASS, "sshuttle")

        pfctl('-a sshuttle -f /dev/stdin', rules)
        if sys.platform == "darwin":
            o = pfctl('-E')
            _pf_context['Xtoken'] = re.search(r'Token : (.+)', o[1]).group(1)
        elif 'INFO:\nStatus: Disabled' in pf_status:
            pfctl('-e')
            _pf_context['started_by_sshuttle'] = True
    else:
        pfctl('-a sshuttle -F all')
        if sys.platform == "darwin":
            pfctl('-X %s' % _pf_context['Xtoken'])
        elif _pf_context['started_by_sshuttle']:
            pfctl('-d')
Exemplo n.º 4
0
def do_pf(port, dnsport, family, subnets, udp):
    global _pf_started_by_sshuttle
    tables = []
    translating_rules = []
    filtering_rules = []

    if subnets:
        include_subnets = filter(lambda s:not s[2], sorted(subnets, reverse=True))
        if include_subnets:
            tables.append('table <include_subnets> {%s}' % ','.join(["%s/%s" % (n[3], n[1]) for n in include_subnets]))
            translating_rules.append('rdr pass on lo0 proto tcp to <include_subnets> -> 127.0.0.1 port %r' % port)
            filtering_rules.append('pass out route-to lo0 inet proto tcp to <include_subnets> keep state')

        exclude_subnets = filter(lambda s:s[2], sorted(subnets, reverse=True))
        if exclude_subnets:
            tables.append('table <exclude_subnets> {%s}' % ','.join(["%s/%s" % (n[3], n[1]) for n in exclude_subnets]))
            filtering_rules.append('pass out quick proto tcp from any to <exclude_subnets> keep state')

        if dnsport:
            nslist = resolvconf_nameservers()
            tables.append('table <dns_servers> {%s}' % ','.join([ns[1] for ns in nslist]))
            translating_rules.append('rdr pass on lo0 proto udp to <dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
            filtering_rules.append('pass out route-to lo0 inet proto udp to <dns_servers> port 53 keep state')

        rules = '\n'.join(tables + translating_rules + filtering_rules) + '\n'

        pf_status = pfctl('-s all')[0]
        if not '\nrdr-anchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_RDR, "sshuttle")        
        if not '\nanchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_PASS, "sshuttle")

        pfctl('-a sshuttle -f /dev/stdin', rules)
        if sys.platform == "darwin":
            o = pfctl('-E')
            _pf_context['Xtoken'] = re.search(r'Token : (.+)', o[1]).group(1)
        elif 'INFO:\nStatus: Disabled' in pf_status: 
            pfctl('-e')
            _pf_context['started_by_sshuttle'] = True
    else:
        pfctl('-a sshuttle -F all')
        if sys.platform == "darwin":
            pfctl('-X %s' % _pf_context['Xtoken'])
        elif _pf_context['started_by_sshuttle']:
            pfctl('-d')
Exemplo n.º 5
0
def do_pf(port, dnsport, family, subnets, udp):
    global _pf_started_by_sshuttle
    tables = []
    translating_rules = []
    filtering_rules = []

    if subnets:
        includes=[]
        # If a given subnet is both included and excluded, list the exclusion
        # first; the table will ignore the second, opposite definition
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: (s[1], s[2]), reverse=True):
                    includes.append("%s%s/%s" % ("!" if sexclude else "", snet, swidth))

        tables.append('table <forward_subnets> {%s}' % ','.join(includes))
        translating_rules.append('rdr pass on lo0 proto tcp to <forward_subnets> -> 127.0.0.1 port %r' % port)
        filtering_rules.append('pass out route-to lo0 inet proto tcp to <forward_subnets> keep state')

        if dnsport:
            nslist = resolvconf_nameservers()
            tables.append('table <dns_servers> {%s}' % ','.join([ns[1] for ns in nslist]))
            translating_rules.append('rdr pass on lo0 proto udp to <dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
            filtering_rules.append('pass out route-to lo0 inet proto udp to <dns_servers> port 53 keep state')

        rules = '\n'.join(tables + translating_rules + filtering_rules) + '\n'

        pf_status = pfctl('-s all')[0]
        if not '\nrdr-anchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_RDR, "sshuttle")        
        if not '\nanchor "sshuttle" all\n' in pf_status:
            pf_add_anchor_rule(PF_PASS, "sshuttle")

        pfctl('-a sshuttle -f /dev/stdin', rules)
        if sys.platform == "darwin":
            o = pfctl('-E')
            _pf_context['Xtoken'] = re.search(r'Token : (.+)', o[1]).group(1)
        elif 'INFO:\nStatus: Disabled' in pf_status: 
            pfctl('-e')
            _pf_context['started_by_sshuttle'] = True
    else:
        pfctl('-a sshuttle -F all')
        if sys.platform == "darwin":
            pfctl('-X %s' % _pf_context['Xtoken'])
        elif _pf_context['started_by_sshuttle']:
            pfctl('-d')
Exemplo n.º 6
0
def do_pf(port, dnsport, family, subnets, udp):
    exclude_set = []
    redirect_set = []
    ns_set = []

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in nslist:
            ns_set.append(ip)

    if subnets:
        for f, swidth, sexclude, snet in sorted(subnets, reverse=True):
            if sexclude:
                exclude_set.append("%s/%s" % (snet, swidth))
            else:
                redirect_set.append("%s/%s" % (snet, swidth))

        ruleset = [
            'packets = "proto tcp to {%s}"' % ','.join(redirect_set),
            'rdr pass on lo0 $packets -> 127.0.0.1 port %s' % port,
            'pass out route-to lo0 inet $packets keep state'
        ]

        if len(ns_set) > 0:
            ns_ruleset = [
                'dnspackets = "proto udp to {%s} port 53"' % ','.join(ns_set),
                'rdr pass on lo0 $dnspackets -> 127.0.0.1 port %s' % port,
                'pass out route-to lo0 inet $dnspackets keep state'
            ]
            ruleset = list(sum(zip(ruleset, ns_ruleset), ()))

        if len(exclude_set) > 0:
            ruleset.append('pass out quick proto tcp to {%s}' %
                           ','.join(exclude_set))

        f = open('/etc/sshuttle.pf.conf', 'w+')
        f.write('\n'.join(ruleset) + '\n')
        f.close()

        pfctl('-f', '/etc/sshuttle.pf.conf', '-E')
    else:
        pfctl('-d')
        pfctl('-F', 'all')
Exemplo n.º 7
0
def do_pf(port, dnsport, family, subnets, udp):
    exclude_set = []
    redirect_set = []
    ns_set = []

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in nslist:
            ns_set.append(ip)

    if subnets:
        for f, swidth, sexclude, snet in sorted(subnets, reverse=True):
            if sexclude:
                exclude_set.append("%s/%s" % (snet,swidth))
            else:
                redirect_set.append("%s/%s" % (snet,swidth))

        ruleset = [
            'packets = "proto tcp to {%s}"' % ','.join(redirect_set),
            'rdr pass on lo0 $packets -> 127.0.0.1 port %s' % port,
            'pass out route-to lo0 inet $packets keep state'
            ]

        if len(ns_set) > 0:
            ns_ruleset = [
                    'dnspackets = "proto udp to {%s} port 53"' % ','.join(ns_set),
                    'rdr pass on lo0 $dnspackets -> 127.0.0.1 port %s' % port,
                    'pass out route-to lo0 inet $dnspackets keep state'
                ]
            ruleset = list(sum(zip(ruleset, ns_ruleset), ()))

        if len(exclude_set) > 0:
            ruleset.append('pass out quick proto tcp to {%s}' % ','.join(exclude_set))

        f = open('/etc/sshuttle.pf.conf', 'w+')
        f.write('\n'.join(ruleset) + '\n')
        f.close()

        pfctl('-f', '/etc/sshuttle.pf.conf', '-E')
    else:
        pfctl('-d')
        pfctl('-F', 'all')
Exemplo n.º 8
0
def do_ipfw(port, dnsport, family, subnets, udp):
    # IPv6 not supported
    if family not in [socket.AF_INET, ]:
        raise Exception(
            'Address family "%s" unsupported by ipfw method'
            % family_to_string(family))
    if udp:
        raise Exception("UDP not supported by ipfw method")

    sport = str(port)
    xsport = str(port + 1)

    # cleanup any existing rules
    if ipfw_rule_exists(port):
        ipfw('delete', sport)

    while _changedctls:
        name = _changedctls.pop()
        oldval = _oldctls[name]
        _sysctl_set(name, oldval)

    if subnets or dnsport:
        sysctl_set('net.inet.ip.fw.enable', 1)
        changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
        if changed:
            log("\n"
                "        WARNING: ONE-TIME NETWORK DISRUPTION:\n"
                "        =====================================\n"
                "sshuttle has changed a MacOS kernel setting to work around\n"
                "a bug in MacOS 10.6.  This will cause your network to drop\n"
                "within 5-10 minutes unless you restart your network\n"
                "interface (change wireless networks or unplug/plug the\n"
                "ethernet port) NOW, then restart sshuttle.  The fix is\n"
                "permanent; you only have to do this once.\n\n")
            sys.exit(1)

        ipfw('add', sport, 'check-state', 'ip',
             'from', 'any', 'to', 'any')

    if subnets:
        # create new subnet entries
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipfw('add', sport, 'skipto', xsport,
                     'tcp',
                     'from', 'any', 'to', '%s/%s' % (snet, swidth))
            else:
                ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
                     'tcp',
                     'from', 'any', 'to', '%s/%s' % (snet, swidth),
                     'not', 'ipttl', '42', 'keep-state', 'setup')

    # This part is much crazier than it is on Linux, because MacOS (at least
    # 10.6, and probably other versions, and maybe FreeBSD too) doesn't
    # correctly fixup the dstip/dstport for UDP packets when it puts them
    # through a 'fwd' rule.  It also doesn't fixup the srcip/srcport in the
    # response packet.  In Linux iptables, all that happens magically for us,
    # so we just redirect the packets and relax.
    #
    # On MacOS, we have to fix the ports ourselves.  For that, we use a
    # 'divert' socket, which receives raw packets and lets us mangle them.
    #
    # Here's how it works.  Let's say the local DNS server is 1.1.1.1:53,
    # and the remote DNS server is 2.2.2.2:53, and the local transproxy port
    # is 10.0.0.1:12300, and a client machine is making a request from
    # 10.0.0.5:9999. We see a packet like this:
    #    10.0.0.5:9999 -> 1.1.1.1:53
    # Since the destip:port matches one of our local nameservers, it will
    # match a 'fwd' rule, thus grabbing it on the local machine.  However,
    # the local kernel will then see a packet addressed to *:53 and
    # not know what to do with it; there's nobody listening on port 53.  Thus,
    # we divert it, rewriting it into this:
    #    10.0.0.5:9999 -> 10.0.0.1:12300
    # This gets proxied out to the server, which sends it to 2.2.2.2:53,
    # and the answer comes back, and the proxy sends it back out like this:
    #    10.0.0.1:12300 -> 10.0.0.5:9999
    # But that's wrong!  The original machine expected an answer from
    # 1.1.1.1:53, so we have to divert the *answer* and rewrite it:
    #    1.1.1.1:53 -> 10.0.0.5:9999
    #
    # See?  Easy stuff.
    if dnsport:
        divertsock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
                                   IPPROTO_DIVERT)
        divertsock.bind(('0.0.0.0', port))  # IP field is ignored

        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            # relabel and then catch outgoing DNS requests
            ipfw('add', sport, 'divert', sport,
                 'udp',
                 'from', 'any', 'to', '%s/32' % ip, '53',
                 'not', 'ipttl', '42')
        # relabel DNS responses
        ipfw('add', sport, 'divert', sport,
             'udp',
             'from', 'any', str(dnsport), 'to', 'any',
             'not', 'ipttl', '42')

        def do_wait():
            while 1:
                r, w, x = select.select([sys.stdin, divertsock], [], [])
                if divertsock in r:
                    _handle_diversion(divertsock, dnsport)
                if sys.stdin in r:
                    return
    else:
        do_wait = None

    return do_wait
Exemplo n.º 9
0
def do_iptables_tproxy(port, dnsport, family, subnets, udp):
    if family not in [socket.AF_INET, socket.AF_INET6]:
        raise Exception(
            'Address family "%s" unsupported by tproxy method'
            % family_to_string(family))

    table = "mangle"

    def ipt(*args):
        return _ipt(family, table, *args)

    def ipt_ttl(*args):
        return _ipt_ttl(family, table, *args)

    mark_chain = 'sshuttle-m-%s' % port
    tproxy_chain = 'sshuttle-t-%s' % port
    divert_chain = 'sshuttle-d-%s' % port

    # basic cleanup/setup of chains
    if ipt_chain_exists(family, table, mark_chain):
        ipt('-D', 'OUTPUT', '-j', mark_chain)
        ipt('-F', mark_chain)
        ipt('-X', mark_chain)

    if ipt_chain_exists(family, table, tproxy_chain):
        ipt('-D', 'PREROUTING', '-j', tproxy_chain)
        ipt('-F', tproxy_chain)
        ipt('-X', tproxy_chain)

    if ipt_chain_exists(family, table, divert_chain):
        ipt('-F', divert_chain)
        ipt('-X', divert_chain)

    if subnets or dnsport:
        ipt('-N', mark_chain)
        ipt('-F', mark_chain)
        ipt('-N', divert_chain)
        ipt('-F', divert_chain)
        ipt('-N', tproxy_chain)
        ipt('-F', tproxy_chain)
        ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
        ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
        ipt('-A', divert_chain, '-j', 'MARK', '--set-mark', '1')
        ipt('-A', divert_chain, '-j', 'ACCEPT')
        ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
            '-m', 'tcp', '-p', 'tcp')
    if subnets and udp:
        ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
            '-m', 'udp', '-p', 'udp')

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
                '--dest', '%s/32' % ip,
                '-m', 'udp', '-p', 'udp', '--dport', '53')
            ipt('-A', tproxy_chain, '-j', 'TPROXY', '--tproxy-mark', '0x1/0x1',
                '--dest', '%s/32' % ip,
                '-m', 'udp', '-p', 'udp', '--dport', '53',
                '--on-port', str(dnsport))

    if subnets:
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipt('-A', mark_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
                ipt('-A', tproxy_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
            else:
                ipt('-A', mark_chain, '-j', 'MARK',
                    '--set-mark', '1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
                ipt('-A', tproxy_chain, '-j', 'TPROXY',
                    '--tproxy-mark', '0x1/0x1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp',
                    '--on-port', str(port))

            if sexclude and udp:
                ipt('-A', mark_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
                ipt('-A', tproxy_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
            elif udp:
                ipt('-A', mark_chain, '-j', 'MARK',
                    '--set-mark', '1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
                ipt('-A', tproxy_chain, '-j', 'TPROXY',
                    '--tproxy-mark', '0x1/0x1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp',
                    '--on-port', str(port))
Exemplo n.º 10
0
def do_ipfw(port, dnsport, family, subnets, udp):
    # IPv6 not supported
    if family not in [socket.AF_INET, ]:
        raise Exception(
            'Address family "%s" unsupported by ipfw method'
            % family_to_string(family))
    if udp:
        raise Exception("UDP not supported by ipfw method")

    sport = str(port)
    xsport = str(port + 1)

    # cleanup any existing rules
    if ipfw_rule_exists(port):
        ipfw('delete', sport)

    while _changedctls:
        name = _changedctls.pop()
        oldval = _oldctls[name]
        _sysctl_set(name, oldval)

    if subnets or dnsport:
        sysctl_set('net.inet.ip.fw.enable', 1)
        changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
        if changed:
            log("\n"
                "        WARNING: ONE-TIME NETWORK DISRUPTION:\n"
                "        =====================================\n"
                "sshuttle has changed a MacOS kernel setting to work around\n"
                "a bug in MacOS 10.6.  This will cause your network to drop\n"
                "within 5-10 minutes unless you restart your network\n"
                "interface (change wireless networks or unplug/plug the\n"
                "ethernet port) NOW, then restart sshuttle.  The fix is\n"
                "permanent; you only have to do this once.\n\n")
            sys.exit(1)

        ipfw('add', sport, 'check-state', 'ip',
             'from', 'any', 'to', 'any')

    if subnets:
        # create new subnet entries
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipfw('add', sport, 'skipto', xsport,
                     'tcp',
                     'from', 'any', 'to', '%s/%s' % (snet, swidth))
            else:
                ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
                     'tcp',
                     'from', 'any', 'to', '%s/%s' % (snet, swidth),
                     'not', 'ipttl', '42', 'keep-state', 'setup')

    # This part is much crazier than it is on Linux, because MacOS (at least
    # 10.6, and probably other versions, and maybe FreeBSD too) doesn't
    # correctly fixup the dstip/dstport for UDP packets when it puts them
    # through a 'fwd' rule.  It also doesn't fixup the srcip/srcport in the
    # response packet.  In Linux iptables, all that happens magically for us,
    # so we just redirect the packets and relax.
    #
    # On MacOS, we have to fix the ports ourselves.  For that, we use a
    # 'divert' socket, which receives raw packets and lets us mangle them.
    #
    # Here's how it works.  Let's say the local DNS server is 1.1.1.1:53,
    # and the remote DNS server is 2.2.2.2:53, and the local transproxy port
    # is 10.0.0.1:12300, and a client machine is making a request from
    # 10.0.0.5:9999. We see a packet like this:
    #    10.0.0.5:9999 -> 1.1.1.1:53
    # Since the destip:port matches one of our local nameservers, it will
    # match a 'fwd' rule, thus grabbing it on the local machine.  However,
    # the local kernel will then see a packet addressed to *:53 and
    # not know what to do with it; there's nobody listening on port 53.  Thus,
    # we divert it, rewriting it into this:
    #    10.0.0.5:9999 -> 10.0.0.1:12300
    # This gets proxied out to the server, which sends it to 2.2.2.2:53,
    # and the answer comes back, and the proxy sends it back out like this:
    #    10.0.0.1:12300 -> 10.0.0.5:9999
    # But that's wrong!  The original machine expected an answer from
    # 1.1.1.1:53, so we have to divert the *answer* and rewrite it:
    #    1.1.1.1:53 -> 10.0.0.5:9999
    #
    # See?  Easy stuff.
    if dnsport:
        divertsock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
                                   IPPROTO_DIVERT)
        divertsock.bind(('0.0.0.0', port))  # IP field is ignored

        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            # relabel and then catch outgoing DNS requests
            ipfw('add', sport, 'divert', sport,
                 'udp',
                 'from', 'any', 'to', '%s/32' % ip, '53',
                 'not', 'ipttl', '42')
        # relabel DNS responses
        ipfw('add', sport, 'divert', sport,
             'udp',
             'from', 'any', str(dnsport), 'to', 'any',
             'not', 'ipttl', '42')

        def do_wait():
            while 1:
                r, w, x = select.select([sys.stdin, divertsock], [], [])
                if divertsock in r:
                    _handle_diversion(divertsock, dnsport)
                if sys.stdin in r:
                    return
    else:
        do_wait = None

    return do_wait
Exemplo n.º 11
0
def do_iptables_tproxy(port, dnsport, family, subnets, udp):
    if family not in [socket.AF_INET, socket.AF_INET6]:
        raise Exception(
            'Address family "%s" unsupported by tproxy method'
            % family_to_string(family))

    table = "mangle"

    def ipt(*args):
        return _ipt(family, table, *args)

    def ipt_ttl(*args):
        return _ipt_ttl(family, table, *args)

    mark_chain = 'sshuttle-m-%s' % port
    tproxy_chain = 'sshuttle-t-%s' % port
    divert_chain = 'sshuttle-d-%s' % port

    # basic cleanup/setup of chains
    if ipt_chain_exists(family, table, mark_chain):
        ipt('-D', 'OUTPUT', '-j', mark_chain)
        ipt('-F', mark_chain)
        ipt('-X', mark_chain)

    if ipt_chain_exists(family, table, tproxy_chain):
        ipt('-D', 'PREROUTING', '-j', tproxy_chain)
        ipt('-F', tproxy_chain)
        ipt('-X', tproxy_chain)

    if ipt_chain_exists(family, table, divert_chain):
        ipt('-F', divert_chain)
        ipt('-X', divert_chain)

    if subnets or dnsport:
        ipt('-N', mark_chain)
        ipt('-F', mark_chain)
        ipt('-N', divert_chain)
        ipt('-F', divert_chain)
        ipt('-N', tproxy_chain)
        ipt('-F', tproxy_chain)
        ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
        ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
        ipt('-A', divert_chain, '-j', 'MARK', '--set-mark', '1')
        ipt('-A', divert_chain, '-j', 'ACCEPT')
        ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
            '-m', 'tcp', '-p', 'tcp')
    if subnets and udp:
        ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
            '-m', 'udp', '-p', 'udp')

    if dnsport:
        nslist = resolvconf_nameservers()
        for f, ip in filter(lambda i: i[0] == family, nslist):
            ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
                '--dest', '%s/32' % ip,
                '-m', 'udp', '-p', 'udp', '--dport', '53')
            ipt('-A', tproxy_chain, '-j', 'TPROXY', '--tproxy-mark', '0x1/0x1',
                '--dest', '%s/32' % ip,
                '-m', 'udp', '-p', 'udp', '--dport', '53',
                '--on-port', str(dnsport))

    if subnets:
        for f, swidth, sexclude, snet \
                in sorted(subnets, key=lambda s: s[1], reverse=True):
            if sexclude:
                ipt('-A', mark_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
                ipt('-A', tproxy_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
            else:
                ipt('-A', mark_chain, '-j', 'MARK',
                    '--set-mark', '1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp')
                ipt('-A', tproxy_chain, '-j', 'TPROXY',
                    '--tproxy-mark', '0x1/0x1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'tcp', '-p', 'tcp',
                    '--on-port', str(port))

            if sexclude and udp:
                ipt('-A', mark_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
                ipt('-A', tproxy_chain, '-j', 'RETURN',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
            elif udp:
                ipt('-A', mark_chain, '-j', 'MARK',
                    '--set-mark', '1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp')
                ipt('-A', tproxy_chain, '-j', 'TPROXY',
                    '--tproxy-mark', '0x1/0x1',
                    '--dest', '%s/%s' % (snet, swidth),
                    '-m', 'udp', '-p', 'udp',
                    '--on-port', str(port))