diff --git a/docs/requirements.rst b/docs/requirements.rst index 49a2865..8278b31 100644 --- a/docs/requirements.rst +++ b/docs/requirements.rst @@ -40,11 +40,11 @@ Linux with TPROXY method Supports: * IPv4 TCP -* IPv4 UDP (requires ``recvmsg`` - see below) -* IPv6 DNS (requires ``recvmsg`` - see below) +* IPv4 UDP +* IPv6 DNS * IPv6 TCP -* IPv6 UDP (requires ``recvmsg`` - see below) -* IPv6 DNS (requires ``recvmsg`` - see below) +* IPv6 UDP +* IPv6 DNS MacOS / FreeBSD / OpenBSD / pfSense diff --git a/sshuttle/client.py b/sshuttle/client.py index 6fe9e53..f9bf04b 100644 --- a/sshuttle/client.py +++ b/sshuttle/client.py @@ -22,19 +22,7 @@ try: except ImportError: getpwnam = None -try: - # try getting recvmsg from python - import socket as pythonsocket - getattr(pythonsocket.socket, "recvmsg") - socket = pythonsocket -except AttributeError: - # try getting recvmsg from socket_ext library - try: - import socket_ext - getattr(socket_ext.socket, "recvmsg") - socket = socket_ext - except ImportError: - import socket +import socket _extra_fd = os.open(os.devnull, os.O_RDONLY) diff --git a/sshuttle/methods/ipfw.py b/sshuttle/methods/ipfw.py index 0a3c44e..8afef51 100644 --- a/sshuttle/methods/ipfw.py +++ b/sshuttle/methods/ipfw.py @@ -4,59 +4,26 @@ from sshuttle.methods import BaseMethod from sshuttle.helpers import log, debug1, debug2, debug3, \ Fatal, family_to_string, get_env, which -recvmsg = None -try: - # try getting recvmsg from python - import socket as pythonsocket - getattr(pythonsocket.socket, "recvmsg") - socket = pythonsocket - recvmsg = "python" -except AttributeError: - # try getting recvmsg from socket_ext library - try: - import socket_ext - getattr(socket_ext.socket, "recvmsg") - socket = socket_ext - recvmsg = "socket_ext" - except ImportError: - import socket +import socket IP_BINDANY = 24 IP_RECVDSTADDR = 7 SOL_IPV6 = 41 IPV6_RECVDSTADDR = 74 -if recvmsg == "python": - def recv_udp(listener, bufsize): - debug3('Accept UDP python using recvmsg.') - data, ancdata, _, srcip = listener.recvmsg(4096, - socket.CMSG_SPACE(4)) - dstip = None - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level == socket.SOL_IP and cmsg_type == IP_RECVDSTADDR: - port = 53 - ip = socket.inet_ntop(socket.AF_INET, cmsg_data[0:4]) - dstip = (ip, port) - break - return (srcip, dstip, data) -elif recvmsg == "socket_ext": - def recv_udp(listener, bufsize): - debug3('Accept UDP using socket_ext recvmsg.') - srcip, data, adata, _ = listener.recvmsg((bufsize,), - socket.CMSG_SPACE(4)) - dstip = None - for a in adata: - if a.cmsg_level == socket.SOL_IP and a.cmsg_type == IP_RECVDSTADDR: - port = 53 - ip = socket.inet_ntop(socket.AF_INET, a.cmsg_data[0:4]) - dstip = (ip, port) - break - return (srcip, dstip, data[0]) -else: - def recv_udp(listener, bufsize): - debug3('Accept UDP using recvfrom.') - data, srcip = listener.recvfrom(bufsize) - return (srcip, None, data) + +def recv_udp(listener, bufsize): + debug3('Accept UDP python using recvmsg.') + data, ancdata, _, srcip = listener.recvmsg(4096, + socket.CMSG_SPACE(4)) + dstip = None + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_IP and cmsg_type == IP_RECVDSTADDR: + port = 53 + ip = socket.inet_ntop(socket.AF_INET, cmsg_data[0:4]) + dstip = (ip, port) + break + return (srcip, dstip, data) def ipfw_rule_exists(n): diff --git a/sshuttle/methods/tproxy.py b/sshuttle/methods/tproxy.py index 6c8fab0..0bd15a1 100644 --- a/sshuttle/methods/tproxy.py +++ b/sshuttle/methods/tproxy.py @@ -5,22 +5,7 @@ from sshuttle.linux import ipt, ipt_chain_exists from sshuttle.methods import BaseMethod from sshuttle.helpers import debug1, debug2, debug3, Fatal, which -recvmsg = None -try: - # try getting recvmsg from python - import socket as pythonsocket - getattr(pythonsocket.socket, "recvmsg") - socket = pythonsocket - recvmsg = "python" -except AttributeError: - # try getting recvmsg from socket_ext library - try: - import socket_ext - getattr(socket_ext.socket, "recvmsg") - socket = socket_ext - recvmsg = "socket_ext" - except ImportError: - import socket +import socket IP_TRANSPARENT = 19 @@ -30,75 +15,37 @@ SOL_IPV6 = 41 IPV6_ORIGDSTADDR = 74 IPV6_RECVORIGDSTADDR = IPV6_ORIGDSTADDR -if recvmsg == "python": - def recv_udp(listener, bufsize): - debug3('Accept UDP python using recvmsg.') - data, ancdata, _, srcip = listener.recvmsg( - 4096, socket.CMSG_SPACE(24)) - dstip = None - family = None - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level == socket.SOL_IP and cmsg_type == IP_ORIGDSTADDR: - family, port = struct.unpack('=HH', cmsg_data[0:4]) - port = socket.htons(port) - if family == socket.AF_INET: - start = 4 - length = 4 - else: - raise Fatal("Unsupported socket type '%s'" % family) - ip = socket.inet_ntop(family, cmsg_data[start:start + length]) - dstip = (ip, port) - break - elif cmsg_level == SOL_IPV6 and cmsg_type == IPV6_ORIGDSTADDR: - family, port = struct.unpack('=HH', cmsg_data[0:4]) - port = socket.htons(port) - if family == socket.AF_INET6: - start = 8 - length = 16 - else: - raise Fatal("Unsupported socket type '%s'" % family) - ip = socket.inet_ntop(family, cmsg_data[start:start + length]) - dstip = (ip, port) - break - return (srcip, dstip, data) -elif recvmsg == "socket_ext": - def recv_udp(listener, bufsize): - debug3('Accept UDP using socket_ext recvmsg.') - srcip, data, adata, _ = listener.recvmsg( - (bufsize,), socket.CMSG_SPACE(24)) - dstip = None - family = None - for a in adata: - if a.cmsg_level == socket.SOL_IP and a.cmsg_type == IP_ORIGDSTADDR: - family, port = struct.unpack('=HH', a.cmsg_data[0:4]) - port = socket.htons(port) - if family == socket.AF_INET: - start = 4 - length = 4 - else: - raise Fatal("Unsupported socket type '%s'" % family) - ip = socket.inet_ntop( - family, a.cmsg_data[start:start + length]) - dstip = (ip, port) - break - elif a.cmsg_level == SOL_IPV6 and a.cmsg_type == IPV6_ORIGDSTADDR: - family, port = struct.unpack('=HH', a.cmsg_data[0:4]) - port = socket.htons(port) - if family == socket.AF_INET6: - start = 8 - length = 16 - else: - raise Fatal("Unsupported socket type '%s'" % family) - ip = socket.inet_ntop( - family, a.cmsg_data[start:start + length]) - dstip = (ip, port) - break - return (srcip, dstip, data[0]) -else: - def recv_udp(listener, bufsize): - debug3('Accept UDP using recvfrom.') - data, srcip = listener.recvfrom(bufsize) - return (srcip, None, data) + +def recv_udp(listener, bufsize): + debug3('Accept UDP python using recvmsg.') + data, ancdata, _, srcip = listener.recvmsg( + 4096, socket.CMSG_SPACE(24)) + dstip = None + family = None + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_IP and cmsg_type == IP_ORIGDSTADDR: + family, port = struct.unpack('=HH', cmsg_data[0:4]) + port = socket.htons(port) + if family == socket.AF_INET: + start = 4 + length = 4 + else: + raise Fatal("Unsupported socket type '%s'" % family) + ip = socket.inet_ntop(family, cmsg_data[start:start + length]) + dstip = (ip, port) + break + elif cmsg_level == SOL_IPV6 and cmsg_type == IPV6_ORIGDSTADDR: + family, port = struct.unpack('=HH', cmsg_data[0:4]) + port = socket.htons(port) + if family == socket.AF_INET6: + start = 8 + length = 16 + else: + raise Fatal("Unsupported socket type '%s'" % family) + ip = socket.inet_ntop(family, cmsg_data[start:start + length]) + dstip = (ip, port) + break + return (srcip, dstip, data) class Method(BaseMethod): @@ -106,12 +53,8 @@ class Method(BaseMethod): def get_supported_features(self): result = super(Method, self).get_supported_features() result.ipv6 = True - if recvmsg is None: - result.udp = False - result.dns = False - else: - result.udp = True - result.dns = True + result.udp = True + result.dns = True return result def get_tcp_dstip(self, sock): diff --git a/tests/client/test_methods_tproxy.py b/tests/client/test_methods_tproxy.py index 3e8b12f..994a907 100644 --- a/tests/client/test_methods_tproxy.py +++ b/tests/client/test_methods_tproxy.py @@ -6,8 +6,7 @@ from unittest.mock import Mock, patch, call from sshuttle.methods import get_method -@patch("sshuttle.methods.tproxy.recvmsg") -def test_get_supported_features_recvmsg(mock_recvmsg): +def test_get_supported_features(): method = get_method('tproxy') features = method.get_supported_features() assert features.ipv6 @@ -15,15 +14,6 @@ def test_get_supported_features_recvmsg(mock_recvmsg): assert features.dns -@patch("sshuttle.methods.tproxy.recvmsg", None) -def test_get_supported_features_norecvmsg(): - method = get_method('tproxy') - features = method.get_supported_features() - assert features.ipv6 - assert not features.udp - assert not features.dns - - def test_get_tcp_dstip(): sock = Mock() sock.getsockname.return_value = ('127.0.0.1', 1024)