First draft of OSX transparent proxy mode.

This commit is contained in:
Aldo Cortesi 2013-01-01 11:13:56 +13:00
parent 5347cb9c26
commit e2dc7ba09d
4 changed files with 47 additions and 97 deletions

View File

@ -1,103 +1,20 @@
import socket, ctypes
# Python socket module does not have this constant
DIOCNATLOOK = 23
PFDEV = "/dev/pf"
class PF_STATE_XPORT(ctypes.Union):
"""
union pf_state_xport {
u_int16_t port;
u_int16_t call_id;
u_int32_t spi;
};
"""
_fields_ = [
("port", ctypes.c_uint),
("call_id", ctypes.c_uint),
("spi", ctypes.c_ulong),
]
class PF_ADDR(ctypes.Union):
"""
struct pf_addr {
union {
struct in_addr v4;
struct in6_addr v6;
u_int8_t addr8[16];
u_int16_t addr16[8];
u_int32_t addr32[4];
} pfa;
}
"""
_fields_ = [
("addr8", ctypes.c_byte * 2),
("addr16", ctypes.c_byte * 4),
("addr32", ctypes.c_byte * 8),
]
class PFIOC_NATLOOK(ctypes.Structure):
"""
struct pfioc_natlook {
struct pf_addr saddr;
struct pf_addr daddr;
struct pf_addr rsaddr;
struct pf_addr rdaddr;
#ifndef NO_APPLE_EXTENSIONS
union pf_state_xport sxport;
union pf_state_xport dxport;
union pf_state_xport rsxport;
union pf_state_xport rdxport;
sa_family_t af;
u_int8_t proto;
u_int8_t proto_variant;
u_int8_t direction;
#else
u_int16_t sport;
u_int16_t dport;
u_int16_t rsport;
u_int16_t rdport;
sa_family_t af;
u_int8_t proto;
u_int8_t direction;
#endif
};
"""
_fields_ = [
("saddr", PF_ADDR),
("daddr", PF_ADDR),
("rsaddr", PF_ADDR),
("rdaddr", PF_ADDR),
("sxport", PF_STATE_XPORT),
("dxport", PF_STATE_XPORT),
("rsxport", PF_STATE_XPORT),
("rdxport", PF_STATE_XPORT),
("af", ctypes.c_uint),
("proto", ctypes.c_ushort),
("proto_variant", ctypes.c_ushort),
("direction", ctypes.c_ushort),
]
import subprocess
import pf
"""
Doing this the "right" way by using DIOCNATLOOK on the pf device turns out
to be a pain. Apple has made a number of modifications to the data
structures returned, and compiling userspace tools to test and work with
this turns out to be a pain in the ass. Parsing pfctl output is short,
simple, and works.
"""
class Resolver:
STATECMD = ("sudo", "-n", "/sbin/pfctl", "-s", "state")
def __init__(self):
self.pfdev = open(PFDEV, "r")
pass
def original_addr(self, csock):
"""
The following sttruct defintions are plucked from the current XNU source, found here:
http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/bsd/net/pfvar.h
union pf_state_xport {
u_int16_t port;
u_int16_t call_id;
u_int32_t spi;
};
"""
pass
peer = csock.getpeername()
stxt = subprocess.check_output(self.STATECMD, stderr=subprocess.STDOUT)
return pf.lookup(peer[0], peer[1], stxt)

16
libmproxy/platform/pf.py Normal file
View File

@ -0,0 +1,16 @@
def lookup(address, port, s):
"""
Parse the pfctl state output s, to look up the destination host
matching the client (address, port).
Returns an (address, port) tuple, or None.
"""
spec = "%s:%s"%(address, port)
for i in s.split("\n"):
if "ESTABLISHED:ESTABLISHED" in i and spec in i:
s = i.split()
if len(s) > 4:
s = s[4].split(":")
if len(s) == 2:
return s[0], int(s[1])

4
test/data/pf01 Normal file
View File

@ -0,0 +1,4 @@
No ALTQ support in kernel
ALTQ related functions disabled
ALL tcp 127.0.0.1:8080 <- 5.5.5.6:80 <- 192.168.1.111:40001 FIN_WAIT_2:FIN_WAIT_2
ALL tcp 127.0.0.1:8080 <- 5.5.5.5:80 <- 192.168.1.111:40000 ESTABLISHED:ESTABLISHED

13
test/test_platform_pf.py Normal file
View File

@ -0,0 +1,13 @@
import tutils
from libmproxy.platform import pf
class TestLookup:
def test_simple(self):
p = tutils.test_data.path("data/pf01")
d = open(p).read()
assert pf.lookup("192.168.1.111", 40000, d) == ("5.5.5.5", 80)
assert not pf.lookup("192.168.1.112", 40000, d)
assert not pf.lookup("192.168.1.111", 40001, d)